Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include "all.h"
#include "io.h"

static void     dowormcopy(void);
static int      dodevcopy(void);

struct {
        char*   icharp;
        char*   charp;
        int     error;
        int     newconf;        /* clear before start */
        int     modconf;        /* write back when done */
        int     nextiter;
        int     lastiter;
        int     diriter;
        Device* lastcw;
        Device* devlist;
} f;

static Device* confdev;
static int copyworm = 0, copydev = 0;
static char *src, *dest;

static int resetparams;

Fspar fspar[] = {
        { "blocksize",  RBUFSIZE, },
        { "daddrbits",  sizeof(Off)*8, },
        { "indirblks",  NIBLOCK, },
        { "dirblks",    NDBLOCK, },
        { "namelen",    NAMELEN, },
        { nil, 0, },
};

int
devcmpr(Device *d1, Device *d2)
{
        while (d1 != d2) {
                if(d1 == nil || d2 == nil || d1->type != d2->type)
                        return 1;

                switch(d1->type) {
                default:
                        print("can't compare dev: %Z\n", d1);
                        panic("devcmp");
                        return 1;

                case Devmcat:
                case Devmlev:
                case Devmirr:
                        d1 = d1->cat.first;
                        d2 = d2->cat.first;
                        while(d1 && d2) {
                                if(devcmpr(d1, d2))
                                        return 1;
                                d1 = d1->link;
                                d2 = d2->link;
                        }
                        break;

                case Devnone:
                        return 0;

                case Devro:
                        d1 = d1->ro.parent;
                        d2 = d2->ro.parent;
                        break;

                case Devjuke:
                case Devcw:
                        if(devcmpr(d1->cw.c, d2->cw.c))
                                return 1;
                        d1 = d1->cw.w;
                        d2 = d2->cw.w;
                        break;

                case Devfworm:
                        d1 = d1->fw.fw;
                        d2 = d2->fw.fw;
                        break;

                case Devwren:
                case Devworm:
                case Devlworm:
                        if(d1->wren.ctrl == d2->wren.ctrl)
                        if(d1->wren.targ == d2->wren.targ)
                        if(d1->wren.lun == d2->wren.lun)
                                return 0;
                        return 1;

                case Devpart:
                        if(d1->part.base == d2->part.base)
                        if(d1->part.size == d2->part.size) {
                                d1 = d1->part.d;
                                d2 = d2->part.d;
                                break;
                        }
                        return 1;
                }
        }
        return 0;
}

void
cdiag(char *s, int c1)
{

        f.charp--;
        if(f.error == 0) {
                print("config diag: %s -- <%c>\n", s, c1);
                f.error = 1;
        }
}

int
cnumb(void)
{
        int c, n;

        c = *f.charp++;
        if(c == '<') {
                n = f.nextiter;
                if(n >= 0) {
                        f.nextiter = n+f.diriter;
                        if(n == f.lastiter) {
                                f.nextiter = -1;
                                f.lastiter = -1;
                        }
                        do {
                                c = *f.charp++;
                        } while (c != '>');
                        return n;
                }
                n = cnumb();
                if(*f.charp++ != '-') {
                        cdiag("- expected", f.charp[-1]);
                        return 0;
                }
                c = cnumb();
                if(*f.charp++ != '>') {
                        cdiag("> expected", f.charp[-1]);
                        return 0;
                }
                f.lastiter = c;
                f.diriter = 1;
                if(n > c)
                        f.diriter = -1;
                f.nextiter = n+f.diriter;
                return n;
        }
        if(!isascii(c) || !isdigit(c)) {
                cdiag("number expected", c);
                return 0;
        }
        n = 0;
        while(isascii(c) && isdigit(c)) {
                n = n*10 + (c-'0');
                c = *f.charp++;
        }
        f.charp--;
        return n;
}

