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