code: 9ferno

ref: 83246e296ea433b65b9d295b5e08fedd39ff1ab7
dir: /os/port/devcons.c/

View raw version
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	<version.h>
#include	"mp.h"
#include	"libsec.h"
#include	"keyboard.h"

extern int cflag;
extern int keepbroken;

void	(*serwrite)(char *, int);

Queue*	kscanq;			/* keyboard raw scancodes (when needed) */
char*	kscanid;		/* name of raw scan format (if defined) */
Queue*	kbdq;			/* unprocessed console input */
Queue*	lineq;			/* processed console input */
Queue*	printq;			/* console output */
Queue*	klogq;			/* kernel print (log) output */
int	iprintscreenputs;
int	panicking;

static struct
{
	RWlock;
	Queue*	q;
} kprintq;

static struct
{
	QLock;

	int	raw;		/* true if we shouldn't process input */
	int	ctl;		/* number of opens to the control file */
	int	kbdr;		/* number of open reads to the keyboard */
	int	scan;		/* true if reading raw scancodes */
	int	x;		/* index into line */
	char	line[1024];	/* current input line */

	char	c;
	int	count;
	int	repeat;
} kbd;

char*	sysname;
char*	eve;

enum
{
	CMreboot,
	CMhalt,
	CMpanic,
	CMbroken,
	CMnobroken,
	CMconsole,
};

static Cmdtab sysctlcmd[] =
{
	CMreboot,	"reboot",	0,
	CMhalt,	"halt", 0,
	CMpanic,	"panic", 0,
	CMconsole,	"console", 1,
	CMbroken,	"broken", 0,
	CMnobroken,	"nobroken", 0,
};

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

/*
 *  return true if current user is eve
 */
int
iseve(void)
{
	Osenv *o;

	o = up->env;
	return strcmp(eve, o->user) == 0;
}

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

static void
prflush(void)
{
	ulong now;

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

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;
	char buf[PRINTSIZE+2];

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

	/*
	 *  if kprint is open, put the message there, otherwise
	 *  if there's an attached bit mapped display,
	 *  put the message there.
	 */
	m = consoleprint;
	if(canrlock(&kprintq)){
		if(kprintq.q != nil){
			if(waserror()){
				runlock(&kprintq);
				nexterror();
			}
			if(usewrite)
				qwrite(kprintq.q, str, n);
			else
				qiwrite(kprintq.q, str, n);
			poperror();
			m = 0;
		}
		runlock(&kprintq);
	}
	if(m && screenputs != nil){
		screenputs(str, n);
	}

	/*
	 *  if there's a serial line being used as a console,
	 *  put the message there.
	 */
	if(serwrite != nil) {
		serwrite(str, n);
		return;
	}

	if(printq == 0)
		return;

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

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

int
snprint(char *s, int n, char *fmt, ...)
{
	va_list arg;

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

	return n;
}

int
sprint(char *s, char *fmt, ...)
{
	int n;
	va_list arg;

	va_start(arg, fmt);
	n = vseprint(s, s+PRINTSIZE, fmt, arg) - s;
	va_end(arg);

	return n;
}

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

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

	return n;
}

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

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

	return n;
}

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

	va_start(arg, fmt);
	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
	va_end(arg);
	if(qfull(klogq))
		qflush(klogq);
	return qproduce(klogq, buf, n);
}

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

	s = splhi();
	va_start(arg, fmt);
	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
	va_end(arg);
	if(screenputs != nil && iprintscreenputs)
		screenputs(buf, n);
	uartputs(buf, n);
	splx(s);

	return n;
}

void
setpanic(void)
{
	panicking = 1;
}

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

	setpanic();
	kprintq.q = nil;
	strcpy(buf, "panic: ");
	va_start(arg, fmt);
	n = vseprint(buf+strlen(buf), buf+sizeof(buf)-1, fmt, arg) - buf;
	va_end(arg);
	buf[n] = '\n';
	putstrn(buf, n+1);
	spllo();
	dumpstack();

	exit(1);
}

void
_assert(char *fmt)
{
	panic("assert failed: %s", fmt);
}

/*
 * mainly for libmp
 */
