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