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