Device*
config1(int c)
{
        Device *d, *t;
        int m;

        d = malloc(sizeof(Device));
        do {
                t = config();
                if(d->cat.first == 0)
                        d->cat.first = t;
                else
                        d->cat.last->link = t;
                d->cat.last = t;
                if(f.error)
                        return devnone;
                m = *f.charp;
                if(c == '(' && m == ')')
                        d->type = Devmcat;
                else if(c == '[' && m == ']')
                        d->type = Devmlev;
                else if(c == '{' && m == '}')
                        d->type = Devmirr;
        } while (d->type == 0);
        f.charp++;
        if(d->cat.first == d->cat.last)
                d = d->cat.first;
        return d;
}

static void
map(Device *d)
{
        Map *map;

        if (d->type != Devwren || d->wren.mapped)
                return;
        for (map = devmap; map != nil; map = map->next)
                if (devcmpr(d, map->fdev) == 0)
                        break;
        if (map == nil)
                return;
        if (access(map->to, AEXIST) >= 0)
{ print("map: mapped wren %Z to existing file %s\n", d, map->to); // DEBUG
                d->wren.file = map->to;         /* wren -> file mapping */
}
        else if (map->tdev != nil)
{ print("map: mapped wren %Z to dev %Z\n", d, map->tdev); // DEBUG
                *d = *map->tdev;                /* wren -> wren mapping */
}
        else
                print("bad mapping %Z to %s; no such file or device",
                        d, map->to);
        d->wren.mapped = 1;
}

Device*
config(void)
{
        int c, m;
        Device *d;
        char *icp;

        if(f.error)
                return devnone;
        d = malloc(sizeof(Device));

        c = *f.charp++;
        switch(c) {
        default:
                cdiag("unknown type", c);
                return devnone;

        case '(':       /* (d+) one or multiple cat */
        case '[':       /* [d+] one or multiple interleave */
        case '{':       /* {d+} a mirrored device and optional mirrors */
                return config1(c);

        case 'f':       /* fd fake worm */
                d->type = Devfworm;
                d->fw.fw = config();
                break;

        case 'n':
                d->type = Devnone;
                break;

        case 'w':       /* w[#.]#[.#] wren      [ctrl] unit [lun] */
        case 'r':       /* r# worm side */
        case 'l':       /* l# labelled-worm side */
                icp = f.charp;
                d->type = Devwren;
                d->wren.ctrl = 0;
                d->wren.targ = cnumb();
                d->wren.lun = 0;
                m = *f.charp;
                if(m == '.') {
                        f.charp++;
                        d->wren.lun = cnumb();
                        m = *f.charp;
                        if(m == '.') {
                                f.charp++;
                                d->wren.ctrl = d->wren.targ;
                                d->wren.targ = d->wren.lun;
                                d->wren.lun = cnumb();
                        }
                }
                if(f.nextiter >= 0)
                        f.charp = icp-1;
                if(c == 'r')            /* worms are virtual and not uniqued */
                        d->type = Devworm;
                else if(c == 'l')
                        d->type = Devlworm;
                else
                        map(d);         /* subject wrens to optional mapping */
                break;

        case 'o':       /* o ro part of last cw */
                if(f.lastcw == 0) {
                        cdiag("no cw to match", c);
                        return devnone;
                }
                return f.lastcw->cw.ro;

        case 'j':       /* DD jukebox */
                d->type = Devjuke;
                d->j.j = config();
                d->j.m = config();
                break;

        case 'c':       /* cache/worm */
                d->type = Devcw;
                d->cw.c = config();
                d->cw.w = config();
                d->cw.ro = malloc(sizeof(Device));
                d->cw.ro->type = Devro;
                d->cw.ro->ro.parent = d;
                f.lastcw = d;
                break;

        case 'p':       /* pd#.# partition base% size% */
                d->type = Devpart;
                d->part.d = config();
                d->part.base = cnumb();
                c = *f.charp++;
                if(c != '.')
                        cdiag("dot expected", c);
                d->part.size = cnumb();
                break;

        case 'x':       /* xD swab a device's metadata */
                d->type = Devswab;
                d->swab.d = config();
                break;
        }
        d->dlink = f.devlist;
        f.devlist = d;
        return d;
}

