Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include        "u.h"
#include        "../port/lib.h"
#include        "mem.h"
#include        "dat.h"
#include        "fns.h"
#include        "../port/error.h"
#include        "pool.h"
#include        <authsrv.h>

void    (*consdebug)(void) = nil;
void    (*screenputs)(char*, int) = nil;

Queue*  kbdq;                   /* unprocessed console input */
Queue*  lineq;                  /* processed console input */
Queue*  serialoq;               /* serial console output */
Queue*  kprintoq;               /* console output, for /dev/kprint */
ulong   kprintinuse;            /* test and set whether /dev/kprint is open */
int     iprintscreenputs = 1;

int     panicking;

static struct
{
        QLock;

        int     raw;            /* true if we shouldn't process input */
        Ref     ctl;            /* number of opens to the control file */
        int     x;              /* index into line */
        char    line[1024];     /* current input line */

        int     count;
        int     ctlpoff;

        /* a place to save up characters at interrupt time before dumping them in the queue */
        Lock    lockputc;
        char    istage[1024];
        char    *iw;
        char    *ir;
        char    *ie;
} kbd = {
        .iw     = kbd.istage,
        .ir     = kbd.istage,
        .ie     = kbd.istage + sizeof(kbd.istage),
};

char    *sysname;
vlong   fasthz;

static void     seedrand(void);
static int      readtime(ulong, char*, int);
static int      readbintime(char*, int);
static int      writetime(char*, int);
static int      writebintime(char*, int);

enum
{
        CMhalt,
        CMreboot,
        CMpanic,
};

Cmdtab rebootmsg[] =
{
        CMhalt,         "halt",         1,
        CMreboot,       "reboot",       0,
        CMpanic,        "panic",        0,
};

void
printinit(void)
{
        lineq = qopen(2*1024, 0, nil, nil);
        if(lineq == nil)
                panic("printinit");
        qnoblock(lineq, 1);
}

int
consactive(void)
{
        if(serialoq)
                return qlen(serialoq) > 0;
        return 0;
}

void
prflush(void)
{
        ulong now;

        now = m->ticks;
        while(consactive())
                if(m->ticks - now >= HZ)
                        break;
}

/*
 * Log console output so it can be retrieved via /dev/kmesg.
 * This is good for catching boot-time messages after the fact.
 */
struct {
        Lock lk;
        char buf[KMESGSIZE];
        uint n;
} kmesg;

static void
kmesgputs(char *str, int n)
{
        uint nn, d;

        ilock(&kmesg.lk);
        /* take the tail of huge writes */
        if(n > sizeof kmesg.buf){
                d = n - sizeof kmesg.buf;
                str += d;
                n -= d;
        }

        /* slide the buffer down to make room */
        nn = kmesg.n;
        if(nn + n >= sizeof kmesg.buf){
                d = nn + n - sizeof kmesg.buf;
                if(d)
                        memmove(kmesg.buf, kmesg.buf+d, sizeof kmesg.buf-d);
                nn -= d;
        }

        /* copy the data in */
        memmove(kmesg.buf+nn, str, n);
        nn += n;
        kmesg.n = nn;
        iunlock(&kmesg.lk);
}

/*
 *   Print a string on the console.  Convert \n to \r\n for serial
 *   line consoles.  Locking of the queues is left up to the screen
 *   or uart code.  Multi-line messages to serial consoles may get
 *   interspersed with other messages.
 */
static void
putstrn0(char *str, int n, int usewrite)
{
        int m;
        char *t;

        if(!islo())
                usewrite = 0;

        /*
         *  how many different output devices do we need?
         */
        kmesgputs(str, n);

        /*
         *  if someone is reading /dev/kprint,
         *  put the message there.
         *  if not and there's an attached bit mapped display,
         *  put the message there.
         *
         *  if there's a serial line being used as a console,
         *  put the message there.
         */
        if(kprintoq != nil && !qisclosed(kprintoq)){
                if(usewrite)
                        qwrite(kprintoq, str, n);
                else
                        qiwrite(kprintoq, str, n);
        }else if(screenputs != nil)
                screenputs(str, n);

        if(serialoq == nil){
                uartputs(str, n);
                return;
        }

        while(n > 0) {
                t = memchr(str, '\n', n);
                if(t && !kbd.raw) {
                        m = t-str;
                        if(usewrite){
                                qwrite(serialoq, str, m);
                                qwrite(serialoq, "\r\n", 2);
                        } else {
                                qiwrite(serialoq, str, m);
                                qiwrite(serialoq, "\r\n", 2);
                        }
                        n -= m+1;
                        str = t+1;
                } else {
                        if(usewrite)
                                qwrite(serialoq, str, n);
                        else
                                qiwrite(serialoq, str, n);
                        break;
                }
        }
}

