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 void remoteside(void); 16 void fatal(int, char*, ...); 17 void lclnoteproc(int); 18 void rmtnoteproc(void); 19 void catcher(void*, char*); 20 void usage(void); 21 void writestr(int, char*, char*, int); 22 int readstr(int, char*, int); 23 char *rexcall(int*, char*, char*); 24 int filter(int); 25 int setamalg(char*); 26 27 int notechan; 28 char system[32]; 29 int cflag; 30 int hflag; 31 int fflag; 32 int dbg; 33 34 char *srvname = "cpu"; 35 char *exportfs = "/bin/exportfs"; 36 char *ealgs = "rc4_256 sha1"; 37 38 /* authentication mechanisms */ 39 static int netkeyauth(int); 40 static int netkeysrvauth(int, char*); 41 static int p9auth(int); 42 static int srvp9auth(int, char*); 43 44 typedef struct AuthMethod AuthMethod; 45 struct AuthMethod { 46 char *name; /* name of method */ 47 int (*cf)(int); /* client side authentication */ 48 int (*sf)(int, char*); /* server side authentication */ 49 } authmethod[] = 50 { 51 { "p9", p9auth, srvp9auth}, 52 { "netkey", netkeyauth, netkeysrvauth}, 53 { nil, nil} 54 }; 55 AuthMethod *am = authmethod; /* default is p9 */ 56 57 int setam(char*); 58 59 void 60 usage(void) 61 { 62 fprint(2, "usage: cpu [-h system] [-a authmethod] [-e 'crypt hash'] [-c cmd args ...]\n"); 63 exits("usage"); 64 } 65 int fdd; 66 67 void 68 main(int argc, char **argv) 69 { 70 char dat[128], buf[128], cmd[128], *p, *err; 71 int data; 72 73 ARGBEGIN{ 74 case 'a': 75 p = ARGF(); 76 if(p==0) 77 usage(); 78 if(setam(p) < 0) 79 fatal(0, "unknown auth method %s\n", p); 80 break; 81 case 'e': 82 ealgs = ARGF(); 83 if(ealgs == nil) 84 usage(); 85 if(*ealgs == 0 || strcmp(ealgs, "clear") == 0) 86 ealgs = nil; 87 break; 88 case 'd': 89 dbg++; 90 break; 91 case 'f': 92 fflag++; 93 break; 94 case 'R': /* From listen */ 95 remoteside(); 96 break; 97 case 'h': 98 hflag++; 99 p = ARGF(); 100 if(p==0) 101 usage(); 102 strcpy(system, p); 103 break; 104 case 'c': 105 cflag++; 106 cmd[0] = '!'; 107 cmd[1] = '\0'; 108 while(p = ARGF()) { 109 strcat(cmd, " "); 110 strcat(cmd, p); 111 } 112 break; 113 default: 114 usage(); 115 }ARGEND; 116 117 if(argv); 118 if(argc != 0) 119 usage(); 120 121 if(hflag == 0) { 122 p = getenv("cpu"); 123 if(p == 0) 124 fatal(0, "set $cpu"); 125 strcpy(system, p); 126 } 127 128 if(err = rexcall(&data, system, srvname)) 129 fatal(1, "%s: %s", err, system); 130 131 /* Tell the remote side the command to execute and where our working directory is */ 132 if(cflag) 133 writestr(data, cmd, "command", 0); 134 if(getwd(dat, sizeof(dat)) == 0) 135 writestr(data, "NO", "dir", 0); 136 else 137 writestr(data, dat, "dir", 0); 138 139 /* start up a process to pass along notes */ 140 lclnoteproc(data); 141 142 /* 143 * Wait for the other end to execute and start our file service 144 * of /mnt/term 145 */ 146 if(readstr(data, buf, sizeof(buf)) < 0) 147 fatal(1, "waiting for FS"); 148 if(strncmp("FS", buf, 2) != 0) { 149 print("remote cpu: %s", buf); 150 exits(buf); 151 } 152 153 if(fflag) 154 data = filter(data); 155 156 /* Begin serving the gnot namespace */ 157 close(0); 158 dup(data, 0); 159 close(data); 160 if(dbg) 161 execl(exportfs, exportfs, "-d", 0); 162 else 163 execl(exportfs, exportfs, 0); 164 fatal(1, "starting exportfs"); 165 } 166 167 void 168 fatal(int syserr, char *fmt, ...) 169 { 170 char buf[ERRLEN]; 171 va_list arg; 172 173 va_start(arg, fmt); 174 doprint(buf, buf+sizeof(buf), fmt, arg); 175 va_end(arg); 176 if(syserr) 177 fprint(2, "cpu: %s: %r\n", buf); 178 else 179 fprint(2, "cpu: %s\n", buf); 180 exits(buf); 181 } 182 183 char *negstr = "negotiating authentication method"; 184 185 /* Invoked with stdin, stdout and stderr connected to the network connection */ 186 void 187 remoteside(void) 188 { 189 char user[NAMELEN], home[128], buf[128], xdir[128], cmd[128]; 190 int i, n, fd, badchdir, gotcmd; 191 192 fd = 0; 193 194 /* negotiate authentication mechanism */ 195 n = readstr(fd, cmd, sizeof(cmd)); 196 if(n < 0) 197 fatal(1, "authenticating"); 198 if(setamalg(cmd) < 0){ 199 writestr(fd, "unsupported auth method", nil, 0); 200 fatal(1, "bad auth method %s", cmd); 201 } else 202 writestr(fd, "", "", 1); 203 204 fd = (*am->sf)(fd, user); 205 if(fd < 0) 206 fatal(1, "srvauth"); 207 if(newns(user, 0) < 0) 208 fatal(1, "newns"); 209 210 /* Set environment values for the user */ 211 putenv("user", user); 212 sprint(home, "/usr/%s", user); 213 putenv("home", home); 214 215 /* Now collect invoking cpus current directory or possibly a command */ 216 gotcmd = 0; 217 if(readstr(fd, xdir, sizeof(xdir)) < 0) 218 fatal(1, "dir/cmd"); 219 if(xdir[0] == '!') { 220 strcpy(cmd, &xdir[1]); 221 gotcmd = 1; 222 if(readstr(fd, xdir, sizeof(xdir)) < 0) 223 fatal(1, "dir"); 224 } 225 226 /* Establish the new process at the current working directory of the 227 * gnot */ 228 badchdir = 0; 229 if(strcmp(xdir, "NO") == 0) 230 chdir(home); 231 else if(chdir(xdir) < 0) { 232 badchdir = 1; 233 chdir(home); 234 } 235 236 /* Start the gnot serving its namespace */ 237 writestr(fd, "FS", "FS", 0); 238 writestr(fd, "/", "exportfs dir", 0); 239 240 n = read(fd, buf, sizeof(buf)); 241 if(n != 2 || buf[0] != 'O' || buf[1] != 'K') 242 exits("remote tree"); 243 244 fd = dup(fd, -1); 245 246 /* push fcall and note proc */ 247 if(fflag) 248 fd = filter(fd); 249 if(amount(fd, "/mnt/term", MCREATE|MREPL, "") < 0) 250 exits("mount failed"); 251 close(fd); 252 253 /* the remote noteproc uses the mount so it must follow it */ 254 rmtnoteproc(); 255 256 for(i = 0; i < 3; i++) 257 close(i); 258 259 if(open("/mnt/term/dev/cons", OREAD) != 0){ 260 exits("open stdin"); 261 } 262 if(open("/mnt/term/dev/cons", OWRITE) != 1){ 263 exits("open stdout"); 264 } 265 dup(1, 2); 266 267 if(badchdir) 268 print("cpu: failed to chdir to '%s'\n", xdir); 269 270 if(gotcmd) 271 execl("/bin/rc", "rc", "-lc", cmd, 0); 272 else 273 execl("/bin/rc", "rc", "-li", 0); 274 fatal(1, "exec shell"); 275 } 276 277 char* 278 rexcall(int *fd, char *host, char *service) 279 { 280 char *na; 281 char dir[4*NAMELEN]; 282 char err[ERRLEN]; 283 char msg[128]; 284 int n; 285 286 na = netmkaddr(host, 0, service); 287 if((*fd = dial(na, 0, dir, 0)) < 0) 288 return "can't dial"; 289 290 /* negotiate authentication mechanism */ 291 if(ealgs != nil) 292 snprint(msg, sizeof(msg), "%s %s", am->name, ealgs); 293 else 294 snprint(msg, sizeof(msg), "%s", am->name); 295 writestr(*fd, msg, negstr, 0); 296 n = readstr(*fd, err, ERRLEN); 297 if(n < 0) 298 return negstr; 299 if(*err){ 300 werrstr(err); 301 return negstr; 302 } 303 304 /* authenticate */ 305 *fd = (*am->cf)(*fd); 306 if(*fd < 0) 307 return "can't authenticate"; 308 if(strstr(dir, "tcp")) 309 fflag = 1; 310 return 0; 311 } 312 313 void 314 writestr(int fd, char *str, char *thing, int ignore) 315 { 316 int l, n; 317 318 l = strlen(str); 319 n = write(fd, str, l+1); 320 if(!ignore && n < 0) 321 fatal(1, "writing network: %s", thing); 322 } 323 324 int 325 readstr(int fd, char *str, int len) 326 { 327 int n; 328 329 while(len) { 330 n = read(fd, str, 1); 331 if(n < 0) 332 return -1; 333 if(*str == '\0') 334 return 0; 335 str++; 336 len--; 337 } 338 return -1; 339 } 340 341 static int 342 readln(char *buf, int n) 343 { 344 char *p = buf; 345 346 n--; 347 while(n > 0){ 348 if(read(0, p, 1) != 1) 349 break; 350 if(*p == '\n' || *p == '\r'){ 351 *p = 0; 352 return p-buf; 353 } 354 p++; 355 } 356 *p = 0; 357 return p-buf; 358 } 359 360 /* 361 * user level challenge/response 362 */ 363 static int 364 netkeyauth(int fd) 365 { 366 char chall[NAMELEN]; 367 char resp[NAMELEN]; 368 369 strcpy(chall, getuser()); 370 print("user[%s]: ", chall); 371 if(readln(resp, sizeof(resp)) < 0) 372 return -1; 373 if(*resp != 0) 374 strcpy(chall, resp); 375 writestr(fd, chall, "challenge/response", 1); 376 377 for(;;){ 378 if(readstr(fd, chall, sizeof chall) < 0) 379 break; 380 if(*chall == 0) 381 return fd; 382 print("challenge: %s\nresponse: ", chall); 383 if(readln(resp, sizeof(resp)) < 0) 384 break; 385 writestr(fd, resp, "challenge/response", 1); 386 } 387 return -1; 388 } 389 390 static int 391 netkeysrvauth(int fd, char *user) 392 { 393 char response[NAMELEN]; 394 Chalstate ch; 395 int tries; 396 397 if(readstr(fd, user, NAMELEN) < 0) 398 return -1; 399 400 for(tries = 0; tries < 10; tries++){ 401 if(getchal(&ch, user) < 0) 402 return -1; 403 writestr(fd, ch.chal, "challenge", 1); 404 if(readstr(fd, response, sizeof response) < 0) 405 return -1; 406 if(chalreply(&ch, response) >= 0) 407 break; 408 } 409 if(tries >= 10) 410 return -1; 411 writestr(fd, "", "challenge", 1); 412 return fd; 413 } 414 415 static void 416 mksecret(char *t, uchar *f) 417 { 418 sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", 419 f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]); 420 } 421 422 /* 423 * plan9 authentication followed by rc4 encryption 424 */ 425 static int 426 p9auth(int fd) 427 { 428 uchar key[16]; 429 uchar digest[SHA1dlen]; 430 char fromclientsecret[21]; 431 char fromserversecret[21]; 432 int i; 433 434 if(authnonce(fd, key+4) < 0) 435 return -1; 436 if(ealgs == nil) 437 return fd; 438 439 /* exchange random numbers */ 440 srand(truerand()); 441 for(i = 0; i < 4; i++) 442 key[i] = rand(); 443 if(write(fd, key, 4) != 4) 444 return -1; 445 if(readn(fd, key+12, 4) != 4) 446 return -1; 447 448 /* scramble into two secrets */ 449 sha1(key, sizeof(key), digest, nil); 450 mksecret(fromclientsecret, digest); 451 mksecret(fromserversecret, digest+10); 452 453 /* set up encryption */ 454 return pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil); 455 } 456 457 static int 458 srvp9auth(int fd, char *user) 459 { 460 uchar key[16]; 461 uchar digest[SHA1dlen]; 462 char fromclientsecret[21]; 463 char fromserversecret[21]; 464 int i; 465 466 if(srvauthnonce(fd, user, key+4) < 0) 467 return -1; 468 469 if(ealgs == nil) 470 return fd; 471 472 /* exchange random numbers */ 473 srand(truerand()); 474 for(i = 0; i < 4; i++) 475 key[i+12] = rand(); 476 if(readn(fd, key, 4) != 4) 477 return -1; 478 if(write(fd, key+12, 4) != 4) 479 return -1; 480 481 /* scramble into two secrets */ 482 sha1(key, sizeof(key), digest, nil); 483 mksecret(fromclientsecret, digest); 484 mksecret(fromserversecret, digest+10); 485 486 /* set up encryption */ 487 return pushssl(fd, ealgs, fromserversecret, fromclientsecret, nil); 488 } 489 490 /* Network on fd1, mount driver on fd0 */ 491 int 492 filter(int fd) 493 { 494 int p[2]; 495 496 if(pipe(p) < 0) 497 fatal(1, "pipe"); 498 499 switch(rfork(RFNOWAIT|RFPROC|RFFDG)) { 500 case -1: 501 fatal(1, "rfork record module"); 502 case 0: 503 dup(fd, 1); 504 close(fd); 505 dup(p[0], 0); 506 close(p[0]); 507 close(p[1]); 508 execl("/bin/aux/fcall", "fcall", 0); 509 fatal(1, "exec record module"); 510 default: 511 close(fd); 512 close(p[0]); 513 } 514 return p[1]; 515 } 516 517 /* 518 * set authentication mechanism 519 */ 520 int 521 setam(char *name) 522 { 523 for(am = authmethod; am->name != nil; am++) 524 if(strcmp(am->name, name) == 0) 525 return 0; 526 am = authmethod; 527 return -1; 528 } 529 530 /* 531 * set authentication mechanism and encryption/hash algs 532 */ 533 int 534 setamalg(char *s) 535 { 536 ealgs = strchr(s, ' '); 537 if(ealgs != nil) 538 *ealgs++ = 0; 539 return setam(s); 540 } 541 542 char *rmtnotefile = "/mnt/term/dev/cpunote"; 543 544 /* 545 * loop reading /mnt/term/dev/note looking for notes. 546 * The child returns to start the shell. 547 */ 548 void 549 rmtnoteproc(void) 550 { 551 int n, fd, pid, notepid; 552 char buf[256]; 553 Waitmsg w; 554 555 /* new proc returns to start shell */ 556 pid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFMEM); 557 switch(pid){ 558 case -1: 559 syslog(0, "cpu", "cpu -R: can't start noteproc: %r"); 560 return; 561 case 0: 562 return; 563 } 564 565 /* new proc reads notes from other side and posts them to shell */ 566 switch(notepid = rfork(RFPROC|RFFDG|RFMEM)){ 567 case -1: 568 syslog(0, "cpu", "cpu -R: can't start wait proc: %r"); 569 _exits(0); 570 case 0: 571 fd = open(rmtnotefile, OREAD); 572 if(fd < 0){ 573 syslog(0, "cpu", "cpu -R: can't open %s", rmtnotefile); 574 _exits(0); 575 } 576 577 for(;;){ 578 n = read(fd, buf, sizeof(buf)-1); 579 if(n <= 0){ 580 postnote(PNGROUP, pid, "hangup"); 581 _exits(0); 582 } 583 buf[n] = 0; 584 postnote(PNGROUP, pid, buf); 585 } 586 break; 587 } 588 589 /* original proc waits for shell proc to die and kills note proc */ 590 for(;;){ 591 n = wait(&w); 592 if(n < 0 || n == pid) 593 break; 594 } 595 postnote(PNPROC, notepid, "kill"); 596 _exits(0); 597 } 598 599 enum 600 { 601 Qdir, 602 Qcpunote, 603 604 Nfid = 32, 605 }; 606 607 struct { 608 char *name; 609 Qid qid; 610 ulong perm; 611 } fstab[] = 612 { 613 [Qdir] { ".", {CHDIR|Qdir, 0}, CHDIR|0555 }, 614 [Qcpunote] { "cpunote", {Qcpunote, 0}, 0444 }, 615 }; 616 617 typedef struct Note Note; 618 struct Note 619 { 620 Note *next; 621 char msg[ERRLEN]; 622 }; 623 624 typedef struct Request Request; 625 struct Request 626 { 627 Request *next; 628 Fcall f; 629 }; 630 631 typedef struct Fid Fid; 632 struct Fid 633 { 634 int fid; 635 int file; 636 }; 637 Fid fids[Nfid]; 638 639 struct { 640 Lock; 641 Note *nfirst, *nlast; 642 Request *rfirst, *rlast; 643 } nfs; 644 645 int 646 fsreply(int fd, Fcall *f) 647 { 648 char buf[ERRLEN+MAXMSG]; 649 int n; 650 651 if(dbg) 652 fprint(2, "<-%F\n", f); 653 n = convS2M(f, buf); 654 if(n > 0){ 655 if(write(fd, buf, n) != n){ 656 close(fd); 657 return -1; 658 } 659 } 660 return 0; 661 } 662 663 /* match a note read request with a note, reply to the request */ 664 int 665 kick(int fd) 666 { 667 Request *rp; 668 Note *np; 669 int rv; 670 671 for(;;){ 672 lock(&nfs); 673 rp = nfs.rfirst; 674 np = nfs.nfirst; 675 if(rp == nil || np == nil){ 676 unlock(&nfs); 677 break; 678 } 679 nfs.rfirst = rp->next; 680 nfs.nfirst = np->next; 681 unlock(&nfs); 682 683 rp->f.type = Rread; 684 rp->f.count = strlen(np->msg); 685 rp->f.data = np->msg; 686 rv = fsreply(fd, &rp->f); 687 free(rp); 688 free(np); 689 if(rv < 0) 690 return -1; 691 } 692 return 0; 693 } 694 695 void 696 flushreq(int tag) 697 { 698 Request **l, *rp; 699 700 lock(&nfs); 701 for(l = &nfs.rfirst; *l != nil; l = &(*l)->next){ 702 rp = *l; 703 if(rp->f.tag == tag){ 704 *l = rp->next; 705 unlock(&nfs); 706 free(rp); 707 return; 708 } 709 } 710 unlock(&nfs); 711 } 712 713 Fid* 714 getfid(int fid) 715 { 716 int i, freefid; 717 718 freefid = -1; 719 for(i = 0; i < Nfid; i++){ 720 if(freefid < 0 && fids[i].file < 0) 721 freefid = i; 722 if(fids[i].fid == fid) 723 return &fids[i]; 724 } 725 if(freefid >= 0){ 726 fids[freefid].fid = fid; 727 return &fids[freefid]; 728 } 729 return nil; 730 } 731 732 int 733 fsstat(int fd, Fid *fid, Fcall *f) 734 { 735 Dir d; 736 737 memset(&d, 0, sizeof(d)); 738 strcpy(d.name, fstab[fid->file].name); 739 d.qid = fstab[fid->file].qid; 740 d.mode = fstab[fid->file].perm; 741 d.atime = d.mtime = time(0); 742 convD2M(&d, f->stat); 743 return fsreply(fd, f); 744 } 745 746 int 747 fsread(int fd, Fid *fid, Fcall *f) 748 { 749 Dir d; 750 char buf[DIRLEN]; 751 Request *rp; 752 753 switch(fid->file){ 754 default: 755 return -1; 756 case Qdir: 757 if(f->offset == 0 && f->count == DIRLEN){ 758 memset(&d, 0, sizeof(d)); 759 d.qid = fstab[Qcpunote].qid; 760 d.mode = fstab[Qcpunote].perm; 761 d.atime = d.mtime = time(0); 762 convD2M(&d, buf); 763 f->data = buf; 764 f->count = 0; 765 } else 766 f->count = 0; 767 return fsreply(fd, f); 768 case Qcpunote: 769 rp = mallocz(sizeof(*rp), 1); 770 if(rp == nil) 771 return -1; 772 rp->f = *f; 773 lock(&nfs); 774 if(nfs.rfirst == nil) 775 nfs.rfirst = rp; 776 else 777 nfs.rlast->next = rp; 778 nfs.rlast = rp; 779 unlock(&nfs); 780 return kick(fd);; 781 } 782 } 783 784 char *Eperm = "permission denied"; 785 char *Enofile = "out of files"; 786 char *Enotexist = "file does not exist"; 787 char *Enotdir = "not a directory"; 788 789 void 790 notefs(int fd) 791 { 792 char buf[MAXFDATA+MAXMSG]; 793 int n; 794 Fcall f; 795 Fid *fid, *nfid; 796 int doreply; 797 798 rfork(RFNOTEG); 799 fmtinstall('F', fcallconv); 800 801 for(n = 0; n < Nfid; n++) 802 fids[n].file = -1; 803 804 for(;;){ 805 n = read9p(fd, buf, sizeof(buf)); 806 if(n <= 0) 807 break; 808 if(convM2S(buf, &f, n) < 0) 809 break; 810 if(dbg) 811 fprint(2, "->%F\n", &f); 812 doreply = 1; 813 fid = getfid(f.fid); 814 if(fid == nil){ 815 nofids: 816 f.type = Rerror; 817 strcpy(f.ename, Enofile); 818 fsreply(fd, &f); 819 continue; 820 } 821 switch(f.type++){ 822 default: 823 f.type = Rerror; 824 strcpy(f.ename, "unknown type"); 825 break; 826 case Tflush: 827 flushreq(f.oldtag); 828 break; 829 case Tsession: 830 memset(f.authid, 0, sizeof(f.authid)); 831 memset(f.authdom, 0, sizeof(f.authdom)); 832 memset(f.chal, 0, sizeof(f.chal)); 833 break; 834 case Tnop: 835 break; 836 case Tattach: 837 f.qid = fstab[Qdir].qid; 838 fid->file = Qdir; 839 break; 840 case Tclone: 841 nfid = getfid(f.newfid); 842 if(nfid == nil) 843 goto nofids; 844 nfid->file = fid->file; 845 break; 846 case Tclwalk: 847 nfid = getfid(f.newfid); 848 if(nfid == nil) 849 goto nofids; 850 nfid->file = fid->file; 851 fid = nfid; 852 /* fall through */ 853 case Twalk: 854 if(fid->file != Qdir){ 855 f.type = Rerror; 856 strcpy(f.ename, Enotdir); 857 } else if(strcmp(f.name, "cpunote") == 0){ 858 fid->file = Qcpunote; 859 f.qid = fstab[Qcpunote].qid; 860 } else { 861 f.type = Rerror; 862 strcpy(f.ename, Enotexist); 863 } 864 break; 865 case Topen: 866 if(f.mode != OREAD){ 867 f.type = Rerror; 868 strcpy(f.ename, Eperm); 869 } 870 break; 871 case Tcreate: 872 f.type = Rerror; 873 strcpy(f.ename, Eperm); 874 break; 875 case Tread: 876 if(fsread(fd, fid, &f) < 0) 877 goto err; 878 doreply = 0; 879 break; 880 case Twrite: 881 f.type = Rerror; 882 strcpy(f.ename, Eperm); 883 break; 884 case Tclunk: 885 fid->file = -1; 886 break; 887 case Tremove: 888 f.type = Rerror; 889 strcpy(f.ename, Eperm); 890 break; 891 case Tstat: 892 if(fsstat(fd, fid, &f) < 0) 893 goto err; 894 doreply = 0; 895 break; 896 case Twstat: 897 f.type = Rerror; 898 strcpy(f.ename, Eperm); 899 break; 900 } 901 if(doreply) 902 if(fsreply(fd, &f) < 0) 903 break; 904 } 905 err: 906 close(fd); 907 } 908 909 char notebuf[ERRLEN]; 910 911 void 912 catcher(void*, char *text) 913 { 914 strncpy(notebuf, text, ERRLEN); 915 notebuf[ERRLEN-1] = 0; 916 noted(NCONT); 917 } 918 919 /* 920 * mount in /dev a note file for the remote side to read. 921 */ 922 void 923 lclnoteproc(int netfd) 924 { 925 int exportfspid, n; 926 Waitmsg w; 927 Note *np; 928 int pfd[2]; 929 930 if(pipe(pfd) < 0){ 931 fprint(2, "cpu: can't start note proc: %r\n"); 932 return; 933 } 934 935 /* new proc mounts and returns to start exportfs */ 936 switch(exportfspid = rfork(RFPROC|RFNAMEG|RFFDG|RFMEM)){ 937 case -1: 938 fprint(2, "cpu: can't start note proc: %r\n"); 939 return; 940 case 0: 941 close(pfd[0]); 942 amount(pfd[1], "/dev", MBEFORE, ""); 943 close(pfd[1]); 944 return; 945 } 946 947 close(netfd); 948 close(pfd[1]); 949 950 /* new proc listens for note file system rpc's */ 951 switch(rfork(RFPROC|RFNAMEG|RFMEM)){ 952 case -1: 953 fprint(2, "cpu: can't start note proc: %r\n"); 954 _exits(0); 955 case 0: 956 notefs(pfd[0]); 957 _exits(0); 958 } 959 960 /* original proc waits for notes */ 961 notify(catcher); 962 for(;;) { 963 *notebuf = 0; 964 n = wait(&w); 965 if(n < 0) { 966 if(*notebuf == 0) 967 break; 968 np = mallocz(sizeof(Note), 1); 969 if(np != nil){ 970 strcpy(np->msg, notebuf); 971 lock(&nfs); 972 if(nfs.nfirst == nil) 973 nfs.nfirst = np; 974 else 975 nfs.nlast->next = np; 976 nfs.nlast = np; 977 unlock(&nfs); 978 kick(pfd[0]); 979 } 980 unlock(&nfs); 981 } else if(n == exportfspid) 982 break; 983 } 984 985 exits(w.msg); 986 } 987