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