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