Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include "../boot/boot.h"

#define PARTSRV "partfs.sdXX"

enum {
        Dontpost,
        Post,
};

char    cputype[64];
char    sys[2*64];
char    reply[256];
int     printcol;
int     mflag;
int     fflag;
int     kflag;
int     debugboot;
int     nousbboot;

char    *bargv[Nbarg];
int     bargc;

static void     swapproc(void);
static Method   *rootserver(char*);
static void     kbmap(void);

/*
 * we should inherit the standard fds all referring to /dev/cons,
 * but we're being paranoid.
 */
static void
opencons(void)
{
        close(0);
        close(1);
        close(2);
        bind("#c", "/dev", MBEFORE);
        open("/dev/cons", OREAD);
        open("/dev/cons", OWRITE);
        open("/dev/cons", OWRITE);
}

/*
 * init will reinitialize its namespace.
 * #ec gets us plan9.ini settings (*var variables).
 */
static void
bindenvsrv(void)
{
        bind("#ec", "/env", MREPL);
        bind("#e", "/env", MBEFORE|MCREATE);
        bind("#s", "/srv/", MREPL|MCREATE);
}

static void
debuginit(int argc, char **argv)
{
        int fd;

        if(getenv("debugboot"))
                debugboot = 1;
        if(getenv("nousbboot"))
                nousbboot = 1;
#ifdef DEBUG
        print("argc=%d\n", argc);
        for(fd = 0; fd < argc; fd++)
                print("%#p %s ", argv[fd], argv[fd]);
        print("\n");
#endif  /* DEBUG */
        SET(fd);
        USED(argc, argv, fd);
}

/*
 * read disk partition tables here so that readnvram via factotum
 * can see them.  ideally we would have this information in
 * environment variables before attaching #S, which would then
 * parse them and create partitions.
 */
static void
partinit(void)
{
        char *rdparts;

        rdparts = getenv("readparts");
        if(rdparts)
                readparts();
        free(rdparts);
}

/*
 *  pick a method and initialize it
 */
static Method *
pickmethod(int argc, char **argv)
{
        Method *mp;

        if(method[0].name == nil)
                fatal("no boot methods");
        mp = rootserver(argc ? *argv : 0);
        (*mp->config)(mp);
        return mp;
}

/*
 *  authentication agent
 *  sets hostowner, creating an auth discontinuity
 */
static void
doauth(int cpuflag)
{
        dprint("auth...");
        authentication(cpuflag);
}

/*
 *  connect to the root file system
 */
static int
connectroot(Method *mp, int islocal, int ishybrid)
{
        int fd, n;
        char buf[32];

        fd = (*mp->connect)();
        if(fd < 0)
                fatal("can't connect to file server");
        if(getenv("srvold9p"))
                fd = old9p(fd);
        if(!islocal && !ishybrid){
                if(cfs)
                        fd = (*cfs)(fd);
        }
        print("version...");
        buf[0] = '\0';
        n = fversion(fd, 0, buf, sizeof buf);
        if(n < 0)
                fatal("can't init 9P");
        srvcreate("boot", fd);
        return fd;
}

/*
 *  create the name space, mount the root fs
 */
static int
nsinit(int fd, char **rspp)
{
        int afd;
        char *rp, *rsp;
        AuthInfo *ai;
        static char rootbuf[64];

        if(bind("/", "/", MREPL) < 0)
                fatal("bind /");
        rp = getenv("rootspec");
        if(rp == nil)
                rp = "";
        
        afd = fauth(fd, rp);
        if(afd >= 0){
                ai = auth_proxy(afd, auth_getkey, "proto=p9any role=client");
                if(ai == nil)
                        print("authentication failed (%r), trying mount anyways\n");
        }
        if(mount(fd, afd, "/root", MREPL|MCREATE, rp) < 0)
                fatal("mount /");
        rsp = rp;
        rp = getenv("rootdir");
        if(rp == nil)
                rp = rootdir;
        if(bind(rp, "/", MAFTER|MCREATE) < 0){
                if(strncmp(rp, "/root", 5) == 0){
                        fprint(2, "boot: couldn't bind $rootdir=%s to root: %r\n", rp);
                        fatal("second bind /");
                }
                snprint(rootbuf, sizeof rootbuf, "/root/%s", rp);
                rp = rootbuf;
                if(bind(rp, "/", MAFTER|MCREATE) < 0){
                        fprint(2, "boot: couldn't bind $rootdir=%s to root: %r\n", rp);
                        if(strcmp(rootbuf, "/root//plan9") != 0)
                                fatal("second bind /");
                        /* undo installer's work */
                        fprint(2, "**** warning: remove rootdir=/plan9 "
                                "entry from plan9.ini\n");
                        rp = "/root";
                        if(bind(rp, "/", MAFTER|MCREATE) < 0)
                                fatal("second bind /");
                }
        }
        setenv("rootdir", rp);
        *rspp = rsp;
        return afd;
}

static void
execinit(void)
{
        int iargc;
        char *cmd, cmdbuf[64], *iargv[16];

        /* exec init */
        cmd = getenv("init");
        if(cmd == nil){
                sprint(cmdbuf, "/%s/init -%s%s", cputype,
                        cpuflag ? "c" : "t", mflag ? "m" : "");
                cmd = cmdbuf;
        }
        iargc = tokenize(cmd, iargv, nelem(iargv)-1);
        cmd = iargv[0];

        /* make iargv[0] basename(iargv[0]) */
        if(iargv[0] = strrchr(iargv[0], '/'))
                iargv[0]++;
        else
                iargv[0] = cmd;

        iargv[iargc] = nil;

        chmod("/srv/" PARTSRV, 0600);
        exec(cmd, iargv);
        fatal(cmd);
}