void
sysfatal(char *fmt, ...)
{
	va_list arg;
	char buf[64];

	va_start(arg, fmt);
	vsnprint(buf, sizeof(buf), fmt, arg);
	va_end(arg);
	error(buf);
}

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

	n = sprint(buf, "%s %d: ", up->text, up->pid);
	va_start(arg, fmt);
	n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf;
	va_end(arg);

	o = up->env;
	if(o->fgrp == 0) {
		print("%s", buf);
		return 0;
	}
	c = o->fgrp->fd[2];
	if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) {
		print("%s", buf);
		return 0;
	}

	if(waserror()) {
		print("%s", buf);
		return 0;
	}
	devtab[c->type]->write(c, buf, n, c->offset);
	poperror();

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

	return n;
}

void
echo(Rune r, char *buf, int n)
{
	if(kbd.raw)
		return;

	if(r == '\n'){
		if(printq)
			qiwrite(printq, "\r", 1);
	} else if(r == 0x15){
		buf = "^U\n";
		n = 3;
	}
	if(consoleprint && screenputs != nil)
		screenputs(buf, n);
	if(printq)
		qiwrite(printq, buf, n);
}

/*
 *	Debug key support.  Allows other parts of the kernel to register debug
 *	key handlers, instead of devcons.c having to know whatever's out there.
 *	A kproc is used to invoke most handlers, rather than tying up the CPU at
 *	splhi, which can choke some device drivers (eg softmodem).
 */
typedef struct {
	Rune	r;
	char	*m;
	void	(*f)(Rune);
	int	i;	/* function called at interrupt time */
} Dbgkey;

static struct {
	Rendez;
	Dbgkey	*work;
	Dbgkey	keys[50];
	int	nkeys;
	int	on;
} dbg;

static Dbgkey *
finddbgkey(Rune r)
{
	int i;
	Dbgkey *dp;

	for(dp = dbg.keys, i = 0; i < dbg.nkeys; i++, dp++)
		if(dp->r == r)
			return dp;
	return nil;
}

static int
dbgwork(void *)
{
	return dbg.work != 0;
}

static void
dbgproc(void *)
{
	Dbgkey *dp;

	setpri(PriRealtime);
	for(;;) {
		do {
			sleep(&dbg, dbgwork, 0);
			dp = dbg.work;
		} while(dp == nil);
		dp->f(dp->r);
		dbg.work = nil;
	}
}

void
debugkey(Rune r, char *msg, void (*fcn)(), int iflag)
{
	Dbgkey *dp;

	if(dbg.nkeys >= nelem(dbg.keys))
		return;
	if(finddbgkey(r) != nil)
		return;
	for(dp = &dbg.keys[dbg.nkeys++] - 1; dp >= dbg.keys; dp--) {
		if(strcmp(dp->m, msg) < 0)
			break;
		dp[1] = dp[0];
	}
	dp++;
	dp->r = r;
	dp->m = msg;
	dp->f = fcn;
	dp->i = iflag;
}

static int
isdbgkey(Rune r)
{
	static int ctrlt;
	Dbgkey *dp;
	int echoctrlt = ctrlt;

	/*
	 * ^t hack BUG
	 */
	if(dbg.on || (ctrlt >= 2)) {
		if(r == 0x14 || r == 0x05) {
			ctrlt++;
			return 0;
		}
		if(dp = finddbgkey(r)) {
			if(dp->i || ctrlt > 2)
				dp->f(r);
			else {
				dbg.work = dp;
				wakeup(&dbg);
			}
			ctrlt = 0;
			return 1;
		}
		ctrlt = 0;
	}
	else if(r == 0x14){
		ctrlt++;
		return 1;
	}
	else
		ctrlt = 0;
	if(echoctrlt){
		char buf[UTFmax];

		buf[0] = 0x14;
		while(--echoctrlt >= 0){
			echo(buf[0], buf, 1);
			qproduce(kbdq, buf, 1);
		}
	}
	return 0;
}

static void
dbgtoggle(Rune)
{
	dbg.on = !dbg.on;
	print("Debug keys %s\n", dbg.on ? "HOT" : "COLD");
}

static void
dbghelp(void)
{
	int i;
	Dbgkey *dp;
	Dbgkey *dp2;
	static char fmt[] = "%c: %-22s";

	dp = dbg.keys;
	dp2 = dp + (dbg.nkeys + 1)/2;
	for(i = dbg.nkeys; i > 1; i -= 2, dp++, dp2++) {
		print(fmt, dp->r, dp->m);
		print(fmt, dp2->r, dp2->m);
		print("\n");
	}
	if(i)
		print(fmt, dp->r, dp->m);
	print("\n");
}

