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