1 /* 2 * exportfs - Export a plan 9 name space across a network 3 */ 4 #include <u.h> 5 #include <libc.h> 6 #include <auth.h> 7 #include <fcall.h> 8 #include <libsec.h> 9 #define Extern 10 #include "exportfs.h" 11 12 #define QIDPATH ((1LL<<48)-1) 13 vlong newqid = 0; 14 15 enum { 16 Encnone, 17 Encssl, 18 Enctls, 19 }; 20 21 void (*fcalls[])(Fsrpc*) = 22 { 23 [Tversion] Xversion, 24 [Tauth] Xauth, 25 [Tflush] Xflush, 26 [Tattach] Xattach, 27 [Twalk] Xwalk, 28 [Topen] slave, 29 [Tcreate] Xcreate, 30 [Tclunk] Xclunk, 31 [Tread] slave, 32 [Twrite] slave, 33 [Tremove] Xremove, 34 [Tstat] Xstat, 35 [Twstat] Xwstat, 36 }; 37 38 /* accounting and debugging counters */ 39 int filecnt; 40 int freecnt; 41 int qidcnt; 42 int qfreecnt; 43 int ncollision; 44 45 int netfd; /* initially stdin */ 46 int srvfd = -1; 47 int nonone = 1; 48 char *filterp; 49 char *ealgs = "rc4_256 sha1"; 50 char *aanfilter = "/bin/aan"; 51 int encproto = Encnone; 52 int readonly; 53 54 static void mksecret(char *, uchar *); 55 static int localread9pmsg(int, void *, uint, ulong *); 56 static char *anstring = "tcp!*!0"; 57 58 char *netdir = "", *local = "", *remote = ""; 59 60 int filter(int, char *); 61 62 void 63 usage(void) 64 { 65 fprint(2, "usage: %s [-adnsR] [-f dbgfile] [-m msize] [-r root] " 66 "[-S srvfile] [-e 'crypt hash'] [-P exclusion-file] " 67 "[-A announce-string] [-B address]\n", argv0); 68 fatal("usage"); 69 } 70 71 static void 72 noteconn(int fd) 73 { 74 NetConnInfo *nci; 75 76 nci = getnetconninfo(nil, fd); 77 if (nci == nil) 78 return; 79 netdir = strdup(nci->dir); 80 local = strdup(nci->lsys); 81 remote = strdup(nci->rsys); 82 freenetconninfo(nci); 83 } 84 85 void 86 main(int argc, char **argv) 87 { 88 char buf[ERRMAX], ebuf[ERRMAX], *srvfdfile; 89 Fsrpc *r; 90 int doauth, n, fd; 91 char *dbfile, *srv, *na, *nsfile, *keyspec; 92 AuthInfo *ai; 93 ulong initial; 94 95 dbfile = "/tmp/exportdb"; 96 srv = nil; 97 srvfd = -1; 98 srvfdfile = nil; 99 na = nil; 100 nsfile = nil; 101 keyspec = ""; 102 doauth = 0; 103 104 ai = nil; 105 ARGBEGIN{ 106 case 'a': 107 doauth = 1; 108 break; 109 110 case 'd': 111 dbg++; 112 break; 113 114 case 'e': 115 ealgs = EARGF(usage()); 116 if(*ealgs == 0 || strcmp(ealgs, "clear") == 0) 117 ealgs = nil; 118 break; 119 120 case 'f': 121 dbfile = EARGF(usage()); 122 break; 123 124 case 'k': 125 keyspec = EARGF(usage()); 126 break; 127 128 case 'm': 129 messagesize = strtoul(EARGF(usage()), nil, 0); 130 break; 131 132 case 'n': 133 nonone = 0; 134 break; 135 136 case 'r': 137 srv = EARGF(usage()); 138 break; 139 140 case 's': 141 srv = "/"; 142 break; 143 144 case 'A': 145 anstring = EARGF(usage()); 146 break; 147 148 case 'B': 149 na = EARGF(usage()); 150 break; 151 152 case 'F': 153 /* accepted but ignored, for backwards compatibility */ 154 break; 155 156 case 'N': 157 nsfile = EARGF(usage()); 158 break; 159 160 case 'P': 161 patternfile = EARGF(usage()); 162 break; 163 164 case 'R': 165 readonly = 1; 166 break; 167 168 case 'S': 169 if(srvfdfile) 170 usage(); 171 srvfdfile = EARGF(usage()); 172 break; 173 174 default: 175 usage(); 176 }ARGEND 177 USED(argc, argv); 178 179 if(doauth){ 180 /* 181 * We use p9any so we don't have to visit this code again, with the 182 * cost that this code is incompatible with the old world, which 183 * requires p9sk2. (The two differ in who talks first, so compatibility 184 * is awkward.) 185 */ 186 ai = auth_proxy(0, auth_getkey, "proto=p9any role=server %s", keyspec); 187 if(ai == nil) 188 fatal("auth_proxy: %r"); 189 if(nonone && strcmp(ai->cuid, "none") == 0) 190 fatal("exportfs by none disallowed"); 191 if(auth_chuid(ai, nsfile) < 0) 192 fatal("auth_chuid: %r"); 193 putenv("service", "exportfs"); 194 } 195 196 if(srvfdfile){ 197 if((srvfd = open(srvfdfile, ORDWR)) < 0) 198 sysfatal("open '%s': %r", srvfdfile); 199 } 200 201 if(na){ 202 if(srv == nil) 203 sysfatal("-B requires -s"); 204 205 local = "me"; 206 remote = na; 207 if((fd = dial(netmkaddr(na, 0, "importfs"), 0, 0, 0)) < 0) 208 sysfatal("can't dial %s: %r", na); 209 210 ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client %s", keyspec); 211 if(ai == nil) 212 sysfatal("%r: %s", na); 213 214 dup(fd, 0); 215 dup(fd, 1); 216 close(fd); 217 } 218 219 exclusions(); 220 221 if(dbg) { 222 n = create(dbfile, OWRITE|OTRUNC, 0666); 223 dup(n, DFD); 224 close(n); 225 } 226 227 if(srvfd >= 0 && srv){ 228 fprint(2, "exportfs: -S cannot be used with -r or -s\n"); 229 usage(); 230 } 231 232 DEBUG(DFD, "exportfs: started\n"); 233 234 rfork(RFNOTEG); 235 236 if(messagesize == 0){ 237 messagesize = iounit(netfd); 238 if(messagesize == 0) 239 messagesize = 8192+IOHDRSZ; 240 } 241 242 Workq = emallocz(sizeof(Fsrpc)*Nr_workbufs); 243 // for(i=0; i<Nr_workbufs; i++) 244 // Workq[i].buf = emallocz(messagesize); 245 fhash = emallocz(sizeof(Fid*)*FHASHSIZE); 246 247 fmtinstall('F', fcallfmt); 248 249 /* 250 * Get tree to serve from network connection, 251 * check we can get there and ack the connection 252 */ 253 if(srvfd != -1) { 254 /* do nothing */ 255 } 256 else if(srv) { 257 if(chdir(srv) < 0) { 258 errstr(ebuf, sizeof ebuf); 259 fprint(0, "chdir(\"%s\"): %s\n", srv, ebuf); 260 DEBUG(DFD, "chdir(\"%s\"): %s\n", srv, ebuf); 261 exits(ebuf); 262 } 263 DEBUG(DFD, "invoked as server for %s", srv); 264 strncpy(buf, srv, sizeof buf); 265 } 266 else { 267 noteconn(netfd); 268 buf[0] = 0; 269 n = read(0, buf, sizeof(buf)-1); 270 if(n < 0) { 271 errstr(buf, sizeof buf); 272 fprint(0, "read(0): %s\n", buf); 273 DEBUG(DFD, "read(0): %s\n", buf); 274 exits(buf); 275 } 276 buf[n] = 0; 277 if(chdir(buf) < 0) { 278 errstr(ebuf, sizeof ebuf); 279 fprint(0, "chdir(%d:\"%s\"): %s\n", n, buf, ebuf); 280 DEBUG(DFD, "chdir(%d:\"%s\"): %s\n", n, buf, ebuf); 281 exits(ebuf); 282 } 283 } 284 285 DEBUG(DFD, "\niniting root\n"); 286 initroot(); 287 288 DEBUG(DFD, "exportfs: %s\n", buf); 289 290 if(srv == nil && srvfd == -1 && write(0, "OK", 2) != 2) 291 fatal("open ack write"); 292 293 if (readn(netfd, &initial, sizeof(ulong)) < sizeof(ulong)) 294 fatal("can't read initial string: %r\n"); 295 296 if (strncmp((char *)&initial, "impo", sizeof(ulong)) == 0) { 297 char buf[128], *p, *args[3]; 298 299 /* New import. Read import's parameters... */ 300 initial = 0; 301 302 p = buf; 303 while (p - buf < sizeof buf) { 304 if ((n = read(netfd, p, 1)) < 0) 305 fatal("can't read impo arguments: %r\n"); 306 307 if (n == 0) 308 fatal("connection closed while reading arguments\n"); 309 310 if (*p == '\n') 311 *p = '\0'; 312 if (*p++ == '\0') 313 break; 314 } 315 316 if (tokenize(buf, args, nelem(args)) != 2) 317 fatal("impo arguments invalid: impo%s...\n", buf); 318 319 if (strcmp(args[0], "aan") == 0) 320 filterp = aanfilter; 321 else if (strcmp(args[0], "nofilter") != 0) 322 fatal("import filter argument unsupported: %s\n", args[0]); 323 324 if (strcmp(args[1], "ssl") == 0) 325 encproto = Encssl; 326 else if (strcmp(args[1], "tls") == 0) 327 encproto = Enctls; 328 else if (strcmp(args[1], "clear") != 0) 329 fatal("import encryption proto unsupported: %s\n", args[1]); 330 331 if (encproto == Enctls) 332 sysfatal("%s: tls has not yet been implemented", argv[0]); 333 } 334 335 if (encproto != Encnone && ealgs && ai) { 336 uchar key[16]; 337 uchar digest[SHA1dlen]; 338 char fromclientsecret[21]; 339 char fromserversecret[21]; 340 int i; 341 342 memmove(key+4, ai->secret, ai->nsecret); 343 344 /* exchange random numbers */ 345 srand(truerand()); 346 for(i = 0; i < 4; i++) 347 key[i+12] = rand(); 348 349 if (initial) 350 fatal("Protocol botch: old import\n"); 351 if(readn(netfd, key, 4) != 4) 352 fatal("can't read key part; %r\n"); 353 354 if(write(netfd, key+12, 4) != 4) 355 fatal("can't write key part; %r\n"); 356 357 /* scramble into two secrets */ 358 sha1(key, sizeof(key), digest, nil); 359 mksecret(fromclientsecret, digest); 360 mksecret(fromserversecret, digest+10); 361 362 if (filterp) 363 netfd = filter(netfd, filterp); 364 365 switch (encproto) { 366 case Encssl: 367 netfd = pushssl(netfd, ealgs, fromserversecret, 368 fromclientsecret, nil); 369 break; 370 case Enctls: 371 default: 372 fatal("Unsupported encryption protocol\n"); 373 } 374 375 if(netfd < 0) 376 fatal("can't establish ssl connection: %r"); 377 } 378 else if (filterp) { 379 if (initial) 380 fatal("Protocol botch: don't know how to deal with this\n"); 381 netfd = filter(netfd, filterp); 382 } 383 384 /* 385 * Start serving file requests from the network 386 */ 387 for(;;) { 388 r = getsbuf(); 389 if(r == 0) 390 fatal("Out of service buffers"); 391 392 n = localread9pmsg(netfd, r->buf, messagesize, &initial); 393 if(n <= 0) 394 fatal(nil); 395 396 if(convM2S(r->buf, n, &r->work) == 0) 397 fatal("convM2S format error"); 398 399 DEBUG(DFD, "%F\n", &r->work); 400 (fcalls[r->work.type])(r); 401 } 402 } 403 404 /* 405 * WARNING: Replace this with the original version as soon as all 406 * _old_ imports have been replaced with negotiating imports. Also 407 * cpu relies on this (which needs to be fixed!) -- pb. 408 */ 409 static int 410 localread9pmsg(int fd, void *abuf, uint n, ulong *initial) 411 { 412 int m, len; 413 uchar *buf; 414 415 buf = abuf; 416 417 /* read count */ 418 assert(BIT32SZ == sizeof(ulong)); 419 if (*initial) { 420 memcpy(buf, initial, BIT32SZ); 421 *initial = 0; 422 } 423 else { 424 m = readn(fd, buf, BIT32SZ); 425 if(m != BIT32SZ){ 426 if(m < 0) 427 return -1; 428 return 0; 429 } 430 } 431 432 len = GBIT32(buf); 433 if(len <= BIT32SZ || len > n){ 434 werrstr("bad length in 9P2000 message header"); 435 return -1; 436 } 437 len -= BIT32SZ; 438 m = readn(fd, buf+BIT32SZ, len); 439 if(m < len) 440 return 0; 441 return BIT32SZ+m; 442 } 443 void 444 reply(Fcall *r, Fcall *t, char *err) 445 { 446 uchar *data; 447 int n; 448 449 t->tag = r->tag; 450 t->fid = r->fid; 451 if(err) { 452 t->type = Rerror; 453 t->ename = err; 454 } 455 else 456 t->type = r->type + 1; 457 458 DEBUG(DFD, "\t%F\n", t); 459 460 data = malloc(messagesize); /* not mallocz; no need to clear */ 461 if(data == nil) 462 fatal(Enomem); 463 n = convS2M(t, data, messagesize); 464 if(write(netfd, data, n)!=n) 465 {syslog(0, "exportfs", "short write: %r"); 466 fatal("mount write"); 467 } 468 free(data); 469 } 470 471 Fid * 472 getfid(int nr) 473 { 474 Fid *f; 475 476 for(f = fidhash(nr); f; f = f->next) 477 if(f->nr == nr) 478 return f; 479 480 return 0; 481 } 482 483 int 484 freefid(int nr) 485 { 486 Fid *f, **l; 487 char buf[128]; 488 489 l = &fidhash(nr); 490 for(f = *l; f; f = f->next) { 491 if(f->nr == nr) { 492 if(f->mid) { 493 sprint(buf, "/mnt/exportfs/%d", f->mid); 494 unmount(0, buf); 495 psmap[f->mid] = 0; 496 } 497 if(f->f) { 498 freefile(f->f); 499 f->f = nil; 500 } 501 if(f->dir){ 502 free(f->dir); 503 f->dir = nil; 504 } 505 *l = f->next; 506 f->next = fidfree; 507 fidfree = f; 508 return 1; 509 } 510 l = &f->next; 511 } 512 513 return 0; 514 } 515 516 Fid * 517 newfid(int nr) 518 { 519 Fid *new, **l; 520 int i; 521 522 l = &fidhash(nr); 523 for(new = *l; new; new = new->next) 524 if(new->nr == nr) 525 return 0; 526 527 if(fidfree == 0) { 528 fidfree = emallocz(sizeof(Fid) * Fidchunk); 529 530 for(i = 0; i < Fidchunk-1; i++) 531 fidfree[i].next = &fidfree[i+1]; 532 533 fidfree[Fidchunk-1].next = 0; 534 } 535 536 new = fidfree; 537 fidfree = new->next; 538 539 memset(new, 0, sizeof(Fid)); 540 new->next = *l; 541 *l = new; 542 new->nr = nr; 543 new->fid = -1; 544 new->mid = 0; 545 546 return new; 547 } 548 549 Fsrpc * 550 getsbuf(void) 551 { 552 static int ap; 553 int look, rounds; 554 Fsrpc *wb; 555 int small_instead_of_fast = 1; 556 557 if(small_instead_of_fast) 558 ap = 0; /* so we always start looking at the beginning and reuse buffers */ 559 560 for(rounds = 0; rounds < 10; rounds++) { 561 for(look = 0; look < Nr_workbufs; look++) { 562 if(++ap == Nr_workbufs) 563 ap = 0; 564 if(Workq[ap].busy == 0) 565 break; 566 } 567 568 if(look == Nr_workbufs){ 569 sleep(10 * rounds); 570 continue; 571 } 572 573 wb = &Workq[ap]; 574 wb->pid = 0; 575 wb->canint = 0; 576 wb->flushtag = NOTAG; 577 wb->busy = 1; 578 if(wb->buf == nil) /* allocate buffers dynamically to keep size down */ 579 wb->buf = emallocz(messagesize); 580 return wb; 581 } 582 fatal("No more work buffers"); 583 return nil; 584 } 585 586 void 587 freefile(File *f) 588 { 589 File *parent, *child; 590 591 Loop: 592 f->ref--; 593 if(f->ref > 0) 594 return; 595 freecnt++; 596 if(f->ref < 0) abort(); 597 DEBUG(DFD, "free %s\n", f->name); 598 /* delete from parent */ 599 parent = f->parent; 600 if(parent->child == f) 601 parent->child = f->childlist; 602 else{ 603 for(child=parent->child; child->childlist!=f; child=child->childlist) 604 if(child->childlist == nil) 605 fatal("bad child list"); 606 child->childlist = f->childlist; 607 } 608 freeqid(f->qidt); 609 free(f->name); 610 f->name = nil; 611 free(f); 612 f = parent; 613 if(f != nil) 614 goto Loop; 615 } 616 617 File * 618 file(File *parent, char *name) 619 { 620 Dir *dir; 621 char *path; 622 File *f; 623 624 DEBUG(DFD, "\tfile: 0x%p %s name %s\n", parent, parent->name, name); 625 626 path = makepath(parent, name); 627 if(patternfile != nil && excludefile(path)){ 628 free(path); 629 return nil; 630 } 631 dir = dirstat(path); 632 free(path); 633 if(dir == nil) 634 return nil; 635 636 for(f = parent->child; f; f = f->childlist) 637 if(strcmp(name, f->name) == 0) 638 break; 639 640 if(f == nil){ 641 f = emallocz(sizeof(File)); 642 f->name = estrdup(name); 643 644 f->parent = parent; 645 f->childlist = parent->child; 646 parent->child = f; 647 parent->ref++; 648 f->ref = 0; 649 filecnt++; 650 } 651 f->ref++; 652 f->qid.type = dir->qid.type; 653 f->qid.vers = dir->qid.vers; 654 f->qidt = uniqueqid(dir); 655 f->qid.path = f->qidt->uniqpath; 656 657 f->inval = 0; 658 659 free(dir); 660 661 return f; 662 } 663 664 void 665 initroot(void) 666 { 667 Dir *dir; 668 669 root = emallocz(sizeof(File)); 670 root->name = estrdup("."); 671 672 dir = dirstat(root->name); 673 if(dir == nil) 674 fatal("root stat"); 675 676 root->ref = 1; 677 root->qid.vers = dir->qid.vers; 678 root->qidt = uniqueqid(dir); 679 root->qid.path = root->qidt->uniqpath; 680 root->qid.type = QTDIR; 681 free(dir); 682 683 psmpt = emallocz(sizeof(File)); 684 psmpt->name = estrdup("/"); 685 686 dir = dirstat(psmpt->name); 687 if(dir == nil) 688 return; 689 690 psmpt->ref = 1; 691 psmpt->qid.vers = dir->qid.vers; 692 psmpt->qidt = uniqueqid(dir); 693 psmpt->qid.path = psmpt->qidt->uniqpath; 694 free(dir); 695 696 psmpt = file(psmpt, "mnt"); 697 if(psmpt == 0) 698 return; 699 psmpt = file(psmpt, "exportfs"); 700 } 701 702 char* 703 makepath(File *p, char *name) 704 { 705 int i, n; 706 char *c, *s, *path, *seg[256]; 707 708 seg[0] = name; 709 n = strlen(name)+2; 710 for(i = 1; i < 256 && p; i++, p = p->parent){ 711 seg[i] = p->name; 712 n += strlen(p->name)+1; 713 } 714 path = malloc(n); 715 if(path == nil) 716 fatal("out of memory"); 717 s = path; 718 719 while(i--) { 720 for(c = seg[i]; *c; c++) 721 *s++ = *c; 722 *s++ = '/'; 723 } 724 while(s[-1] == '/') 725 s--; 726 *s = '\0'; 727 728 return path; 729 } 730 731 int 732 qidhash(vlong path) 733 { 734 int h, n; 735 736 h = 0; 737 for(n=0; n<64; n+=Nqidbits){ 738 h ^= path; 739 path >>= Nqidbits; 740 } 741 return h & (Nqidtab-1); 742 } 743 744 void 745 freeqid(Qidtab *q) 746 { 747 ulong h; 748 Qidtab *l; 749 750 q->ref--; 751 if(q->ref > 0) 752 return; 753 qfreecnt++; 754 h = qidhash(q->path); 755 if(qidtab[h] == q) 756 qidtab[h] = q->next; 757 else{ 758 for(l=qidtab[h]; l->next!=q; l=l->next) 759 if(l->next == nil) 760 fatal("bad qid list"); 761 l->next = q->next; 762 } 763 free(q); 764 } 765 766 Qidtab* 767 qidlookup(Dir *d) 768 { 769 ulong h; 770 Qidtab *q; 771 772 h = qidhash(d->qid.path); 773 for(q=qidtab[h]; q!=nil; q=q->next) 774 if(q->type==d->type && q->dev==d->dev && q->path==d->qid.path) 775 return q; 776 return nil; 777 } 778 779 int 780 qidexists(vlong path) 781 { 782 int h; 783 Qidtab *q; 784 785 for(h=0; h<Nqidtab; h++) 786 for(q=qidtab[h]; q!=nil; q=q->next) 787 if(q->uniqpath == path) 788 return 1; 789 return 0; 790 } 791 792 Qidtab* 793 uniqueqid(Dir *d) 794 { 795 ulong h; 796 vlong path; 797 Qidtab *q; 798 799 q = qidlookup(d); 800 if(q != nil){ 801 q->ref++; 802 return q; 803 } 804 path = d->qid.path; 805 while(qidexists(path)){ 806 DEBUG(DFD, "collision on %s\n", d->name); 807 /* collision: find a new one */ 808 ncollision++; 809 path &= QIDPATH; 810 ++newqid; 811 if(newqid >= (1<<16)){ 812 DEBUG(DFD, "collision wraparound\n"); 813 newqid = 1; 814 } 815 path |= newqid<<48; 816 DEBUG(DFD, "assign qid %.16llux\n", path); 817 } 818 q = mallocz(sizeof(Qidtab), 1); 819 if(q == nil) 820 fatal("no memory for qid table"); 821 qidcnt++; 822 q->ref = 1; 823 q->type = d->type; 824 q->dev = d->dev; 825 q->path = d->qid.path; 826 q->uniqpath = path; 827 h = qidhash(d->qid.path); 828 q->next = qidtab[h]; 829 qidtab[h] = q; 830 return q; 831 } 832 833 void 834 fatal(char *s, ...) 835 { 836 char buf[ERRMAX]; 837 va_list arg; 838 Proc *m; 839 840 if (s) { 841 va_start(arg, s); 842 vsnprint(buf, ERRMAX, s, arg); 843 va_end(arg); 844 } 845 846 /* Clear away the slave children */ 847 for(m = Proclist; m; m = m->next) 848 postnote(PNPROC, m->pid, "kill"); 849 850 DEBUG(DFD, "%s\n", buf); 851 if (s) 852 sysfatal("%s", buf); /* caution: buf could contain '%' */ 853 else 854 exits(nil); 855 } 856 857 void* 858 emallocz(uint n) 859 { 860 void *p; 861 862 p = mallocz(n, 1); 863 if(p == nil) 864 fatal(Enomem); 865 return p; 866 } 867 868 char* 869 estrdup(char *s) 870 { 871 char *t; 872 873 t = strdup(s); 874 if(t == nil) 875 fatal(Enomem); 876 return t; 877 } 878 879 /* Network on fd1, mount driver on fd0 */ 880 int 881 filter(int fd, char *cmd) 882 { 883 int p[2], lfd, len, nb, argc; 884 char newport[128], buf[128], devdir[40], *s, *file, *argv[16]; 885 886 /* Get a free port and post it to the client. */ 887 if (announce(anstring, devdir) < 0) 888 sysfatal("filter: Cannot announce %s: %r", anstring); 889 890 snprint(buf, sizeof(buf), "%s/local", devdir); 891 buf[sizeof buf - 1] = '\0'; 892 if ((lfd = open(buf, OREAD)) < 0) 893 sysfatal("filter: Cannot open %s: %r", buf); 894 if ((len = read(lfd, newport, sizeof newport - 1)) < 0) 895 sysfatal("filter: Cannot read %s: %r", buf); 896 close(lfd); 897 newport[len] = '\0'; 898 899 if ((s = strchr(newport, '\n')) != nil) 900 *s = '\0'; 901 902 if ((nb = write(fd, newport, len)) < 0) 903 sysfatal("getport; cannot write port; %r"); 904 assert(nb == len); 905 906 argc = tokenize(cmd, argv, nelem(argv)-2); 907 if (argc == 0) 908 sysfatal("filter: empty command"); 909 argv[argc++] = buf; 910 argv[argc] = nil; 911 file = argv[0]; 912 if (s = strrchr(argv[0], '/')) 913 argv[0] = s+1; 914 915 if(pipe(p) < 0) 916 fatal("pipe"); 917 918 switch(rfork(RFNOWAIT|RFPROC|RFFDG)) { 919 case -1: 920 fatal("rfork record module"); 921 case 0: 922 if (dup(p[0], 1) < 0) 923 fatal("filter: Cannot dup to 1; %r\n"); 924 if (dup(p[0], 0) < 0) 925 fatal("filter: Cannot dup to 0; %r\n"); 926 close(p[0]); 927 close(p[1]); 928 exec(file, argv); 929 fatal("exec record module"); 930 default: 931 close(fd); 932 close(p[0]); 933 } 934 return p[1]; 935 } 936 937 static void 938 mksecret(char *t, uchar *f) 939 { 940 sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", 941 f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]); 942 } 943