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