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