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