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