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