1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include <bio.h> 6 #include <ctype.h> 7 #include <ndb.h> 8 #include <ip.h> 9 10 enum 11 { 12 Nreply= 20, 13 Maxreply= 256, 14 Maxrequest= 128, 15 Maxpath= 128, 16 Maxfdata= 8192, 17 18 Qdir= 0, 19 Qcs= 1, 20 }; 21 22 typedef struct Mfile Mfile; 23 typedef struct Mlist Mlist; 24 typedef struct Network Network; 25 typedef struct Flushreq Flushreq; 26 typedef struct Job Job; 27 28 int vers; /* incremented each clone/attach */ 29 30 struct Mfile 31 { 32 int busy; 33 34 char *user; 35 Qid qid; 36 int fid; 37 38 /* 39 * current request 40 */ 41 char *net; 42 char *host; 43 char *serv; 44 char *rem; 45 46 /* 47 * result of the last lookup 48 */ 49 Network *nextnet; 50 int nreply; 51 char *reply[Nreply]; 52 int replylen[Nreply]; 53 }; 54 55 struct Mlist 56 { 57 Mlist *next; 58 Mfile mf; 59 }; 60 61 62 // 63 // active requests 64 // 65 struct Job 66 { 67 Job *next; 68 int flushed; 69 Fcall request; 70 Fcall reply; 71 }; 72 Lock joblock; 73 Job *joblist; 74 75 Mlist *mlist; 76 int mfd[2]; 77 int debug; 78 int paranoia; 79 jmp_buf masterjmp; /* return through here after a slave process has been created */ 80 int *isslave; /* *isslave non-zero means this is a slave process */ 81 char *dbfile; 82 Ndb *db, *netdb; 83 84 void rversion(Job*); 85 void rflush(Job*); 86 void rattach(Job*, Mfile*); 87 char* rwalk(Job*, Mfile*); 88 void ropen(Job*, Mfile*); 89 void rcreate(Job*, Mfile*); 90 void rread(Job*, Mfile*); 91 void rwrite(Job*, Mfile*); 92 void rclunk(Job*, Mfile*); 93 void rremove(Job*, Mfile*); 94 void rstat(Job*, Mfile*); 95 void rwstat(Job*, Mfile*); 96 void rauth(Job*); 97 void sendmsg(Job*, char*); 98 void error(char*); 99 void mountinit(char*, char*); 100 void io(void); 101 void ndbinit(void); 102 void netinit(int); 103 void netadd(char*); 104 char *genquery(Mfile*, char*); 105 char* ipinfoquery(Mfile*, char**, int); 106 int needproto(Network*, Ndbtuple*); 107 int lookup(Mfile*); 108 Ndbtuple* reorder(Ndbtuple*, Ndbtuple*); 109 void ipid(void); 110 void readipinterfaces(void); 111 void* emalloc(int); 112 char* estrdup(char*); 113 Job* newjob(void); 114 void freejob(Job*); 115 void setext(char*, int, char*); 116 void cleanmf(Mfile*); 117 118 extern void paralloc(void); 119 120 Lock dblock; /* mutex on database operations */ 121 Lock netlock; /* mutex for netinit() */ 122 123 char *logfile = "cs"; 124 char *paranoiafile = "cs.paranoia"; 125 126 char mntpt[Maxpath]; 127 char netndb[Maxpath]; 128 129 /* 130 * Network specific translators 131 */ 132 Ndbtuple* iplookup(Network*, char*, char*, int); 133 char* iptrans(Ndbtuple*, Network*, char*, char*, int); 134 Ndbtuple* telcolookup(Network*, char*, char*, int); 135 char* telcotrans(Ndbtuple*, Network*, char*, char*, int); 136 Ndbtuple* dnsiplookup(char*, Ndbs*); 137 138 struct Network 139 { 140 char *net; 141 Ndbtuple *(*lookup)(Network*, char*, char*, int); 142 char *(*trans)(Ndbtuple*, Network*, char*, char*, int); 143 int considered; 144 int fasttimeouthack; 145 Network *next; 146 }; 147 148 enum 149 { 150 Nilfast, 151 Ntcp, 152 Nil, 153 Nudp, 154 Nicmp, 155 Nrudp, 156 Ntelco, 157 }; 158 159 /* 160 * net doesn't apply to udp, icmp, or telco (for speed) 161 */ 162 Network network[] = { 163 [Nilfast] { "il", iplookup, iptrans, 0, 1 }, 164 [Ntcp] { "tcp", iplookup, iptrans, 0, 0 }, 165 [Nil] { "il", iplookup, iptrans, 0, 0 }, 166 [Nudp] { "udp", iplookup, iptrans, 1, 0 }, 167 [Nicmp] { "icmp", iplookup, iptrans, 1, 0 }, 168 [Nrudp] { "rudp", iplookup, iptrans, 1, 0 }, 169 [Ntelco] { "telco", telcolookup, telcotrans, 1, 0 }, 170 { 0 }, 171 }; 172 173 Lock ipifclock; 174 Ipifc *ipifcs; 175 176 char eaddr[Ndbvlen]; /* ascii ethernet address */ 177 char ipaddr[Ndbvlen]; /* ascii internet address */ 178 uchar ipa[IPaddrlen]; /* binary internet address */ 179 char mysysname[Ndbvlen]; 180 181 Network *netlist; /* networks ordered by preference */ 182 Network *last; 183 184 void 185 usage(void) 186 { 187 fprint(2, "usage: %s [-d] [-f ndb-file] [-x netmtpt] [-n]\n", argv0); 188 exits("usage"); 189 } 190 191 void 192 main(int argc, char *argv[]) 193 { 194 char servefile[Maxpath]; 195 int justsetname; 196 char *p; 197 char ext[Maxpath]; 198 199 justsetname = 0; 200 setnetmtpt(mntpt, sizeof(mntpt), nil); 201 ext[0] = 0; 202 ARGBEGIN{ 203 case 'd': 204 debug = 1; 205 break; 206 case 'f': 207 p = ARGF(); 208 if(p == nil) 209 usage(); 210 dbfile = p; 211 break; 212 case 'x': 213 p = ARGF(); 214 if(p == nil) 215 usage(); 216 setnetmtpt(mntpt, sizeof(mntpt), p); 217 setext(ext, sizeof(ext), mntpt); 218 break; 219 case 'n': 220 justsetname = 1; 221 break; 222 }ARGEND 223 USED(argc); 224 USED(argv); 225 226 rfork(RFREND|RFNOTEG); 227 228 snprint(servefile, sizeof(servefile), "#s/cs%s", ext); 229 snprint(netndb, sizeof(netndb), "%s/ndb", mntpt); 230 unmount(servefile, mntpt); 231 remove(servefile); 232 233 fmtinstall('E', eipfmt); 234 fmtinstall('I', eipfmt); 235 fmtinstall('M', eipfmt); 236 fmtinstall('F', fcallfmt); 237 238 ndbinit(); 239 netinit(0); 240 241 if(!justsetname){ 242 mountinit(servefile, mntpt); 243 io(); 244 } 245 exits(0); 246 } 247 248 /* 249 * if a mount point is specified, set the cs extention to be the mount point 250 * with '_'s replacing '/'s 251 */ 252 void 253 setext(char *ext, int n, char *p) 254 { 255 int i, c; 256 257 n--; 258 for(i = 0; i < n; i++){ 259 c = p[i]; 260 if(c == 0) 261 break; 262 if(c == '/') 263 c = '_'; 264 ext[i] = c; 265 } 266 ext[i] = 0; 267 } 268 269 void 270 mountinit(char *service, char *mntpt) 271 { 272 int f; 273 int p[2]; 274 char buf[32]; 275 276 if(pipe(p) < 0) 277 error("pipe failed"); 278 switch(rfork(RFFDG|RFPROC|RFNAMEG)){ 279 case 0: 280 close(p[1]); 281 break; 282 case -1: 283 error("fork failed\n"); 284 default: 285 /* 286 * make a /srv/cs 287 */ 288 f = create(service, 1, 0666); 289 if(f < 0) 290 error(service); 291 snprint(buf, sizeof(buf), "%d", p[1]); 292 if(write(f, buf, strlen(buf)) != strlen(buf)) 293 error("write /srv/cs"); 294 close(f); 295 296 /* 297 * put ourselves into the file system 298 */ 299 close(p[0]); 300 if(mount(p[1], -1, mntpt, MAFTER, "") < 0) 301 error("mount failed\n"); 302 _exits(0); 303 } 304 mfd[0] = mfd[1] = p[0]; 305 } 306 307 void 308 ndbinit(void) 309 { 310 db = ndbopen(dbfile); 311 if(db == nil) 312 error("can't open network database"); 313 314 netdb = ndbopen(netndb); 315 if(netdb != nil){ 316 netdb->nohash = 1; 317 db = ndbcat(netdb, db); 318 } 319 } 320 321 Mfile* 322 newfid(int fid) 323 { 324 Mlist *f, *ff; 325 Mfile *mf; 326 327 ff = 0; 328 for(f = mlist; f; f = f->next) 329 if(f->mf.busy && f->mf.fid == fid) 330 return &f->mf; 331 else if(!ff && !f->mf.busy) 332 ff = f; 333 if(ff == 0){ 334 ff = emalloc(sizeof *f); 335 ff->next = mlist; 336 mlist = ff; 337 } 338 mf = &ff->mf; 339 memset(mf, 0, sizeof *mf); 340 mf->fid = fid; 341 return mf; 342 } 343 344 Job* 345 newjob(void) 346 { 347 Job *job; 348 349 job = mallocz(sizeof(Job), 1); 350 lock(&joblock); 351 job->next = joblist; 352 joblist = job; 353 job->request.tag = -1; 354 unlock(&joblock); 355 return job; 356 } 357 358 void 359 freejob(Job *job) 360 { 361 Job **l; 362 363 lock(&joblock); 364 for(l = &joblist; *l; l = &(*l)->next){ 365 if((*l) == job){ 366 *l = job->next; 367 free(job); 368 break; 369 } 370 } 371 unlock(&joblock); 372 } 373 374 void 375 flushjob(int tag) 376 { 377 Job *job; 378 379 lock(&joblock); 380 for(job = joblist; job; job = job->next){ 381 if(job->request.tag == tag && job->request.type != Tflush){ 382 job->flushed = 1; 383 break; 384 } 385 } 386 unlock(&joblock); 387 } 388 389 void 390 io(void) 391 { 392 long n; 393 Mfile *mf; 394 int slaveflag; 395 uchar mdata[IOHDRSZ + Maxfdata]; 396 Job *job; 397 398 /* 399 * if we ask dns to fulfill requests, 400 * a slave process is created to wait for replies. The 401 * master process returns immediately via a longjmp 402 * through 'masterjmp'. 403 * 404 * *isslave is a pointer into the call stack to a variable 405 * that tells whether or not the current process is a slave. 406 */ 407 slaveflag = 0; /* init slave variable */ 408 isslave = &slaveflag; 409 setjmp(masterjmp); 410 411 for(;;){ 412 n = read9pmsg(mfd[0], mdata, sizeof mdata); 413 if(n<=0) 414 error("mount read"); 415 job = newjob(); 416 if(convM2S(mdata, n, &job->request) != n){ 417 syslog(1, logfile, "format error %ux %ux %ux %ux %ux", mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]); 418 freejob(job); 419 continue; 420 } 421 if(job->request.fid<0) 422 error("fid out of range"); 423 lock(&dblock); 424 mf = newfid(job->request.fid); 425 if(debug) 426 syslog(0, logfile, "%F", &job->request); 427 428 429 switch(job->request.type){ 430 default: 431 syslog(1, logfile, "unknown request type %d", job->request.type); 432 break; 433 case Tversion: 434 rversion(job); 435 break; 436 case Tauth: 437 rauth(job); 438 break; 439 case Tflush: 440 rflush(job); 441 break; 442 case Tattach: 443 rattach(job, mf); 444 break; 445 case Twalk: 446 rwalk(job, mf); 447 break; 448 case Topen: 449 ropen(job, mf); 450 break; 451 case Tcreate: 452 rcreate(job, mf); 453 break; 454 case Tread: 455 rread(job, mf); 456 break; 457 case Twrite: 458 rwrite(job, mf); 459 break; 460 case Tclunk: 461 rclunk(job, mf); 462 break; 463 case Tremove: 464 rremove(job, mf); 465 break; 466 case Tstat: 467 rstat(job, mf); 468 break; 469 case Twstat: 470 rwstat(job, mf); 471 break; 472 } 473 unlock(&dblock); 474 475 freejob(job); 476 477 /* 478 * slave processes die after replying 479 */ 480 if(*isslave){ 481 if(debug) 482 syslog(0, logfile, "slave death %d", getpid()); 483 _exits(0); 484 } 485 } 486 } 487 488 void 489 rversion(Job *job) 490 { 491 if(job->request.msize > IOHDRSZ + Maxfdata) 492 job->reply.msize = IOHDRSZ + Maxfdata; 493 else 494 job->reply.msize = job->request.msize; 495 if(strncmp(job->request.version, "9P2000", 6) != 0) 496 sendmsg(job, "unknown 9P version"); 497 else{ 498 job->reply.version = "9P2000"; 499 sendmsg(job, 0); 500 } 501 } 502 503 void 504 rauth(Job *job) 505 { 506 sendmsg(job, "cs: authentication not required"); 507 } 508 509 /* 510 * don't flush till all the slaves are done 511 */ 512 void 513 rflush(Job *job) 514 { 515 flushjob(job->request.oldtag); 516 sendmsg(job, 0); 517 } 518 519 void 520 rattach(Job *job, Mfile *mf) 521 { 522 if(mf->busy == 0){ 523 mf->busy = 1; 524 mf->user = estrdup(job->request.uname); 525 } 526 mf->qid.vers = vers++; 527 mf->qid.type = QTDIR; 528 mf->qid.path = 0LL; 529 job->reply.qid = mf->qid; 530 sendmsg(job, 0); 531 } 532 533 534 char* 535 rwalk(Job *job, Mfile *mf) 536 { 537 char *err; 538 char **elems; 539 int nelems; 540 int i; 541 Mfile *nmf; 542 Qid qid; 543 544 err = 0; 545 nmf = nil; 546 elems = job->request.wname; 547 nelems = job->request.nwname; 548 job->reply.nwqid = 0; 549 550 if(job->request.newfid != job->request.fid){ 551 /* clone fid */ 552 if(job->request.newfid<0){ 553 err = "clone newfid out of range"; 554 goto send; 555 } 556 nmf = newfid(job->request.newfid); 557 if(nmf->busy){ 558 nmf = nil; 559 err = "clone to used channel"; 560 goto send; 561 } 562 *nmf = *mf; 563 nmf->user = estrdup(mf->user); 564 nmf->fid = job->request.newfid; 565 nmf->qid.vers = vers++; 566 mf = nmf; 567 } 568 /* else nmf will be nil */ 569 570 qid = mf->qid; 571 if(nelems > 0){ 572 /* walk fid */ 573 for(i=0; i<nelems && i<MAXWELEM; i++){ 574 if((qid.type & QTDIR) == 0){ 575 err = "not a directory"; 576 break; 577 } 578 if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){ 579 qid.type = QTDIR; 580 qid.path = Qdir; 581 Found: 582 job->reply.wqid[i] = qid; 583 job->reply.nwqid++; 584 continue; 585 } 586 if(strcmp(elems[i], "cs") == 0){ 587 qid.type = QTFILE; 588 qid.path = Qcs; 589 goto Found; 590 } 591 err = "file does not exist"; 592 break; 593 } 594 } 595 596 send: 597 if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)){ 598 nmf->busy = 0; 599 nmf->fid = 0; 600 } 601 if(err == nil) 602 mf->qid = qid; 603 sendmsg(job, err); 604 return err; 605 } 606 607 void 608 ropen(Job *job, Mfile *mf) 609 { 610 int mode; 611 char *err; 612 613 err = 0; 614 mode = job->request.mode; 615 if(mf->qid.type & QTDIR){ 616 if(mode) 617 err = "permission denied"; 618 } 619 job->reply.qid = mf->qid; 620 job->reply.iounit = 0; 621 sendmsg(job, err); 622 } 623 624 void 625 rcreate(Job *job, Mfile *mf) 626 { 627 USED(mf); 628 sendmsg(job, "creation permission denied"); 629 } 630 631 void 632 rread(Job *job, Mfile *mf) 633 { 634 int i, n, cnt; 635 long off, toff, clock; 636 Dir dir; 637 uchar buf[Maxfdata]; 638 char *err; 639 640 n = 0; 641 err = 0; 642 off = job->request.offset; 643 cnt = job->request.count; 644 if(mf->qid.type & QTDIR){ 645 clock = time(0); 646 if(off == 0){ 647 dir.name = "cs"; 648 dir.qid.type = QTFILE; 649 dir.qid.vers = vers; 650 dir.qid.path = Qcs; 651 dir.mode = 0666; 652 dir.length = 0; 653 dir.uid = mf->user; 654 dir.gid = mf->user; 655 dir.muid = mf->user; 656 dir.atime = clock; /* wrong */ 657 dir.mtime = clock; /* wrong */ 658 n = convD2M(&dir, buf, sizeof buf); 659 } 660 job->reply.data = (char*)buf; 661 } else { 662 for(;;){ 663 /* look for an answer at the right offset */ 664 toff = 0; 665 for(i = 0; mf->reply[i] && i < mf->nreply; i++){ 666 n = mf->replylen[i]; 667 if(off < toff + n) 668 break; 669 toff += n; 670 } 671 if(i < mf->nreply) 672 break; /* got something to return */ 673 674 /* try looking up more answers */ 675 if(lookup(mf) == 0){ 676 /* no more */ 677 n = 0; 678 goto send; 679 } 680 } 681 682 /* give back a single reply (or part of one) */ 683 job->reply.data = mf->reply[i] + (off - toff); 684 if(cnt > toff - off + n) 685 n = toff - off + n; 686 else 687 n = cnt; 688 } 689 send: 690 job->reply.count = n; 691 sendmsg(job, err); 692 } 693 void 694 cleanmf(Mfile *mf) 695 { 696 int i; 697 698 if(mf->net != nil){ 699 free(mf->net); 700 mf->net = nil; 701 } 702 if(mf->host != nil){ 703 free(mf->host); 704 mf->host = nil; 705 } 706 if(mf->serv != nil){ 707 free(mf->serv); 708 mf->serv = nil; 709 } 710 if(mf->rem != nil){ 711 free(mf->rem); 712 mf->rem = nil; 713 } 714 if(mf->user != nil){ 715 free(mf->user); 716 mf->user = nil; 717 } 718 for(i = 0; i < mf->nreply; i++){ 719 free(mf->reply[i]); 720 mf->reply[i] = nil; 721 mf->replylen[i] = 0; 722 } 723 mf->nreply = 0; 724 mf->nextnet = netlist; 725 } 726 727 void 728 rwrite(Job *job, Mfile *mf) 729 { 730 int cnt, n; 731 char *err; 732 char *field[4]; 733 734 err = 0; 735 cnt = job->request.count; 736 if(mf->qid.type & QTDIR){ 737 err = "can't write directory"; 738 goto send; 739 } 740 if(cnt >= Maxrequest){ 741 err = "request too long"; 742 goto send; 743 } 744 job->request.data[cnt] = 0; 745 746 /* 747 * toggle debugging 748 */ 749 if(strncmp(job->request.data, "debug", 5)==0){ 750 debug ^= 1; 751 syslog(1, logfile, "debug %d", debug); 752 goto send; 753 } 754 755 /* 756 * toggle debugging 757 */ 758 if(strncmp(job->request.data, "paranoia", 8)==0){ 759 paranoia ^= 1; 760 syslog(1, logfile, "paranoia %d", paranoia); 761 goto send; 762 } 763 764 /* 765 * add networks to the default list 766 */ 767 if(strncmp(job->request.data, "add ", 4)==0){ 768 if(job->request.data[cnt-1] == '\n') 769 job->request.data[cnt-1] = 0; 770 netadd(job->request.data+4); 771 readipinterfaces(); 772 goto send; 773 } 774 775 /* 776 * refresh all state 777 */ 778 if(strncmp(job->request.data, "refresh", 7)==0){ 779 netinit(1); 780 goto send; 781 } 782 783 /* start transaction with a clean slate */ 784 cleanmf(mf); 785 786 /* 787 * look for a general query 788 */ 789 if(*job->request.data == '!'){ 790 err = genquery(mf, job->request.data+1); 791 goto send; 792 } 793 794 if(debug) 795 syslog(0, logfile, "write %s", job->request.data); 796 if(paranoia) 797 syslog(0, paranoiafile, "write %s by %s", job->request.data, mf->user); 798 799 /* 800 * break up name 801 */ 802 n = getfields(job->request.data, field, 4, 1, "!"); 803 switch(n){ 804 case 1: 805 mf->net = strdup("net"); 806 mf->host = strdup(field[0]); 807 break; 808 case 4: 809 mf->rem = strdup(field[3]); 810 /* fall through */ 811 case 3: 812 mf->serv = strdup(field[2]); 813 /* fall through */ 814 case 2: 815 mf->host = strdup(field[1]); 816 mf->net = strdup(field[0]); 817 break; 818 } 819 820 /* 821 * do the first net worth of lookup 822 */ 823 if(lookup(mf) == 0) 824 err = "can't translate address"; 825 send: 826 job->reply.count = cnt; 827 sendmsg(job, err); 828 } 829 830 void 831 rclunk(Job *job, Mfile *mf) 832 { 833 cleanmf(mf); 834 mf->busy = 0; 835 mf->fid = 0; 836 sendmsg(job, 0); 837 } 838 839 void 840 rremove(Job *job, Mfile *mf) 841 { 842 USED(mf); 843 sendmsg(job, "remove permission denied"); 844 } 845 846 void 847 rstat(Job *job, Mfile *mf) 848 { 849 Dir dir; 850 uchar buf[IOHDRSZ+Maxfdata]; 851 852 if(mf->qid.type & QTDIR){ 853 dir.name = "."; 854 dir.mode = DMDIR|0555; 855 } else { 856 dir.name = "cs"; 857 dir.mode = 0666; 858 } 859 dir.qid = mf->qid; 860 dir.length = 0; 861 dir.uid = mf->user; 862 dir.gid = mf->user; 863 dir.muid = mf->user; 864 dir.atime = dir.mtime = time(0); 865 job->reply.nstat = convD2M(&dir, buf, sizeof buf); 866 job->reply.stat = buf; 867 sendmsg(job, 0); 868 } 869 870 void 871 rwstat(Job *job, Mfile *mf) 872 { 873 USED(mf); 874 sendmsg(job, "wstat permission denied"); 875 } 876 877 void 878 sendmsg(Job *job, char *err) 879 { 880 int n; 881 uchar mdata[IOHDRSZ + Maxfdata]; 882 char ename[ERRMAX]; 883 884 if(err){ 885 job->reply.type = Rerror; 886 snprint(ename, sizeof(ename), "cs: %s", err); 887 job->reply.ename = ename; 888 }else{ 889 job->reply.type = job->request.type+1; 890 } 891 job->reply.tag = job->request.tag; 892 n = convS2M(&job->reply, mdata, sizeof mdata); 893 if(n == 0){ 894 syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply); 895 abort(); 896 } 897 lock(&joblock); 898 if(job->flushed == 0) 899 if(write(mfd[1], mdata, n)!=n) 900 error("mount write"); 901 unlock(&joblock); 902 if(debug) 903 syslog(0, logfile, "%F %d", &job->reply, n); 904 } 905 906 void 907 error(char *s) 908 { 909 syslog(1, "cs", "%s: %r", s); 910 _exits(0); 911 } 912 913 static int 914 isvalidip(uchar *ip) 915 { 916 return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0; 917 } 918 919 void 920 readipinterfaces(void) 921 { 922 Ipifc *nifc; 923 Iplifc *lifc; 924 925 ipifcs = readipifc(mntpt, ipifcs, -1); 926 for(nifc = ipifcs; nifc; nifc = nifc->next) 927 for(lifc = nifc->lifc; lifc; lifc = lifc->next) 928 if(ipcmp(lifc->ip, IPnoaddr) != 0){ 929 ipmove(ipa, lifc->ip); 930 sprint(ipaddr, "%I", ipa); 931 if(debug) 932 syslog(0, "dns", "ipaddr is %s\n", ipaddr); 933 return; 934 } 935 ipmove(ipa, IPnoaddr); 936 } 937 938 /* 939 * get the system name 940 */ 941 void 942 ipid(void) 943 { 944 uchar addr[6]; 945 Ndbtuple *t; 946 char *p, *attr; 947 Ndbs s; 948 int f; 949 char buf[Maxpath]; 950 951 952 /* use environment, ether addr, or ipaddr to get system name */ 953 if(*mysysname == 0){ 954 /* 955 * environment has priority. 956 * 957 * on the sgi power the default system name 958 * is the ip address. ignore that. 959 * 960 */ 961 p = getenv("sysname"); 962 if(p){ 963 attr = ipattr(p); 964 if(strcmp(attr, "ip") != 0) 965 strcpy(mysysname, p); 966 } 967 968 /* 969 * the /net/ndb contains what the network 970 * figured out from DHCP. use that name if 971 * there is one. 972 */ 973 if(*mysysname == 0 && netdb != nil){ 974 ndbreopen(netdb); 975 for(t = ndbparse(netdb); t != nil; t = t->entry){ 976 if(strcmp(t->attr, "sys") == 0){ 977 strcpy(mysysname, t->val); 978 break; 979 } 980 } 981 ndbfree(t); 982 } 983 984 /* next network database, ip address, and ether address to find a name */ 985 if(*mysysname == 0){ 986 t = nil; 987 if(isvalidip(ipa)) 988 t = ndbgetval(db, &s, "ip", ipaddr, "sys", mysysname); 989 else { 990 for(f = 0; f < 3; f++){ 991 snprint(buf, sizeof buf, "%s/ether%d", mntpt, f); 992 if(myetheraddr(addr, buf) >= 0){ 993 snprint(eaddr, sizeof(eaddr), "%E", addr); 994 t = ndbgetval(db, &s, "ether", eaddr, "sys", 995 mysysname); 996 if(t != nil) 997 break; 998 } 999 } 1000 } 1001 ndbfree(t); 1002 } 1003 1004 /* nothing else worked, use the ip address */ 1005 if(*mysysname == 0 && isvalidip(ipa)) 1006 strcpy(mysysname, ipaddr); 1007 1008 1009 /* set /dev/mysysname if we now know it */ 1010 if(*mysysname){ 1011 f = open("/dev/sysname", OWRITE); 1012 if(f >= 0){ 1013 write(f, mysysname, strlen(mysysname)); 1014 close(f); 1015 } 1016 } 1017 } 1018 } 1019 1020 /* 1021 * Set up a list of default networks by looking for 1022 * /net/ * /clone. 1023 */ 1024 void 1025 netinit(int background) 1026 { 1027 char clone[Maxpath]; 1028 Network *np; 1029 static int working; 1030 1031 if(background){ 1032 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){ 1033 case 0: 1034 break; 1035 default: 1036 return; 1037 } 1038 lock(&netlock); 1039 } 1040 1041 /* add the mounted networks to the default list */ 1042 for(np = network; np->net; np++){ 1043 if(np->considered) 1044 continue; 1045 snprint(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net); 1046 if(access(clone, AEXIST) < 0) 1047 continue; 1048 if(netlist) 1049 last->next = np; 1050 else 1051 netlist = np; 1052 last = np; 1053 np->next = 0; 1054 np->considered = 1; 1055 } 1056 1057 /* find out what our ip address is */ 1058 readipinterfaces(); 1059 1060 /* set the system name if we need to, these says ip is all we have */ 1061 ipid(); 1062 1063 if(debug) 1064 syslog(0, logfile, "mysysname %s eaddr %s ipaddr %s ipa %I\n", 1065 mysysname, eaddr, ipaddr, ipa); 1066 1067 if(background){ 1068 unlock(&netlock); 1069 _exits(0); 1070 } 1071 } 1072 1073 /* 1074 * add networks to the standard list 1075 */ 1076 void 1077 netadd(char *p) 1078 { 1079 Network *np; 1080 char *field[12]; 1081 int i, n; 1082 1083 n = getfields(p, field, 12, 1, " "); 1084 for(i = 0; i < n; i++){ 1085 for(np = network; np->net; np++){ 1086 if(strcmp(field[i], np->net) != 0) 1087 continue; 1088 if(np->considered) 1089 break; 1090 if(netlist) 1091 last->next = np; 1092 else 1093 netlist = np; 1094 last = np; 1095 np->next = 0; 1096 np->considered = 1; 1097 } 1098 } 1099 } 1100 1101 /* 1102 * make a tuple 1103 */ 1104 Ndbtuple* 1105 mktuple(char *attr, char *val) 1106 { 1107 Ndbtuple *t; 1108 1109 t = emalloc(sizeof(Ndbtuple)); 1110 strcpy(t->attr, attr); 1111 strncpy(t->val, val, sizeof(t->val)); 1112 t->val[sizeof(t->val)-1] = 0; 1113 t->line = t; 1114 t->entry = 0; 1115 return t; 1116 } 1117 1118 int 1119 lookforproto(Ndbtuple *t, char *proto) 1120 { 1121 for(; t != nil; t = t->entry) 1122 if(strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0) 1123 return 1; 1124 return 0; 1125 } 1126 1127 /* 1128 * lookup a request. the network "net" means we should pick the 1129 * best network to get there. 1130 */ 1131 int 1132 lookup(Mfile *mf) 1133 { 1134 Network *np; 1135 char *cp; 1136 Ndbtuple *nt, *t; 1137 char reply[Maxreply]; 1138 int i, rv; 1139 int hack; 1140 1141 /* open up the standard db files */ 1142 if(db == 0) 1143 ndbinit(); 1144 if(db == 0) 1145 error("can't open mf->network database\n"); 1146 1147 rv = 0; 1148 1149 if(mf->net == nil) 1150 return 0; /* must have been a genquery */ 1151 1152 if(strcmp(mf->net, "net") == 0){ 1153 /* 1154 * go through set of default nets 1155 */ 1156 for(np = mf->nextnet; np; np = np->next){ 1157 nt = (*np->lookup)(np, mf->host, mf->serv, 1); 1158 if(nt == nil) 1159 continue; 1160 hack = np->fasttimeouthack && !lookforproto(nt, np->net); 1161 for(t = nt; mf->nreply < Nreply && t; t = t->entry){ 1162 cp = (*np->trans)(t, np, mf->serv, mf->rem, hack); 1163 if(cp){ 1164 /* avoid duplicates */ 1165 for(i = 0; i < mf->nreply; i++) 1166 if(strcmp(mf->reply[i], cp) == 0) 1167 break; 1168 if(i == mf->nreply){ 1169 /* save the reply */ 1170 mf->replylen[mf->nreply] = strlen(cp); 1171 mf->reply[mf->nreply++] = cp; 1172 rv++; 1173 } 1174 } 1175 } 1176 ndbfree(nt); 1177 np = np->next; 1178 break; 1179 } 1180 mf->nextnet = np; 1181 return rv; 1182 } 1183 1184 /* 1185 * if not /net, we only get one lookup 1186 */ 1187 if(mf->nreply != 0) 1188 return 0; 1189 1190 /* 1191 * look for a specific network 1192 */ 1193 for(np = netlist; np->net != nil; np++){ 1194 if(np->fasttimeouthack) 1195 continue; 1196 if(strcmp(np->net, mf->net) == 0) 1197 break; 1198 } 1199 1200 if(np->net != nil){ 1201 /* 1202 * known network 1203 */ 1204 nt = (*np->lookup)(np, mf->host, mf->serv, 1); 1205 for(t = nt; mf->nreply < Nreply && t; t = t->entry){ 1206 cp = (*np->trans)(t, np, mf->serv, mf->rem, 0); 1207 if(cp){ 1208 mf->replylen[mf->nreply] = strlen(cp); 1209 mf->reply[mf->nreply++] = cp; 1210 rv++; 1211 } 1212 } 1213 ndbfree(nt); 1214 return rv; 1215 } else { 1216 /* 1217 * not a known network, don't translate host or service 1218 */ 1219 if(mf->serv) 1220 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s", 1221 mntpt, mf->net, mf->host, mf->serv); 1222 else 1223 snprint(reply, sizeof(reply), "%s/%s/clone %s", 1224 mntpt, mf->net, mf->host); 1225 mf->reply[0] = strdup(reply); 1226 mf->replylen[0] = strlen(reply); 1227 mf->nreply = 1; 1228 return 1; 1229 } 1230 } 1231 1232 /* 1233 * translate an ip service name into a port number. If it's a numeric port 1234 * number, look for restricted access. 1235 * 1236 * the service '*' needs no translation. 1237 */ 1238 char* 1239 ipserv(Network *np, char *name, char *buf) 1240 { 1241 char *p; 1242 int alpha = 0; 1243 int restr = 0; 1244 char port[Ndbvlen]; 1245 Ndbtuple *t, *nt; 1246 Ndbs s; 1247 1248 /* '*' means any service */ 1249 if(strcmp(name, "*")==0){ 1250 strcpy(buf, name); 1251 return buf; 1252 } 1253 1254 /* see if it's numeric or symbolic */ 1255 port[0] = 0; 1256 for(p = name; *p; p++){ 1257 if(isdigit(*p)) 1258 {} 1259 else if(isalpha(*p) || *p == '-' || *p == '$') 1260 alpha = 1; 1261 else 1262 return 0; 1263 } 1264 if(alpha){ 1265 t = ndbgetval(db, &s, np->net, name, "port", port); 1266 if(t == 0) 1267 return 0; 1268 } else { 1269 t = ndbgetval(db, &s, "port", name, "port", port); 1270 if(t == 0){ 1271 strncpy(port, name, sizeof(port)); 1272 port[sizeof(port)-1] = 0; 1273 } 1274 } 1275 1276 if(t){ 1277 for(nt = t; nt; nt = nt->entry) 1278 if(strcmp(nt->attr, "restricted") == 0) 1279 restr = 1; 1280 ndbfree(t); 1281 } 1282 sprint(buf, "%s%s", port, restr ? "!r" : ""); 1283 return buf; 1284 } 1285 1286 /* 1287 * lookup an ip attribute 1288 */ 1289 int 1290 ipattrlookup(Ndb *db, char *ipa, char *attr, char *val) 1291 { 1292 1293 Ndbtuple *t, *nt; 1294 char *alist[2]; 1295 1296 alist[0] = attr; 1297 t = ndbipinfo(db, "ip", ipa, alist, 1); 1298 if(t == nil) 1299 return 0; 1300 for(nt = t; nt != nil; nt = nt->entry){ 1301 if(strcmp(nt->attr, attr) == 0){ 1302 strcpy(val, nt->val); 1303 ndbfree(t); 1304 return 1; 1305 } 1306 } 1307 1308 /* we shouldn't get here */ 1309 ndbfree(t); 1310 return 0; 1311 } 1312 1313 /* 1314 * lookup (and translate) an ip destination 1315 */ 1316 Ndbtuple* 1317 iplookup(Network *np, char *host, char *serv, int nolookup) 1318 { 1319 char *attr; 1320 Ndbtuple *t, *nt; 1321 Ndbs s; 1322 char ts[Ndbvlen+1]; 1323 char th[Ndbvlen+1]; 1324 char dollar[Ndbvlen+1]; 1325 uchar ip[IPaddrlen]; 1326 uchar net[IPaddrlen]; 1327 uchar tnet[IPaddrlen]; 1328 Ipifc *ifc; 1329 Iplifc *lifc; 1330 1331 USED(nolookup); 1332 1333 /* 1334 * start with the service since it's the most likely to fail 1335 * and costs the least 1336 */ 1337 if(serv==0 || ipserv(np, serv, ts) == 0){ 1338 werrstr("can't translate address"); 1339 return 0; 1340 } 1341 1342 /* for dial strings with no host */ 1343 if(strcmp(host, "*") == 0) 1344 return mktuple("ip", "*"); 1345 1346 /* 1347 * hack till we go v6 :: = 0.0.0.0 1348 */ 1349 if(strcmp("::", host) == 0) 1350 return mktuple("ip", "*"); 1351 1352 /* 1353 * '$' means the rest of the name is an attribute that we 1354 * need to search for 1355 */ 1356 if(*host == '$'){ 1357 if(ipattrlookup(db, ipaddr, host+1, dollar)) 1358 host = dollar; 1359 } 1360 1361 /* 1362 * turn '[ip address]' into just 'ip address' 1363 */ 1364 if(*host == '[' && host[strlen(host)-1] == ']'){ 1365 host++; 1366 host[strlen(host)-1] = 0; 1367 } 1368 1369 /* 1370 * just accept addresses 1371 */ 1372 attr = ipattr(host); 1373 if(strcmp(attr, "ip") == 0) 1374 return mktuple("ip", host); 1375 1376 /* 1377 * give the domain name server the first opportunity to 1378 * resolve domain names. if that fails try the database. 1379 */ 1380 t = 0; 1381 if(strcmp(attr, "dom") == 0) 1382 t = dnsiplookup(host, &s); 1383 if(t == 0) 1384 t = ndbgetval(db, &s, attr, host, "ip", th); 1385 if(t == 0) 1386 t = dnsiplookup(host, &s); 1387 if(t == 0 && strcmp(attr, "dom") != 0) 1388 t = dnsiplookup(host, &s); 1389 if(t == 0) 1390 return 0; 1391 1392 /* 1393 * reorder the tuple to have the matched line first and 1394 * save that in the request structure. 1395 */ 1396 t = reorder(t, s.t); 1397 1398 /* 1399 * reorder according to our interfaces 1400 */ 1401 lock(&ipifclock); 1402 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){ 1403 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){ 1404 maskip(lifc->ip, lifc->mask, net); 1405 for(nt = t; nt; nt = nt->entry){ 1406 if(strcmp(nt->attr, "ip") != 0) 1407 continue; 1408 parseip(ip, nt->val); 1409 maskip(ip, lifc->mask, tnet); 1410 if(memcmp(net, tnet, IPaddrlen) == 0){ 1411 t = reorder(t, nt); 1412 unlock(&ipifclock); 1413 return t; 1414 } 1415 } 1416 } 1417 } 1418 unlock(&ipifclock); 1419 1420 return t; 1421 } 1422 1423 /* 1424 * translate an ip address 1425 */ 1426 char* 1427 iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int hack) 1428 { 1429 char ts[Ndbvlen+1]; 1430 char reply[Maxreply]; 1431 char x[Ndbvlen+1]; 1432 1433 if(strcmp(t->attr, "ip") != 0) 1434 return 0; 1435 1436 if(serv == 0 || ipserv(np, serv, ts) == 0) 1437 return 0; 1438 1439 if(rem != nil) 1440 snprint(x, sizeof(x), "!%s", rem); 1441 else 1442 *x = 0; 1443 1444 if(*t->val == '*') 1445 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", 1446 mntpt, np->net, ts, x); 1447 else 1448 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s", 1449 mntpt, np->net, t->val, ts, x, hack?"!fasttimeout":""); 1450 1451 return strdup(reply); 1452 } 1453 1454 /* 1455 * lookup a telephone number 1456 */ 1457 Ndbtuple* 1458 telcolookup(Network *np, char *host, char *serv, int nolookup) 1459 { 1460 Ndbtuple *t; 1461 Ndbs s; 1462 char th[Ndbvlen+1]; 1463 1464 USED(np, nolookup, serv); 1465 1466 t = ndbgetval(db, &s, "sys", host, "telco", th); 1467 if(t == 0) 1468 return mktuple("telco", host); 1469 1470 return reorder(t, s.t); 1471 } 1472 1473 /* 1474 * translate a telephone address 1475 */ 1476 char* 1477 telcotrans(Ndbtuple *t, Network *np, char *serv, char *rem, int) 1478 { 1479 char reply[Maxreply]; 1480 char x[Ndbvlen+1]; 1481 1482 if(strcmp(t->attr, "telco") != 0) 1483 return 0; 1484 1485 if(rem != nil) 1486 snprint(x, sizeof(x), "!%s", rem); 1487 else 1488 *x = 0; 1489 if(serv) 1490 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net, 1491 t->val, serv, x); 1492 else 1493 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net, 1494 t->val, x); 1495 return strdup(reply); 1496 } 1497 1498 /* 1499 * reorder the tuple to put x's line first in the entry 1500 */ 1501 Ndbtuple* 1502 reorder(Ndbtuple *t, Ndbtuple *x) 1503 { 1504 Ndbtuple *nt; 1505 Ndbtuple *line; 1506 1507 /* find start of this entry's line */ 1508 for(line = x; line->entry == line->line; line = line->line) 1509 ; 1510 line = line->line; 1511 if(line == t) 1512 return t; /* already the first line */ 1513 1514 /* remove this line and everything after it from the entry */ 1515 for(nt = t; nt->entry != line; nt = nt->entry) 1516 ; 1517 nt->entry = 0; 1518 1519 /* make that the start of the entry */ 1520 for(nt = line; nt->entry; nt = nt->entry) 1521 ; 1522 nt->entry = t; 1523 return line; 1524 } 1525 1526 /* 1527 * create a slave process to handle a request to avoid one request blocking 1528 * another. parent returns to job loop. 1529 */ 1530 void 1531 slave(void) 1532 { 1533 if(*isslave) 1534 return; /* we're already a slave process */ 1535 1536 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){ 1537 case -1: 1538 break; 1539 case 0: 1540 if(debug) 1541 syslog(0, logfile, "slave %d", getpid()); 1542 *isslave = 1; 1543 break; 1544 default: 1545 longjmp(masterjmp, 1); 1546 } 1547 1548 } 1549 1550 /* 1551 * call the dns process and have it try to translate a name 1552 */ 1553 Ndbtuple* 1554 dnsiplookup(char *host, Ndbs *s) 1555 { 1556 char buf[Ndbvlen + 4]; 1557 Ndbtuple *t; 1558 1559 unlock(&dblock); 1560 1561 /* save the name before starting a slave */ 1562 snprint(buf, sizeof(buf), "%s", host); 1563 1564 slave(); 1565 1566 if(strcmp(ipattr(buf), "ip") == 0) 1567 t = dnsquery(mntpt, buf, "ptr"); 1568 else 1569 t = dnsquery(mntpt, buf, "ip"); 1570 s->t = t; 1571 1572 lock(&dblock); 1573 return t; 1574 } 1575 1576 int 1577 qmatch(Ndbtuple *t, char **attr, char **val, int n) 1578 { 1579 int i, found; 1580 Ndbtuple *nt; 1581 1582 for(i = 1; i < n; i++){ 1583 found = 0; 1584 for(nt = t; nt; nt = nt->entry) 1585 if(strcmp(attr[i], nt->attr) == 0) 1586 if(strcmp(val[i], "*") == 0 1587 || strcmp(val[i], nt->val) == 0){ 1588 found = 1; 1589 break; 1590 } 1591 if(found == 0) 1592 break; 1593 } 1594 return i == n; 1595 } 1596 1597 void 1598 qreply(Mfile *mf, Ndbtuple *t) 1599 { 1600 int i; 1601 Ndbtuple *nt; 1602 char buf[2048]; 1603 1604 buf[0] = 0; 1605 for(nt = t; mf->nreply < Nreply && nt; nt = nt->entry){ 1606 strcat(buf, nt->attr); 1607 strcat(buf, "="); 1608 strcat(buf, nt->val); 1609 i = strlen(buf); 1610 if(nt->line != nt->entry || sizeof(buf) - i < 2*Ndbvlen+2){ 1611 mf->replylen[mf->nreply] = strlen(buf); 1612 mf->reply[mf->nreply++] = strdup(buf); 1613 buf[0] = 0; 1614 } else 1615 strcat(buf, " "); 1616 } 1617 } 1618 1619 enum 1620 { 1621 Maxattr= 32, 1622 }; 1623 1624 /* 1625 * generic query lookup. The query is of one of the following 1626 * forms: 1627 * 1628 * attr1=val1 attr2=val2 attr3=val3 ... 1629 * 1630 * returns the matching tuple 1631 * 1632 * ipinfo attr=val attr1 attr2 attr3 ... 1633 * 1634 * is like ipinfo and returns the attr{1-n} 1635 * associated with the ip address. 1636 */ 1637 char* 1638 genquery(Mfile *mf, char *query) 1639 { 1640 int i, n; 1641 char *p; 1642 char *attr[Maxattr]; 1643 char *val[Maxattr]; 1644 Ndbtuple *t; 1645 Ndbs s; 1646 1647 n = getfields(query, attr, 32, 1, " "); 1648 if(n == 0) 1649 return "bad query"; 1650 1651 if(strcmp(attr[0], "ipinfo") == 0) 1652 return ipinfoquery(mf, attr, n); 1653 1654 /* parse pairs */ 1655 for(i = 0; i < n; i++){ 1656 p = strchr(attr[i], '='); 1657 if(p == 0) 1658 return "bad query"; 1659 *p++ = 0; 1660 val[i] = p; 1661 } 1662 1663 /* give dns a chance */ 1664 if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){ 1665 t = dnsiplookup(val[0], &s); 1666 if(t){ 1667 if(qmatch(t, attr, val, n)){ 1668 qreply(mf, t); 1669 ndbfree(t); 1670 return 0; 1671 } 1672 ndbfree(t); 1673 } 1674 } 1675 1676 /* first pair is always the key. It can't be a '*' */ 1677 t = ndbsearch(db, &s, attr[0], val[0]); 1678 1679 /* search is the and of all the pairs */ 1680 while(t){ 1681 if(qmatch(t, attr, val, n)){ 1682 qreply(mf, t); 1683 ndbfree(t); 1684 return 0; 1685 } 1686 1687 ndbfree(t); 1688 t = ndbsnext(&s, attr[0], val[0]); 1689 } 1690 1691 return "no match"; 1692 } 1693 1694 /* 1695 * resolve an ip address 1696 */ 1697 static Ndbtuple* 1698 ipresolve(char *attr, char *host) 1699 { 1700 Ndbtuple *t, *nt, **l; 1701 1702 t = iplookup(&network[Ntcp], host, "*", 0); 1703 for(l = &t; *l != nil; ){ 1704 nt = *l; 1705 if(strcmp(nt->attr, "ip") != 0){ 1706 *l = nt->entry; 1707 nt->entry = nil; 1708 ndbfree(nt); 1709 continue; 1710 } 1711 strcpy(nt->attr, attr); 1712 l = &nt->entry; 1713 } 1714 return t; 1715 } 1716 1717 char* 1718 ipinfoquery(Mfile *mf, char **list, int n) 1719 { 1720 int i, nresolve; 1721 int resolve[Maxattr]; 1722 Ndbtuple *t, *nt, **l; 1723 char *attr, *val; 1724 1725 /* skip 'ipinfo' */ 1726 list++; n--; 1727 1728 if(n < 2) 1729 return "bad query"; 1730 1731 /* get search attribute=value */ 1732 attr = *list++; n--; 1733 val = strchr(attr, '='); 1734 if(val == nil) 1735 return "bad query"; 1736 *val++ = 0; 1737 1738 /* 1739 * don't let ndbipinfo resolve the addresses, we're 1740 * better at it. 1741 */ 1742 nresolve = 0; 1743 for(i = 0; i < n; i++) 1744 if(*list[i] == '@'){ 1745 list[i]++; 1746 resolve[i] = 1; 1747 nresolve++; 1748 } else 1749 resolve[i] = 0; 1750 1751 t = ndbipinfo(db, attr, val, list, n); 1752 if(t == nil) 1753 return "no match"; 1754 1755 if(nresolve != 0){ 1756 for(l = &t; *l != nil;){ 1757 nt = *l; 1758 1759 /* already an address? */ 1760 if(strcmp(ipattr(nt->val), "ip") == 0){ 1761 l = &(*l)->entry; 1762 continue; 1763 } 1764 1765 /* user wants it resolved? */ 1766 for(i = 0; i < n; i++) 1767 if(strcmp(list[i], nt->attr) == 0) 1768 break; 1769 if(i >= n || resolve[i] == 0){ 1770 l = &(*l)->entry; 1771 continue; 1772 } 1773 1774 /* resolve address and replace entry */ 1775 *l = ipresolve(nt->attr, nt->val); 1776 while(*l != nil) 1777 l = &(*l)->entry; 1778 *l = nt->entry; 1779 1780 nt->entry = nil; 1781 ndbfree(nt); 1782 } 1783 } 1784 1785 /* make it all one line */ 1786 for(nt = t; nt != nil; nt = nt->entry){ 1787 if(nt->entry == nil) 1788 nt->line = t; 1789 else 1790 nt->line = nt->entry; 1791 } 1792 1793 qreply(mf, t); 1794 1795 return nil; 1796 } 1797 1798 void* 1799 emalloc(int size) 1800 { 1801 void *x; 1802 1803 x = malloc(size); 1804 if(x == nil) 1805 abort(); 1806 memset(x, 0, size); 1807 return x; 1808 } 1809 1810 char* 1811 estrdup(char *s) 1812 { 1813 int size; 1814 char *p; 1815 1816 size = strlen(s)+1; 1817 p = malloc(size); 1818 if(p == nil) 1819 abort(); 1820 memmove(p, s, size); 1821 return p; 1822 } 1823