Device*
iconfig(char *s)
{
        Device *d;

        f.nextiter = -1;
        f.lastiter = -1;
        f.error = 0;
        f.icharp = s;
        f.charp = f.icharp;
        d = config();
        if(*f.charp) {
                cdiag("junk on end", *f.charp);
                f.error = 1;
        }
        return d;
}

int
testconfig(char *s)
{
        iconfig(s);
        return f.error;
}

/*
 * if b is a prefix of a, return 0.
 */
int
astrcmp(char *a, char *b)
{
        int n, c;

        n = strlen(b);
        if(memcmp(a, b, n) != 0)
                return 1;
        c = a[n];
        if(c == '\0')
                return 0;
        if(a[n+1])
                return 1;
        if(isascii(c) && isdigit(c))
                return 0;
        return 1;
}

static Fspar *
getpar(char *name)
{
        Fspar *fsp;

        for (fsp = fspar; fsp->name != nil; fsp++)
                if (strcmp(name, fsp->name) == 0)
                        return fsp;
        return nil;
}

/*
 * continue to parse obsolete keywords so that old configurations can
 * still work.
 */
void
mergeconf(Iobuf *p)
{
        char word[Maxword+1];
        char *cp;
        Filsys *fs;
        Fspar *fsp;

        for (cp = p->iobuf; *cp != '\0'; cp++) {
                cp = getwrd(word, cp);
                if(word[0] == '\0')
                        break;
                else if (word[0] == '#')
                        while (*cp != '\n' && *cp != '\0')
                                cp++;
                else if(strcmp(word, "service") == 0) {
                        cp = getwrd(word, cp);
                        if(service[0] == 0)
                                strncpy(service, word, sizeof service);
                } else if(strcmp(word, "ipauth") == 0)  /* obsolete */
                        cp = getwrd(word, cp);
                else if(astrcmp(word, "ip") == 0)       /* obsolete */
                        cp = getwrd(word, cp);
                else if(astrcmp(word, "ipgw") == 0)     /* obsolete */
                        cp = getwrd(word, cp);
                else if(astrcmp(word, "ipsntp") == 0)   /* obsolete */
                        cp = getwrd(word, cp);
                else if(astrcmp(word, "ipmask") == 0)   /* obsolete */
                        cp = getwrd(word, cp);
                else if(strcmp(word, "filsys") == 0) {
                        cp = getwrd(word, cp);
                        for(fs = filsys; fs < filsys + nelem(filsys) - 1 &&
                            fs->name; fs++)
                                if(strcmp(fs->name, word) == 0)
                                        break;
                        if (fs >= filsys + nelem(filsys) - 1)
                                panic("out of filsys structures");
                        if (fs->name && strcmp(fs->name, word) == 0 &&
                            fs->flags & FEDIT)
                                cp = getwrd(word, cp);  /* swallow conf */
                        else {
                                fs->name = strdup(word);
                                cp = getwrd(word, cp);
                                if (word[0] == '\0')
                                        fs->conf = nil;
                                else
                                        fs->conf = strdup(word);
                        }
                } else if ((fsp = getpar(word)) != nil) {
                        cp = getwrd(word, cp);
                        if (!isascii(word[0]) || !isdigit(word[0]))
                                print("bad %s value: %s", fsp->name, word);
                        else
                                fsp->declared = atol(word);
                } else {
                        putbuf(p);
                        panic("unknown keyword in config block: %s", word);
                }

                if(*cp != '\n') {
                        putbuf(p);
                        panic("syntax error in config block at `%s'", word);
                }
        }
}

