]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cpu.c
9bootfat: rename open() to fileinit and make it static as its really a internal funct...
[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(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
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(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
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
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
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*
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
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
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
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
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
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
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
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
624 noauth(int fd)
625 {
626         ealgs = nil;
627         return fd;
628 }
629
630 static int
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
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
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
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
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
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
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
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
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*
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
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
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
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
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
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 }