void
boot(int argc, char *argv[])
{
        int fd, afd, islocal, ishybrid;
        char *rsp;
        Method *mp;

        fmtinstall('r', errfmt);
        opencons();
        bindenvsrv();
        debuginit(argc, argv);

        ARGBEGIN{
        case 'k':
                kflag = 1;
                break;
        case 'm':
                mflag = 1;
                break;
        case 'f':
                fflag = 1;
                break;
        }ARGEND

        readfile("#e/cputype", cputype, sizeof(cputype));

        /*
         *  set up usb keyboard & mouse, if any.
         *  starts partfs on first disk, if any, to permit nvram on usb.
         */
        if (!nousbboot)
                usbinit(Dontpost);

        dprint("pickmethod...");
        mp = pickmethod(argc, argv);
        islocal = strcmp(mp->name, "local") == 0;
        ishybrid = strcmp(mp->name, "hybrid") == 0;

        kbmap();                        /*  load keymap if it's there. */

        /* don't trigger aoe until the network has been configured */
        dprint("bind #æ...");
        bind("#æ", "/dev", MAFTER);    /* nvram could be here */
        dprint("bind #S...");
        bind("#S", "/dev", MAFTER);     /* nvram could be here */
        dprint("partinit...");
        partinit();

        doauth(cpuflag);        /* authentication usually changes hostowner */
        rfork(RFNAMEG);         /* leave existing subprocs in own namespace */
        if (!nousbboot)
                usbinit(Post);  /* restart partfs under the new hostowner id */
        fd = connectroot(mp, islocal, ishybrid);
        afd = nsinit(fd, &rsp);
        close(fd);

        settime(islocal, afd, rsp);
        if(afd > 0)
                close(afd);
        swapproc();
        execinit();
        exits("failed to exec init");
}

static Method*
findmethod(char *a)
{
        Method *mp;
        int i, j;
        char *cp;

        if((i = strlen(a)) == 0)
                return nil;
        cp = strchr(a, '!');
        if(cp)
                i = cp - a;
        for(mp = method; mp->name; mp++){
                j = strlen(mp->name);
                if(j > i)
                        j = i;
                if(strncmp(a, mp->name, j) == 0)
                        break;
        }
        if(mp->name)
                return mp;
        return nil;
}

/*
 *  ask user from whence cometh the root file system
 */
static Method*
rootserver(char *arg)
{
        char prompt[256];
        Method *mp;
        char *cp;
        int n;

        /* look for required reply */
        dprint("read #e/nobootprompt...");
        readfile("#e/nobootprompt", reply, sizeof(reply));
        if(reply[0]){
                mp = findmethod(reply);
                if(mp)
                        goto HaveMethod;
                print("boot method %s not found\n", reply);
                reply[0] = 0;
        }

        /* make list of methods */
        mp = method;
        n = sprint(prompt, "root is from (%s", mp->name);
        for(mp++; mp->name; mp++)
                n += sprint(prompt+n, ", %s", mp->name);
        sprint(prompt+n, ")");

        /* create default reply */
        dprint("read #e/bootargs...");
        readfile("#e/bootargs", reply, sizeof(reply));
        if(reply[0] == 0 && arg != 0)
                strcpy(reply, arg);
        if(reply[0]){
                mp = findmethod(reply);
                if(mp == 0)
                        reply[0] = 0;
        }
        if(reply[0] == 0)
                strcpy(reply, method->name);

        /* parse replies */
        do{
                dprint("outin...");
                outin(prompt, reply, sizeof(reply));
                mp = findmethod(reply);
        }while(mp == nil);

HaveMethod:
        bargc = tokenize(reply, bargv, Nbarg-2);
        bargv[bargc] = nil;
        cp = strchr(reply, '!');
        if(cp)
                strcpy(sys, cp+1);
        dprint("pickmethod done\n");
        return mp;
}

static void
swapproc(void)
{
        int fd;

        fd = open("#c/swap", OWRITE);
        if(fd < 0){
                warning("opening #c/swap");
                return;
        }
        if(write(fd, "start", 5) <= 0)
                warning("starting swap kproc");
        close(fd);
}

int
old9p(int fd)
{
        int p[2];

        if(pipe(p) < 0)
                fatal("pipe");

        print("srvold9p...");
        switch(fork()) {
        case -1:
                fatal("rfork srvold9p");
        case 0:
                dup(fd, 1);
                close(fd);
                dup(p[0], 0);
                close(p[0]);
                close(p[1]);
                execl("/srvold9p", "srvold9p", "-s", 0);
                fatal("exec srvold9p");
        default:
                close(fd);
                close(p[0]);
        }
        return p[1];
}

static void
kbmap(void)
{
        char *f;
        int n, in, out;
        char buf[1024];

        f = getenv("kbmap");
        if(f == nil)
                return;
        if(bind("#κ", "/dev", MAFTER) < 0){
                warning("can't bind #κ");
                return;
        }

        in = open(f, OREAD);
        if(in < 0){
                warning("can't open kbd map");
                return;
        }
        out = open("/dev/kbmap", OWRITE);
        if(out < 0) {
                warning("can't open /dev/kbmap");
                close(in);
                return;
        }
        while((n = read(in, buf, sizeof(buf))) > 0)
                if(write(out, buf, n) != n){
                        warning("write to /dev/kbmap failed");
                        break;
                }
        close(in);
        close(out);
}