void
cmd_printconf(int, char *[])
{
        char *p, *s;
        Iobuf *iob;

        iob = getbuf(confdev, 0, Brd);
        if(iob == nil)
                return;
        if(checktag(iob, Tconfig, 0)){
                putbuf(iob);
                return;
        }

        print("config %s\n", nvrgetconfig());
        for(s = p = iob->iobuf; *p != 0 && p < iob->iobuf+BUFSIZE; ){
                if(*p++ != '\n')
                        continue;
                if (strncmp(s, "ip", 2) != 0)   /* don't print obsolete cmds */
                        print("%.*s", (int)(p-s), s);
                s = p;
        }
        if(p != s)
                print("%.*s", (int)(p-s), s);
        print("end\n");

        putbuf(iob);
}

void
sysinit(void)
{
        int error;
        char *cp, *ep;
        Device *d;
        Filsys *fs;
        Fspar *fsp;
        Iobuf *p;

        cons.chan = fs_chaninit(Devcon, 1, 0);

start:
        /*
         * part 1 -- read the config file
         */
        devnone = iconfig("n");

        cp = nvrgetconfig();
        print("config %s\n", cp);

        confdev = d = iconfig(cp);
        devinit(d);
        if(f.newconf) {
                p = getbuf(d, 0, Bmod);
                memset(p->iobuf, 0, RBUFSIZE);
                settag(p, Tconfig, 0);
        } else
                p = getbuf(d, 0, Brd|Bmod);
        if(!p || checktag(p, Tconfig, 0))
                panic("config io");

        mergeconf(p);

        if (resetparams) {
                for (fsp = fspar; fsp->name != nil; fsp++)
                        fsp->declared = 0;
                resetparams = 0;
        }

        for (fsp = fspar; fsp->name != nil; fsp++) {
                /* supply defaults from this cwfs instance */
                if (fsp->declared == 0) {
                        fsp->declared = fsp->actual;
                        f.modconf = 1;
                }
                /* warn if declared value is not our compiled-in value */
                if (fsp->declared != fsp->actual)
                        print("warning: config %s %ld != compiled-in %ld\n",
                                fsp->name, fsp->declared, fsp->actual);
        }

        if(f.modconf) {
                memset(p->iobuf, 0, BUFSIZE);
                p->flags |= Bmod|Bimm;
                cp = p->iobuf;
                ep = p->iobuf + RBUFSIZE - 1;
                if(service[0])
                        cp = seprint(cp, ep, "service %s\n", service);
                for(fs=filsys; fs->name; fs++)
                        if(fs->conf && fs->conf[0] != '\0')
                                cp = seprint(cp, ep, "filsys %s %s\n", fs->name,
                                        fs->conf);

                for (fsp = fspar; fsp->name != nil; fsp++)
                        cp = seprint(cp, ep, "%s %ld\n",
                                fsp->name, fsp->declared);

                putbuf(p);
                f.modconf = f.newconf = 0;
                print("config block written\n");
                goto start;
        }
        putbuf(p);

        print("service    %s\n", service);

loop:
        /*
         * part 2 -- squeeze out the deleted filesystems
         */
        for(fs=filsys; fs->name; fs++)
                if(fs->conf == nil || fs->conf[0] == '\0') {
                        for(; fs->name; fs++)
                                *fs = *(fs+1);
                        goto loop;
                }
        if(filsys[0].name == nil)
                panic("no filsys");

        /*
         * part 3 -- compile the device expression
         */
        error = 0;
        for(fs=filsys; fs->name; fs++) {
                print("filsys %s %s\n", fs->name, fs->conf);
                fs->dev = iconfig(fs->conf);
                if(f.error) {
                        error = 1;
                        continue;
                }
        }
        if(error)
                panic("fs config");

        /*
         * part 4 -- initialize the devices
         */
        for(fs=filsys; fs->name; fs++) {
                delay(3000);
                print("sysinit: %s\n", fs->name);
                if(fs->flags & FREAM)
                        devream(fs->dev, 1);
                if(fs->flags & FRECOVER)
                        devrecover(fs->dev);
                devinit(fs->dev);
        }

        /*
         * part 5 -- optionally copy devices or worms
         */
        if (copyworm) {
                dowormcopy();           /* can return if user quits early */
                panic("copyworm bailed out!");
        }
        if (copydev)
                if (dodevcopy() < 0)
                        panic("copydev failed!");
                else
                        panic("copydev done.");
}