static void
debuginit(void)
{
	kproc("consdbg", dbgproc, nil, 0);
	debugkey('|', "HOT|COLD keys", dbgtoggle, 0);
	debugkey('?', "help", dbghelp, 0);
}

/*
 *  Called by a uart interrupt for console input.
 *
 *  turn '\r' into '\n' before putting it into the queue.
 */
int
kbdcr2nl(Queue *q, int ch)
{
	if(ch == '\r')
		ch = '\n';
	return kbdputc(q, ch);
}

/*
 *  Put character, possibly a rune, into read queue at interrupt time.
 *  Performs translation for compose sequences
 *  Called at interrupt time to process a character.
 */
int
kbdputc(Queue *q, int ch)
{
	int n;
	char buf[UTFmax];
	Rune r;
	static Rune kc[15];
	static int nk, collecting = 0;

	r = ch;
	if(r == Latin) {
		collecting = 1;
		nk = 0;
		return 0;
	}
	if(collecting) {
		int c;
		nk += runetochar((char*)&kc[nk], &r);
		c = latin1(kc, nk);
		if(c < -1)	/* need more keystrokes */
			return 0;
		collecting = 0;
		if(c == -1) {	/* invalid sequence */
			echo(kc[0], (char*)kc, nk);
			qproduce(q, kc, nk);
			return 0;
		}
		r = (Rune)c;
	}
	kbd.c = r;
	n = runetochar(buf, &r);
	if(n == 0)
		return 0;
	if(!isdbgkey(r)) {
		echo(r, buf, n);
		qproduce(q, buf, n);
	}
	return 0;
}

void
kbdrepeat(int rep)
{
	kbd.repeat = rep;
	kbd.count = 0;
}

void
kbdclock(void)
{
	if(kbd.repeat == 0)
		return;
	if(kbd.repeat==1 && ++kbd.count>HZ){
		kbd.repeat = 2;
		kbd.count = 0;
		return;
	}
	if(++kbd.count&1)
		kbdputc(kbdq, kbd.c);
}

enum{
	Qdir,
	Qcons,
	Qsysctl,
	Qconsctl,
	Qdrivers,
	Qhostowner,
	Qkeyboard,
	Qklog,
	Qkprint,
	Qscancode,
	Qmemory,
	Qmsec,
	Qnull,
	Qrandom,
	Qnotquiterandom,
	Qsysname,
	Qtime,
	Quser,
	Qjit,
};

static Dirtab consdir[]=
{
	".",	{Qdir, 0, QTDIR},	0,		DMDIR|0555,
	"cons",		{Qcons},	0,		0660,
	"consctl",	{Qconsctl},	0,		0220,
	"sysctl",	{Qsysctl},	0,		0644,
	"drivers",	{Qdrivers},	0,		0444,
	"hostowner",	{Qhostowner},	0,	0644,
	"keyboard",	{Qkeyboard},	0,		0666,
	"klog",		{Qklog},	0,		0444,
	"kprint",		{Qkprint},	0,		0444,
	"scancode",	{Qscancode},	0,		0444,
	"memory",	{Qmemory},	0,		0444,
	"msec",		{Qmsec},	NUMSIZE,	0444,
	"null",		{Qnull},	0,		0666,
	"random",	{Qrandom},	0,		0444,
	"notquiterandom", {Qnotquiterandom}, 0,	0444,
	"sysname",	{Qsysname},	0,		0664,
	"time",		{Qtime},	0,		0664,
	"user",		{Quser},	0,	0644,
	"jit",		{Qjit},	0,	0666,
};

ulong	boottime;		/* seconds since epoch at boot */

long
seconds(void)
{
	return boottime + TK2SEC(MACHP(0)->ticks);
}

vlong
mseconds(void)
{
	return ((vlong)boottime*1000)+((vlong)(TK2MS(MACHP(0)->ticks)));
}

vlong
osusectime(void)
{
	return (((vlong)boottime*1000)+((vlong)(TK2MS(MACHP(0)->ticks)))*1000);
}

vlong
nsec(void)
{
	return osusectime()*1000;	/* TO DO */
}

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

	if(size > 64) size = 64;

	snprint(tmp, sizeof(tmp), "%*.0lud ", size, val);
	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;
}

void
fddump()
{
	Proc *p;
	Osenv *o;
	int i;
	Chan *c;

	p = proctab(6);
	o = p->env;
	for(i = 0; i <= o->fgrp->maxfd; i++) {
		if((c = o->fgrp->fd[i]) == nil)
			continue;
		print("%d: %s\n", i, c->name == nil? "???": c->name->s);
	}
}