void
putstrn(char *str, int n)
{
        putstrn0(str, n, 0);
}

int noprint;

int
print(char *fmt, ...)
{
        int n;
        va_list arg;
        char buf[PRINTSIZE];

        if(noprint)
                return -1;

        va_start(arg, fmt);
        n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
        va_end(arg);

        if(!normalprint) {
                if(0) iprint("\nprint called too early from %#lux\n",
                        getcallerpc(&fmt));
                iprint("%.*s", n, buf);
        } else
                putstrn(buf, n);

        return n;
}

/*
 * Want to interlock iprints to avoid interlaced output on 
 * multiprocessor, but don't want to deadlock if one processor
 * dies during print and another has something important to say.
 * Make a good faith effort.
 */
static Lock iprintlock;
static int
iprintcanlock(Lock *l)
{
        int i;
        
        for(i=0; i<1000; i++){
                if(canlock(l))
                        return 1;
                if(l->m == MACHP(m->machno))
                        return 0;
                microdelay(100);
        }
        return 0;
}

int
iprint(char *fmt, ...)
{
        int n, s, locked;
        va_list arg;
        char buf[PRINTSIZE];

        s = splhi();
        va_start(arg, fmt);
        n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
        va_end(arg);
        locked = iprintcanlock(&iprintlock);
        if(screenputs != nil && iprintscreenputs)
                screenputs(buf, n);
        if(consuart == nil || consuart->phys == nil ||
            consuart->phys->putc == nil)
                _uartputs(buf, n);
        else
                uartputs(buf, n);
        if(locked)
                unlock(&iprintlock);
        splx(s);

        return n;
}

void
panic(char *fmt, ...)
{
        int n, s;
        va_list arg;
        char buf[PRINTSIZE];

        kprintoq = nil; /* don't try to write to /dev/kprint */

        if(panicking)
                for(;;);
        panicking = 1;

        delay(20);
        s = splhi();
        strcpy(buf, "panic: ");
        va_start(arg, fmt);
        n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf;
        va_end(arg);
        iprint("%s\n", buf);
        if(consdebug)
                (*consdebug)();
        splx(s);
        prflush();
        buf[n] = '\n';
//      putstrn(buf, n+1);
//      dumpstack();

        exit(1);
}

/* libmp at least contains a few calls to sysfatal; simulate with panic */
void
sysfatal(char *fmt, ...)
{
        char err[256];
        va_list arg;

        va_start(arg, fmt);
        vseprint(err, err + sizeof err, fmt, arg);
        va_end(arg);
        panic("sysfatal: %s", err);
}

void
_assert(char *fmt)
{
        panic("assert failed at %#p: %s", getcallerpc(&fmt), fmt);
}

int
pprint(char *fmt, ...)
{
        int n;
        Chan *c;
        va_list arg;
        char buf[2*PRINTSIZE];

        if(up == nil || up->fgrp == nil)
                return 0;

        c = up->fgrp->fd[2];
        if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR))
                return 0;
        n = snprint(buf, sizeof buf, "%s %lud: ", up->text, up->pid);
        va_start(arg, fmt);
        n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf;
        va_end(arg);

        if(waserror())
                return 0;
        devtab[c->type]->write(c, buf, n, c->offset);
        poperror();

        lock(c);
        c->offset += n;
        unlock(c);

        return n;
}