/* an unfinished idea.  a non-blocking rawchar() would help. */
static int
userabort(char *msg)
{
        USED(msg);
        return 0;
}

static int
blockok(Device *d, Off a)
{
        Iobuf *p = getbuf(d, a, Brd);

        if (p == 0) {
                print("i/o error reading %Z block %lld\n", d, (Wideoff)a);
                return 0;
        }
        putbuf(p);
        return 1;
}

/*
 * special case for fake worms only:
 * we need to size the inner cw's worm device.
 * in particular, we want to avoid copying the fake-worm bitmap
 * at the end of the device.
 *
 * N.B.: for real worms (e.g. cw jukes), we need to compute devsize(cw(juke)),
 * *NOT* devsize(juke).
 */
static Device *
wormof(Device *dev)
{
        Device *worm = dev, *cw;

        if (dev->type == Devfworm) {
                cw = dev->fw.fw;
                if (cw != nil && cw->type == Devcw)
                        worm = cw->cw.w;
        }
        // print("wormof(%Z)=%Z\n", dev, worm);
        return worm;
}

/*
 * return the number of the highest-numbered block actually written, plus 1.
 * 0 indicates an error.
 */
static Devsize
writtensize(Device *worm)
{
        Devsize lim = devsize(worm);
        Iobuf *p;

        print("devsize(%Z) = %lld\n", worm, (Wideoff)lim);
        if (!blockok(worm, 0) || !blockok(worm, lim-1))
                return 0;
        delay(5*1000);
        if (userabort("sanity checks"))
                return 0;

        /* find worm's last valid block in case "worm" is an (f)worm */
        while (lim > 0) {
                if (userabort("sizing")) {
                        lim = 0;                /* you lose */
                        break;
                }
                --lim;
                p = getbuf(worm, lim, Brd);
                if (p != 0) {                   /* actually read one okay? */
                        putbuf(p);
                        break;
                }
        }
        print("limit(%Z) = %lld\n", worm, (Wideoff)lim);
        if (lim <= 0)
                return 0;
        return lim + 1;
}

/* copy worm fs from "main"'s inner worm to "output" */
static void
dowormcopy(void)
{
        Filsys *f1, *f2;
        Device *fdev, *from, *to = nil;
        Iobuf *p;
        Off a;
        Devsize lim;

        /*
         * convert file system names into Filsyss and Devices.
         */

        f1 = fsstr("main");
        if(f1 == nil)
                panic("main file system missing");
        fdev = f1->dev;
        from = wormof(fdev);                    /* fake worm special */
        if (from->type != Devfworm && from->type != Devcw) {
                print("main file system is not a worm; copyworm may not do what you want!\n");
                print("waiting for 20 seconds...\n");
                delay(20000);
        }

        f2 = fsstr("output");
        if(f2 == nil) {
                print("no output file system - check only\n\n");
                print("reading worm from %Z (worm %Z)\n", fdev, from);
        } else {
                to = f2->dev;
                print("\ncopying worm from %Z (worm %Z) to %Z, starting in 8 seconds\n",
                        fdev, from, to);
                delay(8000);
        }
        if (userabort("preparing to copy"))
                return;

        /*
         * initialise devices, size them, more sanity checking.
         */

        devinit(from);
        if (0 && fdev != from) {
                devinit(fdev);
                print("debugging, sizing %Z first\n", fdev);
                writtensize(fdev);
        }
        lim = writtensize(from);
        if(lim == 0)
                panic("no blocks to copy on %Z", from);
        if (to) {
                print("reaming %Z in 8 seconds\n", to);
                delay(8000);
                if (userabort("preparing to ream & copy"))
                        return;
                devream(to, 0);
                devinit(to);
                print("copying worm: %lld blocks from %Z to %Z\n",
                        (Wideoff)lim, from, to);
        }
        /* can't read to's blocks in case to is a real WORM device */

        /*
         * Copy written fs blocks, a block at a time (or just read
         * if no "output" fs).
         */

        for (a = 0; a < lim; a++) {
                if (userabort("copy"))
                        break;
                p = getbuf(from, a, Brd);
                /*
                 * if from is a real WORM device, we'll get errors trying to
                 * read unwritten blocks, but the unwritten blocks need not
                 * be contiguous.
                 */
                if (p == 0) {
                        print("%lld not written yet; can't read\n", (Wideoff)a);
                        continue;
                }
                if (to != 0 && devwrite(to, p->addr, p->iobuf) != 0) {
                        print("out block %lld: write error; bailing",
                                (Wideoff)a);
                        break;
                }
                putbuf(p);
                if(a % 20000 == 0)
                        print("block %lld %T\n", (Wideoff)a, time(nil));
        }

        /*
         * wrap up: sync target, loop
         */
        print("copied %lld blocks from %Z to %Z\n", (Wideoff)a, from, to);
        sync("wormcopy");
        delay(2000);
        print("looping; reset the machine at any time.\n");
        for (; ; )
                continue;               /* await reset */
}