static void
qpanic(Rune)
{
	panic("User requested panic.");
}

static void
rexit(Rune)
{
	exit(0);
}

static void
consinit(void)
{
	randominit();
	debuginit();
	debugkey('f', "files/6", fddump, 0);
	debugkey('q', "panic", qpanic, 1);
	debugkey('r', "exit", rexit, 1);
	klogq = qopen(128*1024, 0, 0, 0);
}

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

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

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

static void
flushkbdline(Queue *q)
{
	if(kbd.x){
		qwrite(q, kbd.line, kbd.x);
		kbd.x = 0;
	}
}

static Chan*
consopen(Chan *c, u32 omode)
{
	c->aux = 0;
	switch((u64)c->qid.path){
	case Qconsctl:
		if(!iseve())
			error(Eperm);
		qlock(&kbd);
		kbd.ctl++;
		qunlock(&kbd);
		break;

	case Qkeyboard:
		if((omode & 3) != OWRITE) {
			qlock(&kbd);
			kbd.kbdr++;
			flushkbdline(kbdq);
			kbd.raw = 1;
			qunlock(&kbd);
		}
		break;

	case Qscancode:
		qlock(&kbd);
		if(kscanq || !kscanid) {
			qunlock(&kbd);
			c->flag &= ~COPEN;
			if(kscanq)
				error(Einuse);
			else
				error(Ebadarg);
		}
		kscanq = qopen(256, 0, nil, nil);
		qunlock(&kbd);
		break;

	case Qkprint:
		if((omode & 3) != OWRITE) {
			wlock(&kprintq);
			if(kprintq.q != nil){
				wunlock(&kprintq);
				error(Einuse);
			}
			kprintq.q = qopen(32*1024, Qcoalesce, nil, nil);
			if(kprintq.q == nil){
				wunlock(&kprintq);
				error(Enomem);
			}
			qnoblock(kprintq.q, 1);
			wunlock(&kprintq);
			c->iounit = qiomaxatomic;
		}
		break;
	}
	return devopen(c, omode, consdir, nelem(consdir), devgen);
}

static void
consclose(Chan *c)
{
	if((c->flag&COPEN) == 0)
		return;

	switch((u64)c->qid.path){
	case Qconsctl:
		/* last close of control file turns off raw */
		qlock(&kbd);
		if(--kbd.ctl == 0)
			kbd.raw = 0;
		qunlock(&kbd);
		break;

	case Qkeyboard:
		if(c->mode != OWRITE) {
			qlock(&kbd);
			--kbd.kbdr;
			qunlock(&kbd);
		}
		break;

	case Qscancode:
		qlock(&kbd);
		if(kscanq) {
			qfree(kscanq);
			kscanq = 0;
		}
		qunlock(&kbd);
		break;

	case Qkprint:
		wlock(&kprintq);
		qfree(kprintq.q);
		kprintq.q = nil;
		wunlock(&kprintq);
		break;
	}
}