static void
echoscreen(char *buf, int n)
{
        char *e, *p;
        char ebuf[128];
        int x;

        p = ebuf;
        e = ebuf + sizeof(ebuf) - 4;
        while(n-- > 0){
                if(p >= e){
                        screenputs(ebuf, p - ebuf);
                        p = ebuf;
                }
                x = *buf++;
                if(x == 0x15){
                        *p++ = '^';
                        *p++ = 'U';
                        *p++ = '\n';
                } else
                        *p++ = x;
        }
        if(p != ebuf)
                screenputs(ebuf, p - ebuf);
}

static void
echoserialoq(char *buf, int n)
{
        int x;
        char *e, *p;
        char ebuf[128];

        p = ebuf;
        e = ebuf + sizeof(ebuf) - 4;
        while(n-- > 0){
                if(p >= e){
                        qiwrite(serialoq, ebuf, p - ebuf);
                        p = ebuf;
                }
                x = *buf++;
                if(x == '\n'){
                        *p++ = '\r';
                        *p++ = '\n';
                } else if(x == 0x15){
                        *p++ = '^';
                        *p++ = 'U';
                        *p++ = '\n';
                } else
                        *p++ = x;
        }
        if(p != ebuf)
                qiwrite(serialoq, ebuf, p - ebuf);
}

static void
echo(char *buf, int n)
{
        static int ctrlt, pid;
        int x;
        char *e, *p;

        if(n == 0)
                return;

        e = buf+n;
        for(p = buf; p < e; p++){
                switch(*p){
                case 0x10:      /* ^P */
                        if(cpuserver && !kbd.ctlpoff){
                                active.exiting = 1;
                                return;
                        }
                        break;
                case 0x14:      /* ^T */
                        ctrlt++;
                        if(ctrlt > 2)
                                ctrlt = 2;
                        continue;
                }

                if(ctrlt != 2)
                        continue;

                /* ^T escapes */
                ctrlt = 0;
                switch(*p){
                case 'S':
                        x = splhi();
                        dumpstack();
                        procdump();
                        splx(x);
                        return;
                case 's':
                        dumpstack();
                        return;
                case 'x':
                        xsummary();
                        ixsummary();
                        mallocsummary();
                //      memorysummary();
                        pagersummary();
                        return;
                case 'd':
                        if(consdebug == nil)
                                consdebug = rdb;
                        else
                                consdebug = nil;
                        print("consdebug now %#p\n", consdebug);
                        return;
                case 'D':
                        if(consdebug == nil)
                                consdebug = rdb;
                        consdebug();
                        return;
                case 'p':
                        x = spllo();
                        procdump();
                        splx(x);
                        return;
                case 'q':
                        scheddump();
                        return;
                case 'k':
                        killbig("^t ^t k");
                        return;
                case 'r':
                        exit(0);
                        return;
                }
        }

        qproduce(kbdq, buf, n);
        if(kbd.raw)
                return;
        kmesgputs(buf, n);
        if(screenputs != nil)
                echoscreen(buf, n);
        if(serialoq)
                echoserialoq(buf, n);
}

/*
 *  Called by a uart interrupt for console input.
 *
 *  turn '\r' into '\n' before putting it into the queue.
 */
int
kbdcr2nl(Queue*, int ch)
{
        char *next;

        ilock(&kbd.lockputc);           /* just a mutex */
        if(ch == '\r' && !kbd.raw)
                ch = '\n';
        next = kbd.iw+1;
        if(next >= kbd.ie)
                next = kbd.istage;
        if(next != kbd.ir){
                *kbd.iw = ch;
                kbd.iw = next;
        }
        iunlock(&kbd.lockputc);
        return 0;
}

/*
 *  Put character, possibly a rune, into read queue at interrupt time.
 *  Called at interrupt time to process a character.
 */
int
kbdputc(Queue*, int ch)
{
        int i, n;
        char buf[3];
        Rune r;
        char *next;

        if(kbd.ir == nil)
                return 0;               /* in case we're not inited yet */
        
        ilock(&kbd.lockputc);           /* just a mutex */
        r = ch;
        n = runetochar(buf, &r);
        for(i = 0; i < n; i++){
                next = kbd.iw+1;
                if(next >= kbd.ie)
                        next = kbd.istage;
                if(next == kbd.ir)
                        break;
                *kbd.iw = buf[i];
                kbd.iw = next;
        }
        iunlock(&kbd.lockputc);
        return 0;
}