/* copy device from src to dest */
static int
dodevcopy(void)
{
        Device *from, *to;
        Iobuf *p;
        Off a;
        Devsize lim, tosize;

        /*
         * convert config strings into Devices.
         */
        from = iconfig(src);
        if(f.error || from == nil) {
                print("bad src device %s\n", src);
                return -1;
        }
        to = iconfig(dest);
        if(f.error || to == nil) {
                print("bad dest device %s\n", dest);
                return -1;
        }

        /*
         * initialise devices, size them, more sanity checking.
         */

        devinit(from);
        lim = devsize(from);
        if(lim == 0)
                panic("no blocks to copy on %Z", from);
        devinit(to);
        tosize = devsize(to);
        if(tosize == 0)
                panic("no blocks to copy on %Z", to);

        /* use smaller of the device sizes */
        if (tosize < lim)
                lim = tosize;

        print("copy %Z to %Z in 8 seconds\n", from, to);
        delay(8000);
        if (userabort("preparing to copy"))
                return -1;
        print("copying dev: %lld blocks from %Z to %Z\n", (Wideoff)lim,
                from, to);

        /*
         * Copy all blocks, a block at a time.
         */

        for (a = 0; a < lim; a++) {
                if (userabort("copy"))
                        break;
                p = getbuf(from, a, Brd);
                /*
                 * if from is a real WORM device, we'll get errors trying to
                 * read unwritten blocks, but the unwritten blocks need not
                 * be contiguous.
                 */
                if (p == 0) {
                        print("%lld not written yet; can't read\n", (Wideoff)a);
                        continue;
                }
                if (to != 0 && devwrite(to, p->addr, p->iobuf) != 0) {
                        print("out block %lld: write error; bailing",
                                (Wideoff)a);
                        break;
                }
                putbuf(p);
                if(a % 20000 == 0)
                        print("block %lld %T\n", (Wideoff)a, time(nil));
        }

        /*
         * wrap up: sync target
         */
        print("copied %lld blocks from %Z to %Z\n", (Wideoff)a, from, to);
        sync("devcopy");
        return 0;
}

static void
setconfig(char *dev)
{
        if (dev != nil && !testconfig(dev))
                nvrsetconfig(dev);      /* if it fails, it will complain */
}

