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