/*
 *  we save up input characters till clock time to reduce
 *  per character interrupt overhead.
 */
static void
kbdputcclock(void)
{
        char *iw;

        /* this amortizes cost of qproduce */
        if(kbd.iw != kbd.ir){
                iw = kbd.iw;
                if(iw < kbd.ir){
                        echo(kbd.ir, kbd.ie-kbd.ir);
                        kbd.ir = kbd.istage;
                }
                if(kbd.ir != iw){
                        echo(kbd.ir, iw-kbd.ir);
                        kbd.ir = iw;
                }
        }
}

enum{
        Qdir,
        Qbintime,
        Qcons,
        Qconsctl,
        Qcputime,
        Qdrivers,
        Qkmesg,
        Qkprint,
        Qhostdomain,
        Qhostowner,
        Qnull,
        Qosversion,
        Qpgrpid,
        Qpid,
        Qppid,
        Qrandom,
        Qreboot,
        Qswap,
        Qsysname,
        Qsysstat,
        Qtime,
        Quser,
        Qzero,
};

enum
{
        VLNUMSIZE=      22,
};

static Dirtab consdir[]={
        ".",    {Qdir, 0, QTDIR},       0,              DMDIR|0555,
        "bintime",      {Qbintime},     24,             0664,
        "cons",         {Qcons},        0,              0660,
        "consctl",      {Qconsctl},     0,              0220,
        "cputime",      {Qcputime},     6*NUMSIZE,      0444,
        "drivers",      {Qdrivers},     0,              0444,
        "hostdomain",   {Qhostdomain},  DOMLEN,         0664,
        "hostowner",    {Qhostowner},   0,              0664,
        "kmesg",        {Qkmesg},       0,              0440,
        "kprint",       {Qkprint, 0, QTEXCL},   0,      DMEXCL|0440,
        "null",         {Qnull},        0,              0666,
        "osversion",    {Qosversion},   0,              0444,
        "pgrpid",       {Qpgrpid},      NUMSIZE,        0444,
        "pid",          {Qpid},         NUMSIZE,        0444,
        "ppid",         {Qppid},        NUMSIZE,        0444,
        "random",       {Qrandom},      0,              0444,
        "reboot",       {Qreboot},      0,              0664,
        "swap",         {Qswap},        0,              0664,
        "sysname",      {Qsysname},     0,              0664,
        "sysstat",      {Qsysstat},     0,              0666,
        "time",         {Qtime},        NUMSIZE+3*VLNUMSIZE,    0664,
        "user",         {Quser},        0,              0666,
        "zero",         {Qzero},        0,              0444,
};

int
readnum(ulong off, char *buf, ulong n, ulong val, int size)
{
        char tmp[64];

        snprint(tmp, sizeof(tmp), "%*lud", size-1, val);
        tmp[size-1] = ' ';
        if(off >= size)
                return 0;
        if(off+n > size)
                n = size-off;
        memmove(buf, tmp+off, n);
        return n;
}

int
readstr(ulong off, char *buf, ulong n, char *str)
{
        int size;

        size = strlen(str);
        if(off >= size)
                return 0;
        if(off+n > size)
                n = size-off;
        memmove(buf, str+off, n);
        return n;
}

static void
consinit(void)
{
        todinit();
        randominit();
        /*
         * at 115200 baud, the 1024 char buffer takes 56 ms to process,
         * processing it every 22 ms should be fine
         */
        addclock0link(kbdputcclock, 22);
}

static Chan*
consattach(char *spec)
{
        return devattach('c', spec);
}

static Walkqid*
conswalk(Chan *c, Chan *nc, char **name, int nname)
{
        return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen);
}

static int
consstat(Chan *c, uchar *dp, int n)
{
        return devstat(c, dp, n, consdir, nelem(consdir), devgen);
}

static Chan*
consopen(Chan *c, int omode)
{
        c->aux = nil;
        c = devopen(c, omode, consdir, nelem(consdir), devgen);
        switch((ulong)c->qid.path){
        case Qconsctl:
                incref(&kbd.ctl);
                break;

        case Qkprint:
                if(tas(&kprintinuse) != 0){
                        c->flag &= ~COPEN;
                        error(Einuse);
                }
                if(kprintoq == nil){
                        kprintoq = qopen(8*1024, Qcoalesce, 0, 0);
                        if(kprintoq == nil){
                                c->flag &= ~COPEN;
                                error(Enomem);
                        }
                        qnoblock(kprintoq, 1);
                }else
                        qreopen(kprintoq);
                c->iounit = qiomaxatomic;
                break;
        }
        return c;
}

