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