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