#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include "config.h"
int firstfile(const char *);
int yyparse(void);
static struct hashtab *mkopttab;
static struct nvlist **nextopt;
static struct nvlist **nextdefopt;
static struct nvlist **nextmkopt;
static __dead void stop(void);
static int do_option(struct hashtab *, struct nvlist ***,
const char *, const char *, const char *);
static int crosscheck(void);
static int badstar(void);
static int mksymlinks(void);
static int hasparent(struct devi *);
static int cfcrosscheck(struct config *, const char *, struct nvlist *);
static void optiondelta(void);
int verbose;
const char *conffile;
const char *last_component;
const char *machine;
const char *machinearch;
const char *srcdir;
const char *builddir;
const char *defbuilddir;
int errors;
int minmaxusers;
int defmaxusers;
int maxmaxusers;
int maxusers;
int maxpartitions;
struct nvlist *options;
struct nvlist *defoptions;
struct nvlist *mkoptions;
struct hashtab *devbasetab;
struct hashtab *devatab;
struct hashtab *selecttab;
struct hashtab *needcnttab;
struct hashtab *opttab;
struct hashtab *defopttab;
struct devbase *allbases;
struct deva *alldevas;
struct config *allcf;
struct devi *alldevi;
struct devi *allpseudo;
int ndevi;
int npseudo;
struct files *allfiles;
struct objects *allobjects;
struct devi **packed;
int npacked;
struct parents parents;
struct locators locators;
__dead void
usage(void)
{
extern char *__progname;
fprintf(stderr,
"usage: %s [-p] [-b builddir] [-s srcdir] [config-file]\n"
" %s -e [-u] [-c cmdfile] [-f | -o outfile] infile\n",
__progname, __progname);
exit(1);
}
int pflag = 0;
char *sflag = NULL;
char *bflag = NULL;
char *startdir;
char *cmdfile = NULL;
FILE *cmdfp = NULL;
int
main(int argc, char *argv[])
{
char *p;
char *outfile = NULL;
int ch, eflag, uflag, fflag;
char dirbuffer[PATH_MAX];
if (pledge("stdio rpath wpath cpath flock proc exec", NULL) == -1)
err(1, "pledge");
pflag = eflag = uflag = fflag = 0;
while ((ch = getopt(argc, argv, "c:epfb:s:o:u")) != -1) {
switch (ch) {
case 'c':
cmdfile = optarg;
break;
case 'o':
outfile = optarg;
break;
case 'u':
uflag = 1;
break;
case 'f':
fflag = 1;
break;
case 'e':
eflag = 1;
if (!isatty(STDIN_FILENO))
verbose = 1;
break;
case 'p':
pflag = 1;
break;
case 'b':
bflag = optarg;
builddir = optarg;
break;
case 's':
sflag = optarg;
srcdir = optarg;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc > 1 || (eflag && argv[0] == NULL))
usage();
if (bflag) {
startdir = getcwd(dirbuffer, sizeof dirbuffer);
if (startdir == NULL)
warn("Use of -b and can't getcwd, no make config");
} else {
startdir = "../../conf";
}
if (eflag) {
#ifdef MAKE_BOOTSTRAP
errx(1, "UKC not available in this binary");
#else
if (cmdfile != NULL) {
cmdfp = fopen(cmdfile, "r");
if (cmdfp == NULL)
err(1, "open %s", cmdfile);
}
return (ukc(argv[0], outfile, uflag, fflag));
#endif
}
conffile = (argc == 1) ? argv[0] : "CONFIG";
if (firstfile(conffile))
err(2, "cannot read %s", conffile);
minmaxusers = 1;
maxmaxusers = 10000;
initintern();
initfiles();
initsem();
devbasetab = ht_new();
devatab = ht_new();
selecttab = ht_new();
needcnttab = ht_new();
opttab = ht_new();
mkopttab = ht_new();
defopttab = ht_new();
nextopt = &options;
nextmkopt = &mkoptions;
nextdefopt = &defoptions;
last_component = strrchr(conffile, '/');
last_component = (last_component) ? last_component + 1 : conffile;
if (pflag) {
if (asprintf(&p, "../compile/%s.PROF", last_component) == -1)
err(1, NULL);
(void)addmkoption(intern("PROF"), "-pg");
(void)addoption(intern("GPROF"), NULL);
} else {
if (asprintf(&p, "../compile/%s", last_component) == -1)
err(1, NULL);
}
defbuilddir = (argc == 0) ? "." : p;
if (yyparse())
stop();
if (fixfiles())
stop();
if (fixobjects())
stop();
if (maxusers == 0) {
if (defmaxusers) {
(void)printf("maxusers not specified; %d assumed\n",
defmaxusers);
maxusers = defmaxusers;
} else {
warnx("need \"maxusers\" line");
errors++;
}
}
if (crosscheck() || errors)
stop();
pack();
if (badstar())
stop();
if (mksymlinks() || mkmakefile() || mkheaders() || mkswap() ||
mkioconf())
stop();
optiondelta();
return (0);
}
static int
mksymlink(const char *value, const char *path)
{
int ret = 0;
if (remove(path) && errno != ENOENT) {
warn("remove(%s)", path);
ret = 1;
}
if (symlink(value, path)) {
warn("symlink(%s -> %s)", path, value);
ret = 1;
}
return (ret);
}
static int
mksymlinks(void)
{
int ret;
char *p, buf[PATH_MAX];
const char *q;
snprintf(buf, sizeof buf, "arch/%s/include", machine);
p = sourcepath(buf);
ret = mksymlink(p, "machine");
if (machinearch != NULL) {
snprintf(buf, sizeof buf, "arch/%s/include", machinearch);
p = sourcepath(buf);
q = machinearch;
} else {
p = strdup("machine");
if (!p)
errx(1, "out of memory");
q = machine;
}
ret |= mksymlink(p, q);
free(p);
return (ret);
}
static __dead void
stop(void)
{
(void)fprintf(stderr, "*** Stop.\n");
exit(1);
}
void
defoption(const char *name)
{
char *p, *low, c;
const char *n;
low = emalloc(strlen(name) + 1);
for (n = name, p = low; (c = *n) != '\0'; n++)
*p++ = isupper((unsigned char)c) ?
tolower((unsigned char)c) : c;
*p = 0;
n = intern(low);
free(low);
(void)do_option(defopttab, &nextdefopt, n, name, "defopt");
(void)ht_insert(defopttab, name, (void *)name);
}
void
removeoption(const char *name)
{
struct nvlist *nv, *nvt;
char *p, *low, c;
const char *n;
if ((nv = ht_lookup(opttab, name)) != NULL) {
if (options == nv) {
options = nv->nv_next;
nvfree(nv);
} else {
nvt = options;
while (nvt->nv_next != NULL) {
if (nvt->nv_next == nv) {
nvt->nv_next = nvt->nv_next->nv_next;
nvfree(nv);
break;
} else
nvt = nvt->nv_next;
}
}
}
(void)ht_remove(opttab, name);
low = emalloc(strlen(name) + 1);
for (n = name, p = low; (c = *n) != '\0'; n++)
*p++ = isupper((unsigned char)c) ?
tolower((unsigned char)c) : c;
*p = 0;
n = intern(low);
free(low);
(void)ht_remove(selecttab, n);
}
void
addoption(const char *name, const char *value)
{
char *p, *low, c;
const char *n;
if (do_option(opttab, &nextopt, name, value, "options"))
return;
low = emalloc(strlen(name) + 1);
for (n = name, p = low; (c = *n) != '\0'; n++)
*p++ = isupper((unsigned char)c) ?
tolower((unsigned char)c) : c;
*p = 0;
n = intern(low);
free(low);
(void)ht_insert(selecttab, n, (void *)n);
}
void
addmkoption(const char *name, const char *value)
{
(void)do_option(mkopttab, &nextmkopt, name, value, "mkoptions");
}
static int
do_option(struct hashtab *ht, struct nvlist ***nppp, const char *name,
const char *value, const char *type)
{
struct nvlist *nv;
nv = newnv(name, value, NULL, 0, NULL);
if (ht_insert(ht, name, nv) == 0) {
**nppp = nv;
*nppp = &nv->nv_next;
return (0);
}
nvfree(nv);
if ((nv = ht_lookup(ht, name)) == NULL)
panic("do_option");
if (nv->nv_str != NULL)
error("already have %s `%s=%s'", type, name, nv->nv_str);
else
error("already have %s `%s'", type, name);
return (1);
}
int
deva_has_instances(struct deva *deva, int unit)
{
struct devi *i;
if (unit == WILD)
return (deva->d_ihead != NULL);
for (i = deva->d_ihead; i != NULL; i = i->i_asame)
if (unit == i->i_unit)
return (1);
return (0);
}
int
devbase_has_instances(struct devbase *dev, int unit)
{
struct deva *da;
for (da = dev->d_ahead; da != NULL; da = da->d_bsame)
if (deva_has_instances(da, unit))
return (1);
return (0);
}
static int
hasparent(struct devi *i)
{
struct nvlist *nv;
int atunit = i->i_atunit;
if (i->i_atdev != NULL)
return (devbase_has_instances(i->i_atdev, atunit));
if (i->i_atattr != NULL)
for (nv = i->i_atattr->a_refs; nv != NULL; nv = nv->nv_next)
if (devbase_has_instances(nv->nv_ptr, atunit))
return (1);
return (0);
}
static int
cfcrosscheck(struct config *cf, const char *what, struct nvlist *nv)
{
struct devbase *dev;
struct devi *pd;
int errs, devminor;
if (maxpartitions <= 0)
panic("cfcrosscheck");
for (errs = 0; nv != NULL; nv = nv->nv_next) {
if (nv->nv_name == NULL)
continue;
dev = ht_lookup(devbasetab, nv->nv_name);
if (dev == NULL)
panic("cfcrosscheck(%s)", nv->nv_name);
devminor = minor(nv->nv_int) / maxpartitions;
if (devbase_has_instances(dev, devminor))
continue;
if (devbase_has_instances(dev, STAR) &&
devminor >= dev->d_umax)
continue;
for (pd = allpseudo; pd != NULL; pd = pd->i_next)
if (pd->i_base == dev && devminor < dev->d_umax &&
devminor >= 0)
goto loop;
(void)fprintf(stderr,
"%s:%d: %s says %s on %s, but there's no %s\n",
conffile, cf->cf_lineno,
cf->cf_name, what, nv->nv_str, nv->nv_str);
errs++;
loop:
;
}
return (errs);
}
int
crosscheck(void)
{
struct devi *i;
struct config *cf;
int errs;
errs = 0;
for (i = alldevi; i != NULL; i = i->i_next) {
if (i->i_at == NULL || hasparent(i))
continue;
xerror(conffile, i->i_lineno,
"%s at %s is orphaned", i->i_name, i->i_at);
(void)fprintf(stderr, " (%s %s declared)\n",
i->i_atunit == WILD ? "nothing matching" : "no",
i->i_at);
errs++;
}
if (allcf == NULL) {
(void)fprintf(stderr, "%s has no configurations!\n",
conffile);
errs++;
}
for (cf = allcf; cf != NULL; cf = cf->cf_next) {
if (cf->cf_root != NULL) {
errs += cfcrosscheck(cf, "root", cf->cf_root);
errs += cfcrosscheck(cf, "swap", cf->cf_swap);
errs += cfcrosscheck(cf, "dumps", cf->cf_dump);
}
}
return (errs);
}
int
badstar(void)
{
struct devbase *d;
struct deva *da;
struct devi *i;
int errs, n;
errs = 0;
for (d = allbases; d != NULL; d = d->d_next) {
for (da = d->d_ahead; da != NULL; da = da->d_bsame)
for (i = da->d_ihead; i != NULL; i = i->i_asame) {
if (i->i_unit == STAR)
goto foundstar;
}
continue;
foundstar:
if (ht_lookup(needcnttab, d->d_name)) {
warnx("%s's cannot be *'d until its driver is fixed",
d->d_name);
errs++;
continue;
}
for (n = 0; i != NULL; i = i->i_alias)
if (!i->i_collapsed)
n++;
if (n < 1)
panic("badstar() n<1");
}
return (errs);
}
void
setupdirs(void)
{
struct stat st;
FILE *fp;
if ((builddir || strcmp(defbuilddir, ".") == 0) && !srcdir) {
error("source directory must be specified");
exit(1);
}
if (srcdir == NULL)
srcdir = "../../../..";
if (builddir == NULL)
builddir = defbuilddir;
if (stat(builddir, &st) != 0) {
if (mkdir(builddir, 0777))
err(2, "cannot create %s", builddir);
} else if (!S_ISDIR(st.st_mode))
errc(2, ENOTDIR, "%s", builddir);
if (chdir(builddir) != 0)
errx(2, "cannot change to %s", builddir);
if (stat(srcdir, &st) != 0 || !S_ISDIR(st.st_mode))
errc(2, ENOTDIR, "%s", srcdir);
if (bflag) {
if (pledge("stdio rpath wpath cpath flock", NULL) == -1)
err(1, "pledge");
return;
}
if (stat("obj", &st) == 0)
goto reconfig;
fp = fopen("Makefile", "w");
if (!fp)
errx(2, "cannot create Makefile");
if (fprintf(fp, ".include \"../Makefile.inc\"\n") < 0 ||
fclose(fp) == EOF)
errx(2, "cannot write Makefile");
reconfig:
if (system("make obj") != 0)
exit(2);
if (system("make config") != 0)
exit(2);
exit(0);
}
struct opt {
const char *name;
const char *val;
};
int
optcmp(const void *v1, const void *v2)
{
const struct opt *sp1 = v1, *sp2 = v2;
int r;
r = strcmp(sp1->name, sp2->name);
if (r == 0) {
if (!sp1->val && !sp2->val)
r = 0;
else if (sp1->val && !sp2->val)
r = -1;
else if (sp2->val && !sp1->val)
r = 1;
else r = strcmp(sp1->val, sp2->val);
}
return (r);
}
void
optiondelta(void)
{
struct nvlist *nv;
char nbuf[BUFSIZ], obuf[BUFSIZ];
int nnewopts, ret = 0, i;
struct opt *newopts;
FILE *fp;
for (nnewopts = 0, nv = options; nv != NULL; nv = nv->nv_next)
nnewopts++;
newopts = ereallocarray(NULL, nnewopts, sizeof(struct opt));
if (newopts == NULL)
ret = 0;
for (i = 0, nv = options; nv != NULL; nv = nv->nv_next, i++) {
newopts[i].name = nv->nv_name;
newopts[i].val = nv->nv_str;
}
qsort(newopts, nnewopts, sizeof (struct opt), optcmp);
if ((fp = fopen("options", "r"))) {
for (i = 0; !feof(fp) && i < nnewopts && ret == 0; i++) {
if (newopts[i].val)
snprintf(nbuf, sizeof nbuf, "%s=%s\n",
newopts[i].name, newopts[i].val);
else
snprintf(nbuf, sizeof nbuf, "%s\n",
newopts[i].name);
if (fgets(obuf, sizeof obuf, fp) == NULL ||
strcmp(nbuf, obuf))
ret = 1;
}
fclose(fp);
fp = NULL;
} else if (access("options", F_OK) == 0)
ret = 1;
if ((fp = fopen("options", "w+"))) {
rewind(fp);
for (i = 0; i < nnewopts; i++) {
if (newopts[i].val)
fprintf(fp, "%s=%s\n", newopts[i].name,
newopts[i].val);
else
fprintf(fp, "%s\n", newopts[i].name);
}
fclose(fp);
}
free(newopts);
if (ret == 0)
return;
(void)printf("Kernel options have changed -- you must run \"make clean\"\n");
}