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