static void
consclose(Chan *c)
{
        switch((ulong)c->qid.path){
        /* last close of control file turns off raw */
        case Qconsctl:
                if(c->flag&COPEN){
                        if(decref(&kbd.ctl) == 0)
                                kbd.raw = 0;
                }
                break;

        /* close of kprint allows other opens */
        case Qkprint:
                if(c->flag & COPEN){
                        kprintinuse = 0;
                        qhangup(kprintoq, nil);
                }
                break;
        }
}

static long
consread(Chan *c, void *buf, long n, vlong off)
{
        ulong l;
        Mach *mp;
        char *b, *bp, ch;
        char tmp[256];          /* must be >= 18*NUMSIZE (Qswap) */
        int i, k, id, send;
        vlong offset = off;

        if(n <= 0)
                return n;

        switch((ulong)c->qid.path){
        case Qdir:
                return devdirread(c, buf, n, consdir, nelem(consdir), devgen);

        case Qcons:
                qlock(&kbd);
                if(waserror()) {
                        qunlock(&kbd);
                        nexterror();
                }
                while(!qcanread(lineq)){
                        if(qread(kbdq, &ch, 1) == 0)
                                continue;
                        send = 0;
                        if(ch == 0){
                                /* flush output on rawoff -> rawon */
                                if(kbd.x > 0)
                                        send = !qcanread(kbdq);
                        }else if(kbd.raw){
                                kbd.line[kbd.x++] = ch;
                                send = !qcanread(kbdq);
                        }else{
                                switch(ch){
                                case '\b':
                                        if(kbd.x > 0)
                                                kbd.x--;
                                        break;
                                case 0x15:      /* ^U */
                                        kbd.x = 0;
                                        break;
                                case '\n':
                                case 0x04:      /* ^D */
                                        send = 1;
                                default:
                                        if(ch != 0x04)
                                                kbd.line[kbd.x++] = ch;
                                        break;
                                }
                        }
                        if(send || kbd.x == sizeof kbd.line){
                                qwrite(lineq, kbd.line, kbd.x);
                                kbd.x = 0;
                        }
                }
                n = qread(lineq, buf, n);
                qunlock(&kbd);
                poperror();
                return n;

        case Qcputime:
                k = offset;
                if(k >= 6*NUMSIZE)
                        return 0;
                if(k+n > 6*NUMSIZE)
                        n = 6*NUMSIZE - k;
                /* easiest to format in a separate buffer and copy out */
                for(i=0; i<6 && NUMSIZE*i<k+n; i++){
                        l = up->time[i];
                        if(i == TReal)
                                l = MACHP(0)->ticks - l;
                        l = TK2MS(l);
                        readnum(0, tmp+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
                }
                memmove(buf, tmp+k, n);
                return n;

        case Qkmesg:
                /*
                 * This is unlocked to avoid tying up a process
                 * that's writing to the buffer.  kmesg.n never 
                 * gets smaller, so worst case the reader will
                 * see a slurred buffer.
                 */
                if(off >= kmesg.n)
                        n = 0;
                else{
                        if(off+n > kmesg.n)
                                n = kmesg.n - off;
                        memmove(buf, kmesg.buf+off, n);
                }
                return n;
                
        case Qkprint:
                return qread(kprintoq, buf, n);

        case Qpgrpid:
                return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE);

        case Qpid:
                return readnum((ulong)offset, buf, n, up->pid, NUMSIZE);

        case Qppid:
                return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE);

        case Qtime:
                return readtime((ulong)offset, buf, n);

        case Qbintime:
                return readbintime(buf, n);

        case Qhostowner:
                return readstr((ulong)offset, buf, n, eve);

        case Qhostdomain:
                return readstr((ulong)offset, buf, n, hostdomain);

        case Quser:
                return readstr((ulong)offset, buf, n, up->user);

        case Qnull:
                return 0;

        case Qsysstat:
                b = smalloc(conf.nmach*(NUMSIZE*11+1) + 1);     /* +1 for NUL */
                bp = b;
                for(id = 0; id < 32; id++) {
                        if(active.machs & (1<<id)) {
                                mp = MACHP(id);
                                readnum(0, bp, NUMSIZE, id, NUMSIZE);
                                bp += NUMSIZE;
                                readnum(0, bp, NUMSIZE, mp->cs, NUMSIZE);
                                bp += NUMSIZE;
                                readnum(0, bp, NUMSIZE, mp->intr, NUMSIZE);
                                bp += NUMSIZE;
                                readnum(0, bp, NUMSIZE, mp->syscall, NUMSIZE);
                                bp += NUMSIZE;
                                readnum(0, bp, NUMSIZE, mp->pfault, NUMSIZE);
                                bp += NUMSIZE;
                                readnum(0, bp, NUMSIZE, mp->tlbfault, NUMSIZE);
                                bp += NUMSIZE;
                                readnum(0, bp, NUMSIZE, mp->tlbpurge, NUMSIZE);
                                bp += NUMSIZE;
                                readnum(0, bp, NUMSIZE, mp->load, NUMSIZE);
                                bp += NUMSIZE;
                                readnum(0, bp, NUMSIZE,
                                        (mp->perf.avg_inidle*100)/mp->perf.period,
                                        NUMSIZE);
                                bp += NUMSIZE;
                                readnum(0, bp, NUMSIZE,
                                        (mp->perf.avg_inintr*100)/mp->perf.period,
                                        NUMSIZE);
                                bp += NUMSIZE;
                                *bp++ = '\n';
                        }
                }
                if(waserror()){
                        free(b);
                        nexterror();
                }
                n = readstr((ulong)offset, buf, n, b);
                free(b);
                poperror();
                return n;

        case Qswap:
                snprint(tmp, sizeof tmp,
                        "%lud memory\n"
                        "%d pagesize\n"
                        "%lud kernel\n"
                        "%lud/%lud user\n"
                        "%lud/%lud swap\n"
                        "%lud/%lud kernel malloc\n"
                        "%lud/%lud kernel draw\n",
                        conf.npage*BY2PG,
                        BY2PG,
                        conf.npage-conf.upages,
                        palloc.user-palloc.freecount, palloc.user,
                        conf.nswap-swapalloc.free, conf.nswap,
                        mainmem->cursize, mainmem->maxsize,
                        imagmem->cursize, imagmem->maxsize);

                return readstr((ulong)offset, buf, n, tmp);

        case Qsysname:
                if(sysname == nil)
                        return 0;
                return readstr((ulong)offset, buf, n, sysname);

        case Qrandom:
                return randomread(buf, n);

        case Qdrivers:
                b = malloc(READSTR);
                if(b == nil)
                        error(Enomem);
                k = 0;
                for(i = 0; devtab[i] != nil; i++)
                        k += snprint(b+k, READSTR-k, "#%C %s\n",
                                devtab[i]->dc,  devtab[i]->name);
                if(waserror()){
                        free(b);
                        nexterror();
                }
                n = readstr((ulong)offset, buf, n, b);
                free(b);
                poperror();
                return n;

        case Qzero:
                memset(buf, 0, n);
                return n;

        case Qosversion:
                snprint(tmp, sizeof tmp, "2000");
                n = readstr((ulong)offset, buf, n, tmp);
                return n;

        default:
                print("consread %#llux\n", c->qid.path);
                error(Egreg);
        }
        return -1;              /* never reached */
}