void
arginit(void)
{
        int verb;
        char *line;
        char word[Maxword+1], *cp;
        Filsys *fs;

        if(nvrcheck() == 0) {
                setconfig(conf.confdev);
                if (!conf.configfirst)
                        return;
        }

        /* nvr was bad or invoker requested configuration step */
        setconfig(conf.confdev);
        for (;;) {
                print("config: ");
                if ((line = Brdline(&bin, '\n')) == nil)
                        return;
                line[Blinelen(&bin)-1] = '\0';

                cp = getwrd(word, line);
                if (word[0] == '\0' || word[0] == '#')
                        continue;
                if(strcmp(word, "end") == 0)
                        return;
                if(strcmp(word, "halt") == 0)
                        exit();
                if(strcmp(word, "queryjuke") == 0) {
                        getwrd(word, cp);
                        if(testconfig(word) == 0)
                                querychanger(iconfig(word));
                        continue;
                }

                if(strcmp(word, "allow") == 0) {
                        wstatallow = 1;
                        writeallow = 1;
                        continue;
                }
                if(strcmp(word, "copyworm") == 0) {
                        copyworm = 1;
                        continue;
                }
                if(strcmp(word, "copydev") == 0) {
                        cp = getwrd(word, cp);
                        if(testconfig(word))
                                continue;
                        src = strdup(word);
                        getwrd(word, cp);
                        if(testconfig(word))
                                continue;
                        dest = strdup(word);
                        copydev = 1;
                        continue;
                }
                if(strcmp(word, "noauth") == 0) {
                        noauth = !noauth;
                        continue;
                }
                if(strcmp(word, "noattach") == 0) {
                        noattach = !noattach;
                        continue;
                }
                if(strcmp(word, "readonly") == 0) {
                        readonly = 1;
                        continue;
                }

                if(strcmp(word, "ream") == 0) {
                        verb = FREAM;
                        goto gfsname;
                }
                if(strcmp(word, "recover") == 0) {
                        verb = FRECOVER;
                        goto gfsname;
                }
                if(strcmp(word, "filsys") == 0) {
                        verb = FEDIT;
                        goto gfsname;
                }

                if(strcmp(word, "nvram") == 0) {
                        getwrd(word, cp);
                        if(testconfig(word))
                                continue;
                        /* if it fails, it will complain */
                        nvrsetconfig(word);
                        continue;
                }
                if(strcmp(word, "config") == 0) {
                        getwrd(word, cp);
                        if(!testconfig(word) && nvrsetconfig(word) == 0)
                                f.newconf = 1;
                        continue;
                }
                if(strcmp(word, "service") == 0) {
                        getwrd(word, cp);
                        strncpy(service, word, sizeof service);
                        f.modconf = 1;
                        continue;
                }
                if (strcmp(word, "resetparams") == 0) {
                        resetparams++;
                        continue;
                }

                /*
                 * continue to parse obsolete keywords so that old
                 * configurations can still work.
                 */
                if (strcmp(word, "ipauth") != 0 &&
                    astrcmp(word, "ip") != 0 &&
                    astrcmp(word, "ipgw") != 0 &&
                    astrcmp(word, "ipmask") != 0 &&
                    astrcmp(word, "ipsntp") != 0) {
                        print("unknown config command\n");
                        print("\ttype end to get out\n");
                        continue;
                }

                getwrd(word, cp);
                f.modconf = 1;
                continue;

        gfsname:
                cp = getwrd(word, cp);
                for(fs=filsys; fs->name; fs++)
                        if(strcmp(word, fs->name) == 0)
                                break;
                if (fs->name == nil) {
                        memset(fs, 0, sizeof *fs);
                        fs->name = strdup(word);
                }
                switch(verb) {
                case FREAM:
                        if(strcmp(fs->name, "main") == 0)
                                wstatallow = 1; /* only set, never reset */
                        /* fallthrough */
                case FRECOVER:
                        fs->flags |= verb;
                        break;
                case FEDIT:
                        f.modconf = 1;
                        getwrd(word, cp);
                        fs->flags |= verb;
                        if(word[0] == 0)
                                fs->conf = nil;
                        else if(!testconfig(word))
                                fs->conf = strdup(word);
                        break;
                }
        }
}