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