static long
conswrite(Chan *c, void *va, long n, vlong off)
{
        char buf[256], ch;
        long l, bp;
        char *a;
        Mach *mp;
        int id, fd;
        Chan *swc;
        ulong offset;
        Cmdbuf *cb;
        Cmdtab *ct;

        a = va;
        offset = off;

        switch((ulong)c->qid.path){
        case Qcons:
                /*
                 * Can't page fault in putstrn, so copy the data locally.
                 */
                l = n;
                while(l > 0){
                        bp = l;
                        if(bp > sizeof buf)
                                bp = sizeof buf;
                        memmove(buf, a, bp);
                        putstrn0(buf, bp, 1);
                        a += bp;
                        l -= bp;
                }
                break;

        case Qconsctl:
                if(n >= sizeof(buf))
                        n = sizeof(buf)-1;
                strncpy(buf, a, n);
                buf[n] = 0;
                for(a = buf; a;){
                        if(strncmp(a, "rawon", 5) == 0){
                                kbd.raw = 1;
                                /* clumsy hack - wake up reader */
                                ch = 0;
                                qwrite(kbdq, &ch, 1);                   
                        } else if(strncmp(a, "rawoff", 6) == 0){
                                kbd.raw = 0;
                        } else if(strncmp(a, "ctlpon", 6) == 0){
                                kbd.ctlpoff = 0;
                        } else if(strncmp(a, "ctlpoff", 7) == 0){
                                kbd.ctlpoff = 1;
                        }
                        if(a = strchr(a, ' '))
                                a++;
                }
                break;

        case Qtime:
                if(!iseve())
                        error(Eperm);
                return writetime(a, n);

        case Qbintime:
                if(!iseve())
                        error(Eperm);
                return writebintime(a, n);

        case Qhostowner:
                return hostownerwrite(a, n);

        case Qhostdomain:
                return hostdomainwrite(a, n);

        case Quser:
                return userwrite(a, n);

        case Qnull:
                break;

        case Qreboot:
                if(!iseve())
                        error(Eperm);
                cb = parsecmd(a, n);

                if(waserror()) {
                        free(cb);
                        nexterror();
                }
                ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg));
                switch(ct->index) {
                case CMhalt:
                        reboot(nil, 0, 0);
                        break;
                case CMreboot:
                        rebootcmd(cb->nf-1, cb->f+1);
                        break;
                case CMpanic:
                        *(ulong*)0=0;
                        panic("/dev/reboot");
                }
                poperror();
                free(cb);
                break;

        case Qsysstat:
                for(id = 0; id < 32; id++) {
                        if(active.machs & (1<<id)) {
                                mp = MACHP(id);
                                mp->cs = 0;
                                mp->intr = 0;
                                mp->syscall = 0;
                                mp->pfault = 0;
                                mp->tlbfault = 0;
                                mp->tlbpurge = 0;
                        }
                }
                break;

        case Qswap:
                if(n >= sizeof buf)
                        error(Egreg);
                memmove(buf, va, n);    /* so we can NUL-terminate */
                buf[n] = 0;
                /* start a pager if not already started */
                if(strncmp(buf, "start", 5) == 0){
                        kickpager();
                        break;
                }
                if(!iseve())
                        error(Eperm);
                if(buf[0]<'0' || '9'<buf[0])
                        error(Ebadarg);
                fd = strtoul(buf, 0, 0);
                swc = fdtochan(fd, -1, 1, 1);
                setswapchan(swc);
                break;

        case Qsysname:
                if(offset != 0)
                        error(Ebadarg);
                if(n <= 0 || n >= sizeof buf)
                        error(Ebadarg);
                strncpy(buf, a, n);
                buf[n] = 0;
                if(buf[n-1] == '\n')
                        buf[n-1] = 0;
                kstrdup(&sysname, buf);
                break;

        default:
                print("conswrite: %#llux\n", c->qid.path);
                error(Egreg);
        }
        return n;
}

