plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

cpu.c (20341B)


      1 /*
      2  * cpu.c - Make a connection to a cpu server
      3  *
      4  *	   Invoked by listen as 'cpu -R | -N service net netdir'
      5  *	    	   by users  as 'cpu [-h system] [-c cmd args ...]'
      6  */
      7 
      8 #include <u.h>
      9 #include <libc.h>
     10 #include <bio.h>
     11 #include <auth.h>
     12 #include <fcall.h>
     13 #include <libsec.h>
     14 
     15 #define	Maxfdata 8192
     16 
     17 void	remoteside(int);
     18 void	fatal(int, char*, ...);
     19 void	lclnoteproc(int);
     20 void	rmtnoteproc(void);
     21 void	catcher(void*, char*);
     22 void	usage(void);
     23 void	writestr(int, char*, char*, int);
     24 int	readstr(int, char*, int);
     25 char	*rexcall(int*, char*, char*);
     26 int	setamalg(char*);
     27 
     28 int 	notechan;
     29 char	system[32];
     30 int	cflag;
     31 int	hflag;
     32 int	dbg;
     33 char	*user;
     34 
     35 char	*srvname = "ncpu";
     36 char	*exportfs = "/bin/exportfs";
     37 char	*ealgs = "rc4_256 sha1";
     38 
     39 /* message size for exportfs; may be larger so we can do big graphics in CPU window */
     40 int	msgsize = 8192+IOHDRSZ;
     41 
     42 /* authentication mechanisms */
     43 static int	netkeyauth(int);
     44 static int	netkeysrvauth(int, char*);
     45 static int	p9auth(int);
     46 static int	srvp9auth(int, char*);
     47 static int	noauth(int);
     48 static int	srvnoauth(int, char*);
     49 
     50 typedef struct AuthMethod AuthMethod;
     51 struct AuthMethod {
     52 	char	*name;			/* name of method */
     53 	int	(*cf)(int);		/* client side authentication */
     54 	int	(*sf)(int, char*);	/* server side authentication */
     55 } authmethod[] =
     56 {
     57 	{ "p9",		p9auth,		srvp9auth,},
     58 	{ "netkey",	netkeyauth,	netkeysrvauth,},
     59 /*	{ "none",	noauth,		srvnoauth,}, */
     60 	{ nil,	nil}
     61 };
     62 AuthMethod *am = authmethod;	/* default is p9 */
     63 
     64 char *p9authproto = "p9any";
     65 
     66 int setam(char*);
     67 
     68 void
     69 usage(void)
     70 {
     71 	fprint(2, "usage: cpu [-h system] [-a authmethod] [-e 'crypt hash'] [-c cmd args ...]\n");
     72 	exits("usage");
     73 }
     74 int fdd;
     75 
     76 void
     77 main(int argc, char **argv)
     78 {
     79 	char dat[128], buf[128], cmd[128], *p, *err;
     80 	int fd, ms, kms, data;
     81 
     82 	/* see if we should use a larger message size */
     83 	fd = open("/dev/draw", OREAD);
     84 	if(fd > 0){
     85 		ms = iounit(fd);
     86 		if(ms != 0 && ms < ms+IOHDRSZ)
     87 			msgsize = ms+IOHDRSZ;
     88 		close(fd);
     89 	}
     90 	kms = kiounit();
     91 	if(msgsize > kms-IOHDRSZ-100)	/* 100 for network packets, etc. */
     92 		msgsize = kms-IOHDRSZ-100;
     93 
     94 	user = getuser();
     95 	if(user == nil)
     96 		fatal(1, "can't read user name");
     97 	ARGBEGIN{
     98 	case 'a':
     99 		p = EARGF(usage());
    100 		if(setam(p) < 0)
    101 			fatal(0, "unknown auth method %s", p);
    102 		break;
    103 	case 'e':
    104 		ealgs = EARGF(usage());
    105 		if(*ealgs == 0 || strcmp(ealgs, "clear") == 0)
    106 			ealgs = nil;
    107 		break;
    108 	case 'd':
    109 		dbg++;
    110 		break;
    111 	case 'f':
    112 		/* ignored but accepted for compatibility */
    113 		break;
    114 	case 'O':
    115 		p9authproto = "p9sk2";
    116 		remoteside(1);				/* From listen */
    117 		break;
    118 	case 'R':				/* From listen */
    119 		remoteside(0);
    120 		break;
    121 	case 'h':
    122 		hflag++;
    123 		p = EARGF(usage());
    124 		strcpy(system, p);
    125 		break;
    126 	case 'c':
    127 		cflag++;
    128 		cmd[0] = '!';
    129 		cmd[1] = '\0';
    130 		while(p = ARGF()) {
    131 			strcat(cmd, " ");
    132 			strcat(cmd, p);
    133 		}
    134 		break;
    135 	case 'o':
    136 		p9authproto = "p9sk2";
    137 		srvname = "cpu";
    138 		break;
    139 	case 'u':
    140 		user = EARGF(usage());
    141 		break;
    142 	default:
    143 		usage();
    144 	}ARGEND;
    145 
    146 
    147 	if(argc != 0)
    148 		usage();
    149 
    150 	if(hflag == 0) {
    151 		p = getenv("cpu");
    152 		if(p == 0)
    153 			fatal(0, "set $cpu");
    154 		strcpy(system, p);
    155 	}
    156 
    157 	if(err = rexcall(&data, system, srvname))
    158 		fatal(1, "%s: %s", err, system);
    159 
    160 	/* Tell the remote side the command to execute and where our working directory is */
    161 	if(cflag)
    162 		writestr(data, cmd, "command", 0);
    163 	if(getwd(dat, sizeof(dat)) == 0)
    164 		writestr(data, "NO", "dir", 0);
    165 	else
    166 		writestr(data, dat, "dir", 0);
    167 
    168 	/* start up a process to pass along notes */
    169 	lclnoteproc(data);
    170 
    171 	/*
    172 	 *  Wait for the other end to execute and start our file service
    173 	 *  of /mnt/term
    174 	 */
    175 	if(readstr(data, buf, sizeof(buf)) < 0)
    176 		fatal(1, "waiting for FS");
    177 	if(strncmp("FS", buf, 2) != 0) {
    178 		print("remote cpu: %s", buf);
    179 		exits(buf);
    180 	}
    181 
    182 	/* Begin serving the gnot namespace */
    183 	close(0);
    184 	dup(data, 0);
    185 	close(data);
    186 	sprint(buf, "%d", msgsize);
    187 	if(dbg)
    188 		execl(exportfs, exportfs, "-dm", buf, 0);
    189 	else
    190 		execl(exportfs, exportfs, "-m", buf, 0);
    191 	fatal(1, "starting exportfs");
    192 }
    193 
    194 void
    195 fatal(int syserr, char *fmt, ...)
    196 {
    197 	char buf[ERRMAX];
    198 	va_list arg;
    199 
    200 	va_start(arg, fmt);
    201 	doprint(buf, buf+sizeof(buf), fmt, arg);
    202 	va_end(arg);
    203 	if(syserr)
    204 		fprint(2, "cpu: %s: %r\n", buf);
    205 	else
    206 		fprint(2, "cpu: %s\n", buf);
    207 	exits(buf);
    208 }
    209 
    210 char *negstr = "negotiating authentication method";
    211 
    212 char bug[256];
    213 
    214 int
    215 old9p(int fd)
    216 {
    217 	int p[2];
    218 
    219 	if(pipe(p) < 0)
    220 		fatal(1, "pipe");
    221 
    222 	switch(rfork(RFPROC|RFFDG|RFNAMEG)) {
    223 	case -1:
    224 		fatal(1, "rfork srvold9p");
    225 	case 0:
    226 		if(fd != 1){
    227 			dup(fd, 1);
    228 			close(fd);
    229 		}
    230 		if(p[0] != 0){
    231 			dup(p[0], 0);
    232 			close(p[0]);
    233 		}
    234 		close(p[1]);
    235 		if(0){
    236 			fd = open("/sys/log/cpu", OWRITE);
    237 			if(fd != 2){
    238 				dup(fd, 2);
    239 				close(fd);
    240 			}
    241 			execl("/bin/srvold9p", "srvold9p", "-ds", 0);
    242 		} else
    243 			execl("/bin/srvold9p", "srvold9p", "-s", 0);
    244 		fatal(1, "exec srvold9p");
    245 	default:
    246 		close(fd);
    247 		close(p[0]);
    248 	}
    249 	return p[1];
    250 }
    251 
    252 /* Invoked with stdin, stdout and stderr connected to the network connection */
    253 void
    254 remoteside(int old)
    255 {
    256 	char user[128], home[128], buf[128], xdir[128], cmd[128];
    257 	int i, n, fd, badchdir, gotcmd;
    258 
    259 	fd = 0;
    260 
    261 	/* negotiate authentication mechanism */
    262 	n = readstr(fd, cmd, sizeof(cmd));
    263 	if(n < 0)
    264 		fatal(1, "authenticating");
    265 	if(setamalg(cmd) < 0){
    266 		writestr(fd, "unsupported auth method", nil, 0);
    267 		fatal(1, "bad auth method %s", cmd);
    268 	} else
    269 		writestr(fd, "", "", 1);
    270 
    271 	fd = (*am->sf)(fd, user);
    272 	if(fd < 0)
    273 		fatal(1, "srvauth");
    274 
    275 	/* Set environment values for the user */
    276 	putenv("user", user);
    277 	sprint(home, "/usr/%s", user);
    278 	putenv("home", home);
    279 
    280 	/* Now collect invoking cpu's current directory or possibly a command */
    281 	gotcmd = 0;
    282 	if(readstr(fd, xdir, sizeof(xdir)) < 0)
    283 		fatal(1, "dir/cmd");
    284 	if(xdir[0] == '!') {
    285 		strcpy(cmd, &xdir[1]);
    286 		gotcmd = 1;
    287 		if(readstr(fd, xdir, sizeof(xdir)) < 0)
    288 			fatal(1, "dir");
    289 	}
    290 
    291 	/* Establish the new process at the current working directory of the
    292 	 * gnot */
    293 	badchdir = 0;
    294 	if(strcmp(xdir, "NO") == 0)
    295 		chdir(home);
    296 	else if(chdir(xdir) < 0) {
    297 		badchdir = 1;
    298 		chdir(home);
    299 	}
    300 
    301 	/* Start the gnot serving its namespace */
    302 	writestr(fd, "FS", "FS", 0);
    303 	writestr(fd, "/", "exportfs dir", 0);
    304 
    305 	n = read(fd, buf, sizeof(buf));
    306 	if(n != 2 || buf[0] != 'O' || buf[1] != 'K')
    307 		exits("remote tree");
    308 
    309 	if(old)
    310 		fd = old9p(fd);
    311 
    312 	/* make sure buffers are big by doing fversion explicitly; pick a huge number; other side will trim */
    313 	strcpy(buf, VERSION9P);
    314 	if(fversion(fd, 64*1024, buf, sizeof buf) < 0)
    315 		exits("fversion failed");
    316 	if(mount(fd, -1, "/mnt/term", MCREATE|MREPL, "") < 0)
    317 		exits("mount failed");
    318 
    319 	close(fd);
    320 
    321 	/* the remote noteproc uses the mount so it must follow it */
    322 	rmtnoteproc();
    323 
    324 	for(i = 0; i < 3; i++)
    325 		close(i);
    326 
    327 	if(open("/mnt/term/dev/cons", OREAD) != 0)
    328 		exits("open stdin");
    329 	if(open("/mnt/term/dev/cons", OWRITE) != 1)
    330 		exits("open stdout");
    331 	dup(1, 2);
    332 
    333 	if(badchdir)
    334 		print("cpu: failed to chdir to '%s'\n", xdir);
    335 
    336 	if(gotcmd)
    337 		execl("/bin/rc", "rc", "-lc", cmd, 0);
    338 	else
    339 		execl("/bin/rc", "rc", "-li", 0);
    340 	fatal(1, "exec shell");
    341 }
    342 
    343 char*
    344 rexcall(int *fd, char *host, char *service)
    345 {
    346 	char *na;
    347 	char dir[128];
    348 	char err[ERRMAX];
    349 	char msg[128];
    350 	int n;
    351 
    352 	na = netmkaddr(host, 0, service);
    353 	if((*fd = dial(na, 0, dir, 0)) < 0)
    354 		return "can't dial";
    355 
    356 	/* negotiate authentication mechanism */
    357 	if(ealgs != nil)
    358 		snprint(msg, sizeof(msg), "%s %s", am->name, ealgs);
    359 	else
    360 		snprint(msg, sizeof(msg), "%s", am->name);
    361 	writestr(*fd, msg, negstr, 0);
    362 	n = readstr(*fd, err, sizeof err);
    363 	if(n < 0)
    364 		return negstr;
    365 	if(*err){
    366 		werrstr(err);
    367 		return negstr;
    368 	}
    369 
    370 	/* authenticate */
    371 	*fd = (*am->cf)(*fd);
    372 	if(*fd < 0)
    373 		return "can't authenticate";
    374 	return 0;
    375 }
    376 
    377 void
    378 writestr(int fd, char *str, char *thing, int ignore)
    379 {
    380 	int l, n;
    381 
    382 	l = strlen(str);
    383 	n = write(fd, str, l+1);
    384 	if(!ignore && n < 0)
    385 		fatal(1, "writing network: %s", thing);
    386 }
    387 
    388 int
    389 readstr(int fd, char *str, int len)
    390 {
    391 	int n;
    392 
    393 	while(len) {
    394 		n = read(fd, str, 1);
    395 		if(n < 0)
    396 			return -1;
    397 		if(*str == '\0')
    398 			return 0;
    399 		str++;
    400 		len--;
    401 	}
    402 	return -1;
    403 }
    404 
    405 static int
    406 readln(char *buf, int n)
    407 {
    408 	char *p = buf;
    409 
    410 	n--;
    411 	while(n > 0){
    412 		if(read(0, p, 1) != 1)
    413 			break;
    414 		if(*p == '\n' || *p == '\r'){
    415 			*p = 0;
    416 			return p-buf;
    417 		}
    418 		p++;
    419 	}
    420 	*p = 0;
    421 	return p-buf;
    422 }
    423 
    424 /*
    425  *  user level challenge/response
    426  */
    427 static int
    428 netkeyauth(int fd)
    429 {
    430 	char chall[32];
    431 	char resp[32];
    432 
    433 	strcpy(chall, getuser());
    434 	print("user[%s]: ", chall);
    435 	if(readln(resp, sizeof(resp)) < 0)
    436 		return -1;
    437 	if(*resp != 0)
    438 		strcpy(chall, resp);
    439 	writestr(fd, chall, "challenge/response", 1);
    440 
    441 	for(;;){
    442 		if(readstr(fd, chall, sizeof chall) < 0)
    443 			break;
    444 		if(*chall == 0)
    445 			return fd;
    446 		print("challenge: %s\nresponse: ", chall);
    447 		if(readln(resp, sizeof(resp)) < 0)
    448 			break;
    449 		writestr(fd, resp, "challenge/response", 1);
    450 	}
    451 	return -1;
    452 }
    453 
    454 static int
    455 netkeysrvauth(int fd, char *user)
    456 {
    457 	char response[32];
    458 	Chalstate *ch;
    459 	int tries;
    460 	AuthInfo *ai;
    461 
    462 	if(readstr(fd, user, 32) < 0)
    463 		return -1;
    464 
    465 	ai = nil;
    466 	ch = nil;
    467 	for(tries = 0; tries < 10; tries++){
    468 		if((ch = auth_challenge("p9cr", user, nil)) == nil)
    469 			return -1;
    470 		writestr(fd, ch->chal, "challenge", 1);
    471 		if(readstr(fd, response, sizeof response) < 0)
    472 			return -1;
    473 		ch->resp = response;
    474 		ch->nresp = strlen(response);
    475 		if((ai = auth_response(ch)) != nil)
    476 			break;
    477 	}
    478 	auth_freechal(ch);
    479 	if(ai == nil)
    480 		return -1;
    481 	writestr(fd, "", "challenge", 1);
    482 	if(auth_chuid(ai, 0) < 0)
    483 		fatal(1, "newns");
    484 	auth_freeAI(ai);
    485 	return fd;
    486 }
    487 
    488 static void
    489 mksecret(char *t, uchar *f)
    490 {
    491 	sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
    492 		f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]);
    493 }
    494 
    495 /*
    496  *  plan9 authentication followed by rc4 encryption
    497  */
    498 static int
    499 p9auth(int fd)
    500 {
    501 	uchar key[16];
    502 	uchar digest[SHA1dlen];
    503 	char fromclientsecret[21];
    504 	char fromserversecret[21];
    505 	int i;
    506 	AuthInfo *ai;
    507 
    508 	ai = auth_proxy(fd, auth_getkey, "proto=%q user=%q role=client", p9authproto, user);
    509 	if(ai == nil)
    510 		return -1;
    511 	memmove(key+4, ai->secret, ai->nsecret);
    512 	if(ealgs == nil)
    513 		return fd;
    514 
    515 	/* exchange random numbers */
    516 	srand(truerand());
    517 	for(i = 0; i < 4; i++)
    518 		key[i] = rand();
    519 	if(write(fd, key, 4) != 4)
    520 		return -1;
    521 	if(readn(fd, key+12, 4) != 4)
    522 		return -1;
    523 
    524 	/* scramble into two secrets */
    525 	sha1(key, sizeof(key), digest, nil);
    526 	mksecret(fromclientsecret, digest);
    527 	mksecret(fromserversecret, digest+10);
    528 
    529 	/* set up encryption */
    530 	i = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil);
    531 	if(i < 0)
    532 		werrstr("can't establish ssl connection: %r");
    533 	return i;
    534 }
    535 
    536 static int
    537 noauth(int fd)
    538 {
    539 	ealgs = nil;
    540 	return fd;
    541 }
    542 
    543 static int
    544 srvnoauth(int fd, char *user)
    545 {
    546 	strcpy(user, getuser());
    547 	ealgs = nil;
    548 	return fd;
    549 }
    550 
    551 void
    552 loghex(uchar *p, int n)
    553 {
    554 	char buf[100];
    555 	int i;
    556 
    557 	for(i = 0; i < n; i++)
    558 		sprint(buf+2*i, "%2.2ux", p[i]);
    559 	syslog(0, "cpu", buf);
    560 }
    561 
    562 static int
    563 srvp9auth(int fd, char *user)
    564 {
    565 	uchar key[16];
    566 	uchar digest[SHA1dlen];
    567 	char fromclientsecret[21];
    568 	char fromserversecret[21];
    569 	int i;
    570 	AuthInfo *ai;
    571 
    572 	ai = auth_proxy(0, nil, "proto=%q role=server", p9authproto);
    573 	if(ai == nil)
    574 		return -1;
    575 	if(auth_chuid(ai, nil) < 0)
    576 		return -1;
    577 	strcpy(user, ai->cuid);
    578 	memmove(key+4, ai->secret, ai->nsecret);
    579 
    580 	if(ealgs == nil)
    581 		return fd;
    582 
    583 	/* exchange random numbers */
    584 	srand(truerand());
    585 	for(i = 0; i < 4; i++)
    586 		key[i+12] = rand();
    587 	if(readn(fd, key, 4) != 4)
    588 		return -1;
    589 	if(write(fd, key+12, 4) != 4)
    590 		return -1;
    591 
    592 	/* scramble into two secrets */
    593 	sha1(key, sizeof(key), digest, nil);
    594 	mksecret(fromclientsecret, digest);
    595 	mksecret(fromserversecret, digest+10);
    596 
    597 	/* set up encryption */
    598 	i = pushssl(fd, ealgs, fromserversecret, fromclientsecret, nil);
    599 	if(i < 0)
    600 		werrstr("can't establish ssl connection: %r");
    601 	return i;
    602 }
    603 
    604 /*
    605  *  set authentication mechanism
    606  */
    607 int
    608 setam(char *name)
    609 {
    610 	for(am = authmethod; am->name != nil; am++)
    611 		if(strcmp(am->name, name) == 0)
    612 			return 0;
    613 	am = authmethod;
    614 	return -1;
    615 }
    616 
    617 /*
    618  *  set authentication mechanism and encryption/hash algs
    619  */
    620 int
    621 setamalg(char *s)
    622 {
    623 	ealgs = strchr(s, ' ');
    624 	if(ealgs != nil)
    625 		*ealgs++ = 0;
    626 	return setam(s);
    627 }
    628 
    629 char *rmtnotefile = "/mnt/term/dev/cpunote";
    630 
    631 /*
    632  *  loop reading /mnt/term/dev/note looking for notes.
    633  *  The child returns to start the shell.
    634  */
    635 void
    636 rmtnoteproc(void)
    637 {
    638 	int n, fd, pid, notepid;
    639 	char buf[256];
    640 
    641 	/* new proc returns to start shell */
    642 	pid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFMEM);
    643 	switch(pid){
    644 	case -1:
    645 		syslog(0, "cpu", "cpu -R: can't start noteproc: %r");
    646 		return;
    647 	case 0:
    648 		return;
    649 	}
    650 
    651 	/* new proc reads notes from other side and posts them to shell */
    652 	switch(notepid = rfork(RFPROC|RFFDG|RFMEM)){
    653 	case -1:
    654 		syslog(0, "cpu", "cpu -R: can't start wait proc: %r");
    655 		_exits(0);
    656 	case 0:
    657 		fd = open(rmtnotefile, OREAD);
    658 		if(fd < 0){
    659 			syslog(0, "cpu", "cpu -R: can't open %s", rmtnotefile);
    660 			_exits(0);
    661 		}
    662 
    663 		for(;;){
    664 			n = read(fd, buf, sizeof(buf)-1);
    665 			if(n <= 0){
    666 				postnote(PNGROUP, pid, "hangup");
    667 				_exits(0);
    668 			}
    669 			buf[n] = 0;
    670 			postnote(PNGROUP, pid, buf);
    671 		}
    672 		break;
    673 	}
    674 
    675 	/* original proc waits for shell proc to die and kills note proc */
    676 	for(;;){
    677 		n = waitpid();
    678 		if(n < 0 || n == pid)
    679 			break;
    680 	}
    681 	postnote(PNPROC, notepid, "kill");
    682 	_exits(0);
    683 }
    684 
    685 enum
    686 {
    687 	Qdir,
    688 	Qcpunote,
    689 
    690 	Nfid = 32
    691 };
    692 
    693 struct {
    694 	char	*name;
    695 	Qid	qid;
    696 	ulong	perm;
    697 } fstab[] =
    698 {
    699 	[Qdir]		{ ".",		{Qdir, 0, QTDIR},	DMDIR|0555	},
    700 	[Qcpunote]	{ "cpunote",	{Qcpunote, 0},		0444		}
    701 };
    702 
    703 typedef struct Note Note;
    704 struct Note
    705 {
    706 	Note *next;
    707 	char msg[ERRMAX];
    708 };
    709 
    710 typedef struct Request Request;
    711 struct Request
    712 {
    713 	Request *next;
    714 	Fcall f;
    715 };
    716 
    717 typedef struct Fid Fid;
    718 struct Fid
    719 {
    720 	int	fid;
    721 	int	file;
    722 };
    723 Fid fids[Nfid];
    724 
    725 struct {
    726 	Lock;
    727 	Note *nfirst, *nlast;
    728 	Request *rfirst, *rlast;
    729 } nfs;
    730 
    731 int
    732 fsreply(int fd, Fcall *f)
    733 {
    734 	uchar buf[IOHDRSZ+Maxfdata];
    735 	int n;
    736 
    737 	if(dbg)
    738 		fprint(2, "<-%F\n", f);
    739 	n = convS2M(f, buf, sizeof buf);
    740 	if(n > 0){
    741 		if(write(fd, buf, n) != n){
    742 			close(fd);
    743 			return -1;
    744 		}
    745 	}
    746 	return 0;
    747 }
    748 
    749 /* match a note read request with a note, reply to the request */
    750 int
    751 kick(int fd)
    752 {
    753 	Request *rp;
    754 	Note *np;
    755 	int rv;
    756 
    757 	for(;;){
    758 		lock(&nfs);
    759 		rp = nfs.rfirst;
    760 		np = nfs.nfirst;
    761 		if(rp == nil || np == nil){
    762 			unlock(&nfs);
    763 			break;
    764 		}
    765 		nfs.rfirst = rp->next;
    766 		nfs.nfirst = np->next;
    767 		unlock(&nfs);
    768 
    769 		rp->f.type = Rread;
    770 		rp->f.count = strlen(np->msg);
    771 		rp->f.data = np->msg;
    772 		rv = fsreply(fd, &rp->f);
    773 		free(rp);
    774 		free(np);
    775 		if(rv < 0)
    776 			return -1;
    777 	}
    778 	return 0;
    779 }
    780 
    781 void
    782 flushreq(int tag)
    783 {
    784 	Request **l, *rp;
    785 
    786 	lock(&nfs);
    787 	for(l = &nfs.rfirst; *l != nil; l = &(*l)->next){
    788 		rp = *l;
    789 		if(rp->f.tag == tag){
    790 			*l = rp->next;
    791 			unlock(&nfs);
    792 			free(rp);
    793 			return;
    794 		}
    795 	}
    796 	unlock(&nfs);
    797 }
    798 
    799 Fid*
    800 getfid(int fid)
    801 {
    802 	int i, freefid;
    803 
    804 	freefid = -1;
    805 	for(i = 0; i < Nfid; i++){
    806 		if(freefid < 0 && fids[i].file < 0)
    807 			freefid = i;
    808 		if(fids[i].fid == fid)
    809 			return &fids[i];
    810 	}
    811 	if(freefid >= 0){
    812 		fids[freefid].fid = fid;
    813 		return &fids[freefid];
    814 	}
    815 	return nil;
    816 }
    817 
    818 int
    819 fsstat(int fd, Fid *fid, Fcall *f)
    820 {
    821 	Dir d;
    822 	uchar statbuf[256];
    823 
    824 	memset(&d, 0, sizeof(d));
    825 	d.name = fstab[fid->file].name;
    826 	d.uid = user;
    827 	d.gid = user;
    828 	d.muid = user;
    829 	d.qid = fstab[fid->file].qid;
    830 	d.mode = fstab[fid->file].perm;
    831 	d.atime = d.mtime = time(0);
    832 	f->stat = statbuf;
    833 	f->nstat = convD2M(&d, statbuf, sizeof statbuf);
    834 	return fsreply(fd, f);
    835 }
    836 
    837 int
    838 fsread(int fd, Fid *fid, Fcall *f)
    839 {
    840 	Dir d;
    841 	uchar buf[256];
    842 	Request *rp;
    843 
    844 	switch(fid->file){
    845 	default:
    846 		return -1;
    847 	case Qdir:
    848 		if(f->offset == 0 && f->count >0){
    849 			memset(&d, 0, sizeof(d));
    850 			d.name = fstab[Qcpunote].name;
    851 			d.uid = user;
    852 			d.gid = user;
    853 			d.muid = user;
    854 			d.qid = fstab[Qcpunote].qid;
    855 			d.mode = fstab[Qcpunote].perm;
    856 			d.atime = d.mtime = time(0);
    857 			f->count = convD2M(&d, buf, sizeof buf);
    858 			f->data = (char*)buf;
    859 		} else
    860 			f->count = 0;
    861 		return fsreply(fd, f);
    862 	case Qcpunote:
    863 		rp = mallocz(sizeof(*rp), 1);
    864 		if(rp == nil)
    865 			return -1;
    866 		rp->f = *f;
    867 		lock(&nfs);
    868 		if(nfs.rfirst == nil)
    869 			nfs.rfirst = rp;
    870 		else
    871 			nfs.rlast->next = rp;
    872 		nfs.rlast = rp;
    873 		unlock(&nfs);
    874 		return kick(fd);;
    875 	}
    876 }
    877 
    878 char Eperm[] = "permission denied";
    879 char Enofile[] = "out of files";
    880 char Enotdir[] = "not a directory";
    881 
    882 void
    883 notefs(int fd)
    884 {
    885 	uchar buf[IOHDRSZ+Maxfdata];
    886 	int i, j, n;
    887 	char err[ERRMAX];
    888 	Fcall f;
    889 	Fid *fid, *nfid;
    890 	int doreply;
    891 
    892 	rfork(RFNOTEG);
    893 	fmtinstall('F', fcallconv);
    894 
    895 	for(n = 0; n < Nfid; n++)
    896 		fids[n].file = -1;
    897 
    898 	for(;;){
    899 		n = read9pmsg(fd, buf, sizeof(buf));
    900 		if(n <= 0){
    901 			if(dbg)
    902 				fprint(2, "read9pmsg(%d) returns %d: %r\n", fd, n);
    903 			break;
    904 		}
    905 		if(convM2S(buf, n, &f) < 0)
    906 			break;
    907 		if(dbg)
    908 			fprint(2, "->%F\n", &f);
    909 		doreply = 1;
    910 		fid = getfid(f.fid);
    911 		if(fid == nil){
    912 nofids:
    913 			f.type = Rerror;
    914 			f.ename = Enofile;
    915 			fsreply(fd, &f);
    916 			continue;
    917 		}
    918 		switch(f.type++){
    919 		default:
    920 			f.type = Rerror;
    921 			f.ename = "unknown type";
    922 			break;
    923 		case Tflush:
    924 			flushreq(f.oldtag);
    925 			break;
    926 		case Tversion:
    927 			if(f.msize > IOHDRSZ+Maxfdata)
    928 				f.msize = IOHDRSZ+Maxfdata;
    929 			break;
    930 		case Tauth:
    931 			f.type = Rerror;
    932 			f.ename = "cpu: authentication not required";
    933 			break;
    934 		case Tattach:
    935 			f.qid = fstab[Qdir].qid;
    936 			fid->file = Qdir;
    937 			break;
    938 		case Twalk:
    939 			nfid = nil;
    940 			if(f.newfid != f.fid){
    941 				nfid = getfid(f.newfid);
    942 				if(nfid == nil)
    943 					goto nofids;
    944 				nfid->file = fid->file;
    945 				fid = nfid;
    946 			}
    947 
    948 			f.ename = nil;
    949 			for(i=0; i<f.nwname; i++){
    950 				if(i > MAXWELEM){
    951 					f.type = Rerror;
    952 					f.ename = "too many name elements";
    953 					break;
    954 				}
    955 				if(fid->file != Qdir){
    956 					f.type = Rerror;
    957 					f.ename = Enotdir;
    958 					break;
    959 				}
    960 				if(strcmp(f.wname[i], "cpunote") == 0){
    961 					fid->file = Qcpunote;
    962 					f.wqid[i] = fstab[Qcpunote].qid;
    963 					continue;
    964 				}
    965 				f.type = Rerror;
    966 				f.ename = err;
    967 				strcpy(err, "cpu: file \"");
    968 				for(j=0; j<=i; j++){
    969 					if(strlen(err)+1+strlen(f.wname[j])+32 > sizeof err)
    970 						break;
    971 					if(j != 0)
    972 						strcat(err, "/");
    973 					strcat(err, f.wname[j]);
    974 				}
    975 				strcat(err, "\" does not exist");
    976 				break;
    977 			}
    978 			if(nfid != nil && (f.ename != nil || i < f.nwname))
    979 				nfid ->file = -1;
    980 			if(f.type != Rerror)
    981 				f.nwqid = i;
    982 			break;
    983 		case Topen:
    984 			if(f.mode != OREAD){
    985 				f.type = Rerror;
    986 				f.ename = Eperm;
    987 			}
    988 			f.qid = fstab[fid->file].qid;
    989 			break;
    990 		case Tcreate:
    991 			f.type = Rerror;
    992 			f.ename = Eperm;
    993 			break;
    994 		case Tread:
    995 			if(fsread(fd, fid, &f) < 0)
    996 				goto err;
    997 			doreply = 0;
    998 			break;
    999 		case Twrite:
   1000 			f.type = Rerror;
   1001 			f.ename = Eperm;
   1002 			break;
   1003 		case Tclunk:
   1004 			fid->file = -1;
   1005 			break;
   1006 		case Tremove:
   1007 			f.type = Rerror;
   1008 			f.ename = Eperm;
   1009 			break;
   1010 		case Tstat:
   1011 			if(fsstat(fd, fid, &f) < 0)
   1012 				goto err;
   1013 			doreply = 0;
   1014 			break;
   1015 		case Twstat:
   1016 			f.type = Rerror;
   1017 			f.ename = Eperm;
   1018 			break;
   1019 		}
   1020 		if(doreply)
   1021 			if(fsreply(fd, &f) < 0)
   1022 				break;
   1023 	}
   1024 err:
   1025 	if(dbg)
   1026 		fprint(2, "notefs exiting: %r\n");
   1027 	close(fd);
   1028 }
   1029 
   1030 char 	notebuf[ERRMAX];
   1031 
   1032 void
   1033 catcher(void*, char *text)
   1034 {
   1035 	int n;
   1036 
   1037 	n = strlen(text);
   1038 	if(n >= sizeof(notebuf))
   1039 		n = sizeof(notebuf)-1;
   1040 	memmove(notebuf, text, n);
   1041 	notebuf[n] = '\0';
   1042 	noted(NCONT);
   1043 }
   1044 
   1045 /*
   1046  *  mount in /dev a note file for the remote side to read.
   1047  */
   1048 void
   1049 lclnoteproc(int netfd)
   1050 {
   1051 	int exportfspid;
   1052 	Waitmsg *w;
   1053 	Note *np;
   1054 	int pfd[2];
   1055 
   1056 	if(pipe(pfd) < 0){
   1057 		fprint(2, "cpu: can't start note proc: pipe: %r\n");
   1058 		return;
   1059 	}
   1060 
   1061 	/* new proc mounts and returns to start exportfs */
   1062 	switch(exportfspid = rfork(RFPROC|RFNAMEG|RFFDG|RFMEM)){
   1063 	case -1:
   1064 		fprint(2, "cpu: can't start note proc: rfork: %r\n");
   1065 		return;
   1066 	case 0:
   1067 		close(pfd[0]);
   1068 		if(mount(pfd[1], -1, "/dev", MBEFORE, "") < 0)
   1069 			fprint(2, "cpu: can't mount note proc: %r\n");
   1070 		close(pfd[1]);
   1071 		return;
   1072 	}
   1073 
   1074 	close(netfd);
   1075 	close(pfd[1]);
   1076 
   1077 	/* new proc listens for note file system rpc's */
   1078 	switch(rfork(RFPROC|RFNAMEG|RFMEM)){
   1079 	case -1:
   1080 		fprint(2, "cpu: can't start note proc: rfork1: %r\n");
   1081 		_exits(0);
   1082 	case 0:
   1083 		notefs(pfd[0]);
   1084 		_exits(0);
   1085 	}
   1086 
   1087 	/* original proc waits for notes */
   1088 	notify(catcher);
   1089 	w = nil;
   1090 	for(;;) {
   1091 		*notebuf = 0;
   1092 		free(w);
   1093 		w = wait();
   1094 		if(w == nil) {
   1095 			if(*notebuf == 0)
   1096 				break;
   1097 			np = mallocz(sizeof(Note), 1);
   1098 			if(np != nil){
   1099 				strcpy(np->msg, notebuf);
   1100 				lock(&nfs);
   1101 				if(nfs.nfirst == nil)
   1102 					nfs.nfirst = np;
   1103 				else
   1104 					nfs.nlast->next = np;
   1105 				nfs.nlast = np;
   1106 				unlock(&nfs);
   1107 				kick(pfd[0]);
   1108 			}
   1109 			unlock(&nfs);
   1110 		} else if(w->pid == exportfspid)
   1111 			break;
   1112 	}
   1113 
   1114 	if(w == nil)
   1115 		exits(nil);
   1116 	exits(w->msg);
   1117 }