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 } 1211