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