Dev consdevtab = {
        'c',
        "cons",

        devreset,
        consinit,
        devshutdown,
        consattach,
        conswalk,
        consstat,
        consopen,
        devcreate,
        consclose,
        consread,
        devbread,
        conswrite,
        devbwrite,
        devremove,
        devwstat,
};

static  ulong   randn;

static void
seedrand(void)
{
        if(!waserror()){
                randomread((void*)&randn, sizeof(randn));
                poperror();
        }
}

int
nrand(int n)
{
        if(randn == 0)
                seedrand();
        randn = randn*1103515245 + 12345 + MACHP(0)->ticks;
        return (randn>>16) % n;
}

int
rand(void)
{
        nrand(1);
        return randn;
}

static uvlong uvorder = 0x0001020304050607ULL;

static uchar*
le2vlong(vlong *to, uchar *f)
{
        uchar *t, *o;
        int i;

        t = (uchar*)to;
        o = (uchar*)&uvorder;
        for(i = 0; i < sizeof(vlong); i++)
                t[o[i]] = f[i];
        return f+sizeof(vlong);
}

static uchar*
vlong2le(uchar *t, vlong from)
{
        uchar *f, *o;
        int i;

        f = (uchar*)&from;
        o = (uchar*)&uvorder;
        for(i = 0; i < sizeof(vlong); i++)
                t[i] = f[o[i]];
        return t+sizeof(vlong);
}

