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