static s32
consread(Chan *c, void *buf, s32 n, s64 offset)
{
	int l;
	Osenv *o;
	int ch, eol, i;
	char *p, tmp[128];
	char *cbuf = buf;

	if(n <= 0)
		return n;
	o = up->env;
	switch((u64)c->qid.path){
	case Qdir:
		return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
	case Qsysctl:
		return readstr(offset, buf, n, VERSION);
	case Qcons:
	case Qkeyboard:
		qlock(&kbd);
		if(waserror()) {
			qunlock(&kbd);
			nexterror();
		}
		if(kbd.raw || kbd.kbdr) {
			if(qcanread(lineq))
				n = qread(lineq, buf, n);
			else {
				/* read as much as possible */
				do {
					i = qread(kbdq, cbuf, n);
					cbuf += i;
					n -= i;
				} while(n>0 && qcanread(kbdq));
				n = cbuf - (char*)buf;
			}
		} else {
			while(!qcanread(lineq)) {
				qread(kbdq, &kbd.line[kbd.x], 1);
				ch = kbd.line[kbd.x];
				eol = 0;
				switch(ch){
				case '\b':
					if(kbd.x)
						kbd.x--;
					break;
				case 0x15:
					kbd.x = 0;
					break;
				case '\n':
				case 0x04:
					eol = 1;
				default:
					kbd.line[kbd.x++] = ch;
					break;
				}
				if(kbd.x == sizeof(kbd.line) || eol) {
					if(ch == 0x04)
						kbd.x--;
					qwrite(lineq, kbd.line, kbd.x);
					kbd.x = 0;
				}
			}
			n = qread(lineq, buf, n);
		}
		qunlock(&kbd);
		poperror();
		return n;

	case Qscancode:
		if(offset == 0)
			return readstr(0, buf, n, kscanid);
		else
			return qread(kscanq, buf, n);

	case Qtime:
		snprint(tmp, sizeof(tmp), "%.lld", (vlong)mseconds()*1000);
		return readstr(offset, buf, n, tmp);

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

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

	case Qjit:
		snprint(tmp, sizeof(tmp), "%d", cflag);
		return readstr(offset, buf, n, tmp);

	case Qnull:
		return 0;

	case Qmsec:
		return readnum(offset, buf, n, TK2MS(MACHP(0)->ticks), NUMSIZE);

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

	case Qnotquiterandom:
		genrandom(buf, n);
		return n;

	case Qrandom:
		return randomread(buf, n);

	case Qmemory:
		return poolread(buf, n, offset);

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

	case Qklog:
		return qread(klogq, buf, n);

	case Qkprint:
		rlock(&kprintq);
		if(waserror()){
			runlock(&kprintq);
			nexterror();
		}
		n = qread(kprintq.q, buf, n);
		poperror();
		runlock(&kprintq);
		return n;

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

static s32
conswrite(Chan *c, void *va, s32 n, s64 offset)
{
	s64 t;
	long l, bp;
	char *a = va;
	Cmdbuf *cb;
	Cmdtab *ct;
	char buf[256];
	int x;

	switch((u64)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(a, 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){
				qlock(&kbd);
				flushkbdline(kbdq);
				kbd.raw = 1;
				qunlock(&kbd);
			} else if(strncmp(a, "rawoff", 6) == 0){
				qlock(&kbd);
				kbd.raw = 0;
				kbd.x = 0;
				qunlock(&kbd);
			}
			if(a = strchr(a, ' '))
				a++;
		}
		break;

	case Qkeyboard:
		for(x=0; x<n; ) {
			Rune r;
			x += chartorune(&r, &a[x]);
			kbdputc(kbdq, r);
		}
		break;
	
	case Qtime:
		if(n >= sizeof(buf))
			n = sizeof(buf)-1;
		strncpy(buf, a, n);
		buf[n] = 0;
		t = strtoll(buf, 0, 0)/1000000;
		boottime = t - TK2SEC(MACHP(0)->ticks);
		break;

	case Qhostowner:
		if(!iseve())
			error(Eperm);
		if(offset != 0 || n >= sizeof(buf))
			error(Ebadarg);
		memmove(buf, a, n);
		buf[n] = '\0';
		if(n > 0 && buf[n-1] == '\n')
			buf[--n] = 0;
		if(n <= 0)
			error(Ebadarg);
		renameuser(eve, buf);
		renameproguser(eve, buf);
		kstrdup(&eve, buf);
		kstrdup(&up->env->user, buf);
		break;

	case Quser:
		if(!iseve())
			error(Eperm);
		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(&up->env->user, buf);
		break;

	case Qjit:
		if(n >= sizeof(buf))
			n = sizeof(buf)-1;
		strncpy(buf, va, n);
		buf[n] = '\0';
		x = atoi(buf);
		if(x < 0 || x > 9)
			error(Ebadarg);
		cflag = x;
		return n;

	case Qnull:
		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;

	case Qsysctl:
		if(!iseve())
			error(Eperm);
		cb = parsecmd(a, n);
		if(waserror()){
			free(cb);
			nexterror();
		}
		ct = lookupcmd(cb, sysctlcmd, nelem(sysctlcmd));
		switch(ct->index){
		case CMreboot:
			reboot();
			break;
		case CMhalt:
			halt();
			break;
		case CMpanic:
			panic("sysctl");
		case CMconsole:
			consoleprint = strcmp(cb->f[1], "off") != 0;
			break;
		case CMbroken:
			keepbroken = 1;
			break;
		case CMnobroken:
			keepbroken = 0;
			break;
		}
		poperror();
		free(cb);
		break;

	default:
		print("conswrite: %llud\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)
{
	randomread((void*)&randn, sizeof(randn));
}

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;
}

ulong
truerand(void)
{
	ulong x;

	randomread(&x, sizeof(x));
	return x;
}