static long order = 0x00010203;

static uchar*
le2long(long *to, uchar *f)
{
        uchar *t, *o;
        int i;

        t = (uchar*)to;
        o = (uchar*)&order;
        for(i = 0; i < sizeof(long); i++)
                t[o[i]] = f[i];
        return f+sizeof(long);
}

static uchar*
long2le(uchar *t, long from)
{
        uchar *f, *o;
        int i;

        f = (uchar*)&from;
        o = (uchar*)&order;
        for(i = 0; i < sizeof(long); i++)
                t[i] = f[o[i]];
        return t+sizeof(long);
}

char *Ebadtimectl = "bad time control";

/*
 *  like the old #c/time but with added info.  Return
 *
 *      secs    nanosecs        fastticks       fasthz
 */
static int
readtime(ulong off, char *buf, int n)
{
        vlong   nsec, ticks;
        long sec;
        char str[7*NUMSIZE];

        nsec = todget(&ticks);
        if(fasthz == 0LL)
                fastticks((uvlong*)&fasthz);
        sec = nsec/1000000000ULL;
        snprint(str, sizeof(str), "%*lud %*llud %*llud %*llud ",
                NUMSIZE-1, sec,
                VLNUMSIZE-1, nsec,
                VLNUMSIZE-1, ticks,
                VLNUMSIZE-1, fasthz);
        return readstr(off, buf, n, str);
}

/*
 *  set the time in seconds
 */
static int
writetime(char *buf, int n)
{
        char b[13];
        long i;
        vlong now;

        if(n >= sizeof(b))
                error(Ebadtimectl);
        strncpy(b, buf, n);
        b[n] = 0;
        i = strtol(b, 0, 0);
        if(i <= 0)
                error(Ebadtimectl);
        now = i*1000000000LL;
        todset(now, 0, 0);
        return n;
}

/*
 *  read binary time info.  all numbers are little endian.
 *  ticks and nsec are syncronized.
 */
static int
readbintime(char *buf, int n)
{
        int i;
        vlong nsec, ticks;
        uchar *b = (uchar*)buf;

        i = 0;
        if(fasthz == 0LL)
                fastticks((uvlong*)&fasthz);
        nsec = todget(&ticks);
        if(n >= 3*sizeof(uvlong)){
                vlong2le(b+2*sizeof(uvlong), fasthz);
                i += sizeof(uvlong);
        }
        if(n >= 2*sizeof(uvlong)){
                vlong2le(b+sizeof(uvlong), ticks);
                i += sizeof(uvlong);
        }
        if(n >= 8){
                vlong2le(b, nsec);
                i += sizeof(vlong);
        }
        return i;
}

/*
 *  set any of the following
 *      - time in nsec
 *      - nsec trim applied over some seconds
 *      - clock frequency
 */
static int
writebintime(char *buf, int n)
{
        uchar *p;
        vlong delta;
        long period;

        n--;
        p = (uchar*)buf + 1;
        switch(*buf){
        case 'n':
                if(n < sizeof(vlong))
                        error(Ebadtimectl);
                le2vlong(&delta, p);
                todset(delta, 0, 0);
                break;
        case 'd':
                if(n < sizeof(vlong)+sizeof(long))
                        error(Ebadtimectl);
                p = le2vlong(&delta, p);
                le2long(&period, p);
                todset(-1, delta, period);
                break;
        case 'f':
                if(n < sizeof(uvlong))
                        error(Ebadtimectl);
                le2vlong(&fasthz, p);
                if(fasthz <= 0)
                        error(Ebadtimectl);
                todsetfreq(fasthz);
                break;
        }
        return n;
}