1 #include "common.h" 2 #include <auth.h> 3 #include <ndb.h> 4 5 /* 6 * number of predefined fd's 7 */ 8 int nsysfile=3; 9 10 static char err[Errlen]; 11 12 /* 13 * return the date 14 */ 15 extern char * 16 thedate(void) 17 { 18 static char now[64]; 19 char *cp; 20 21 strcpy(now, ctime(time(0))); 22 cp = strchr(now, '\n'); 23 if(cp) 24 *cp = 0; 25 return now; 26 } 27 28 /* 29 * return the user id of the current user 30 */ 31 extern char * 32 getlog(void) 33 { 34 static char user[64]; 35 int fd; 36 int n; 37 38 fd = open("/dev/user", 0); 39 if(fd < 0) 40 return nil; 41 if((n=read(fd, user, sizeof(user)-1)) <= 0) 42 return nil; 43 close(fd); 44 user[n] = 0; 45 return user; 46 } 47 48 /* 49 * return the lock name 50 */ 51 static String * 52 lockname(char *path) 53 { 54 String *lp; 55 char *cp; 56 57 /* 58 * get the name of the lock file 59 */ 60 lp = s_new(); 61 cp = strrchr(path, '/'); 62 if(cp){ 63 cp++; 64 s_nappend(lp, path, cp - path); 65 } else { 66 67 cp = path; 68 } 69 s_append(lp, "L."); 70 s_nappend(lp, cp, Elemlen-3); 71 72 return lp; 73 } 74 75 int 76 syscreatelocked(char *path, int mode, int perm) 77 { 78 return create(path, mode, DMEXCL|perm); 79 } 80 81 int 82 sysopenlocked(char *path, int mode) 83 { 84 /* return open(path, OEXCL|mode);/**/ 85 return open(path, mode); /* until system call is fixed */ 86 } 87 88 int 89 sysunlockfile(int fd) 90 { 91 return close(fd); 92 } 93 94 /* 95 * try opening a lock file. If it doesn't exist try creating it. 96 */ 97 static int 98 openlockfile(Mlock *l) 99 { 100 int fd; 101 Dir *d; 102 Dir nd; 103 char *p; 104 105 fd = open(s_to_c(l->name), OREAD); 106 if(fd >= 0){ 107 l->fd = fd; 108 return 0; 109 } 110 111 d = dirstat(s_to_c(l->name)); 112 if(d == nil){ 113 /* file doesn't exist */ 114 /* try creating it */ 115 fd = create(s_to_c(l->name), OREAD, DMEXCL|0666); 116 if(fd >= 0){ 117 nulldir(&nd); 118 nd.mode = DMEXCL|0666; 119 dirfwstat(fd, &nd); 120 l->fd = fd; 121 return 0; 122 } 123 124 /* couldn't create */ 125 /* do we have write access to the directory? */ 126 p = strrchr(s_to_c(l->name), '/'); 127 if(p != 0){ 128 *p = 0; 129 fd = access(s_to_c(l->name), 2); 130 *p = '/'; 131 if(fd < 0) 132 return -1; /* give up */ 133 } else { 134 fd = access(".", 2); 135 if(fd < 0) 136 return -1; /* give up */ 137 } 138 } else 139 free(d); 140 141 return 1; /* try again later */ 142 } 143 144 #define LSECS 5*60 145 146 /* 147 * Set a lock for a particular file. The lock is a file in the same directory 148 * and has L. prepended to the name of the last element of the file name. 149 */ 150 extern Mlock * 151 syslock(char *path) 152 { 153 Mlock *l; 154 int tries; 155 156 l = mallocz(sizeof(Mlock), 1); 157 if(l == 0) 158 return nil; 159 160 l->name = lockname(path); 161 162 /* 163 * wait LSECS seconds for it to unlock 164 */ 165 for(tries = 0; tries < LSECS*2; tries++){ 166 switch(openlockfile(l)){ 167 case 0: 168 return l; 169 case 1: 170 sleep(500); 171 break; 172 default: 173 goto noway; 174 } 175 } 176 177 noway: 178 s_free(l->name); 179 free(l); 180 return nil; 181 } 182 183 /* 184 * like lock except don't wait 185 */ 186 extern Mlock * 187 trylock(char *path) 188 { 189 Mlock *l; 190 char buf[1]; 191 int fd; 192 193 l = malloc(sizeof(Mlock)); 194 if(l == 0) 195 return 0; 196 197 l->name = lockname(path); 198 if(openlockfile(l) != 0){ 199 s_free(l->name); 200 free(l); 201 return 0; 202 } 203 204 /* fork process to keep lock alive */ 205 switch(l->pid = rfork(RFPROC)){ 206 default: 207 break; 208 case 0: 209 fd = l->fd; 210 for(;;){ 211 sleep(1000*60); 212 if(pread(fd, buf, 1, 0) < 0) 213 break; 214 } 215 _exits(0); 216 } 217 return l; 218 } 219 220 extern void 221 syslockrefresh(Mlock *l) 222 { 223 char buf[1]; 224 225 pread(l->fd, buf, 1, 0); 226 } 227 228 extern void 229 sysunlock(Mlock *l) 230 { 231 if(l == 0) 232 return; 233 if(l->name){ 234 s_free(l->name); 235 } 236 if(l->fd >= 0) 237 close(l->fd); 238 if(l->pid > 0) 239 postnote(PNPROC, l->pid, "time to die"); 240 free(l); 241 } 242 243 /* 244 * Open a file. The modes are: 245 * 246 * l - locked 247 * a - set append permissions 248 * r - readable 249 * w - writable 250 * A - append only (doesn't exist in Bio) 251 */ 252 extern Biobuf * 253 sysopen(char *path, char *mode, ulong perm) 254 { 255 int sysperm; 256 int sysmode; 257 int fd; 258 int docreate; 259 int append; 260 int truncate; 261 Dir *d, nd; 262 Biobuf *bp; 263 264 /* 265 * decode the request 266 */ 267 sysperm = 0; 268 sysmode = -1; 269 docreate = 0; 270 append = 0; 271 truncate = 0; 272 for(; mode && *mode; mode++) 273 switch(*mode){ 274 case 'A': 275 sysmode = OWRITE; 276 append = 1; 277 break; 278 case 'c': 279 docreate = 1; 280 break; 281 case 'l': 282 sysperm |= DMEXCL; 283 break; 284 case 'a': 285 sysperm |= DMAPPEND; 286 break; 287 case 'w': 288 if(sysmode == -1) 289 sysmode = OWRITE; 290 else 291 sysmode = ORDWR; 292 break; 293 case 'r': 294 if(sysmode == -1) 295 sysmode = OREAD; 296 else 297 sysmode = ORDWR; 298 break; 299 case 't': 300 truncate = 1; 301 break; 302 default: 303 break; 304 } 305 switch(sysmode){ 306 case OREAD: 307 case OWRITE: 308 case ORDWR: 309 break; 310 default: 311 if(sysperm&DMAPPEND) 312 sysmode = OWRITE; 313 else 314 sysmode = OREAD; 315 break; 316 } 317 318 /* 319 * create file if we need to 320 */ 321 if(truncate) 322 sysmode |= OTRUNC; 323 fd = open(path, sysmode); 324 if(fd < 0){ 325 d = dirstat(path); 326 if(d == nil){ 327 if(docreate == 0) 328 return 0; 329 330 fd = create(path, sysmode, sysperm|perm); 331 if(fd < 0) 332 return 0; 333 nulldir(&nd); 334 nd.mode = sysperm|perm; 335 dirfwstat(fd, &nd); 336 } else { 337 free(d); 338 return 0; 339 } 340 } 341 342 bp = (Biobuf*)malloc(sizeof(Biobuf)); 343 if(bp == 0){ 344 close(fd); 345 return 0; 346 } 347 memset(bp, 0, sizeof(Biobuf)); 348 Binit(bp, fd, sysmode&~OTRUNC); 349 350 if(append) 351 Bseek(bp, 0, 2); 352 return bp; 353 } 354 355 /* 356 * close the file, etc. 357 */ 358 int 359 sysclose(Biobuf *bp) 360 { 361 int rv; 362 363 rv = Bterm(bp); 364 close(Bfildes(bp)); 365 free(bp); 366 return rv; 367 } 368 369 /* 370 * create a file 371 */ 372 int 373 syscreate(char *file, int mode, ulong perm) 374 { 375 return create(file, mode, perm); 376 } 377 378 /* 379 * make a directory 380 */ 381 int 382 sysmkdir(char *file, ulong perm) 383 { 384 int fd; 385 386 if((fd = create(file, OREAD, DMDIR|perm)) < 0) 387 return -1; 388 close(fd); 389 return 0; 390 } 391 392 /* 393 * change the group of a file 394 */ 395 int 396 syschgrp(char *file, char *group) 397 { 398 Dir nd; 399 400 if(group == 0) 401 return -1; 402 nulldir(&nd); 403 nd.gid = group; 404 return dirwstat(file, &nd); 405 } 406 407 extern int 408 sysdirreadall(int fd, Dir **d) 409 { 410 return dirreadall(fd, d); 411 } 412 413 /* 414 * read in the system name 415 */ 416 extern char * 417 sysname_read(void) 418 { 419 static char name[128]; 420 char *cp; 421 422 cp = getenv("site"); 423 if(cp == 0 || *cp == 0) 424 cp = alt_sysname_read(); 425 if(cp == 0 || *cp == 0) 426 cp = "kremvax"; 427 strcpy(name, cp); 428 return name; 429 } 430 extern char * 431 alt_sysname_read(void) 432 { 433 static char name[128]; 434 int n, fd; 435 436 fd = open("/dev/sysname", OREAD); 437 if(fd < 0) 438 return 0; 439 n = read(fd, name, sizeof(name)-1); 440 close(fd); 441 if(n <= 0) 442 return 0; 443 name[n] = 0; 444 return name; 445 } 446 447 /* 448 * get all names 449 */ 450 extern char** 451 sysnames_read(void) 452 { 453 static char **namev; 454 Ndbtuple *t, *nt; 455 char domain[Ndbvlen]; 456 int n; 457 char *cp; 458 459 if(namev) 460 return namev; 461 462 t = csgetval(0, "sys", alt_sysname_read(), "dom", domain); 463 n = 0; 464 for(nt = t; nt; nt = nt->entry) 465 if(strcmp(nt->attr, "dom") == 0) 466 n++; 467 468 namev = (char**)malloc(sizeof(char *)*(n+3)); 469 470 if(namev){ 471 n = 0; 472 namev[n++] = strdup(sysname_read()); 473 cp = alt_sysname_read(); 474 if(cp) 475 namev[n++] = strdup(cp); 476 for(nt = t; nt; nt = nt->entry) 477 if(strcmp(nt->attr, "dom") == 0) 478 namev[n++] = strdup(nt->val); 479 namev[n] = 0; 480 } 481 if(t) 482 ndbfree(t); 483 484 return namev; 485 } 486 487 /* 488 * read in the domain name 489 */ 490 extern char * 491 domainname_read(void) 492 { 493 char **namev; 494 495 for(namev = sysnames_read(); *namev; namev++) 496 if(strchr(*namev, '.')) 497 return *namev; 498 return 0; 499 } 500 501 /* 502 * return true if the last error message meant file 503 * did not exist. 504 */ 505 extern int 506 e_nonexistent(void) 507 { 508 rerrstr(err, sizeof(err)); 509 return strcmp(err, "file does not exist") == 0; 510 } 511 512 /* 513 * return true if the last error message meant file 514 * was locked. 515 */ 516 extern int 517 e_locked(void) 518 { 519 rerrstr(err, sizeof(err)); 520 return strcmp(err, "open/create -- file is locked") == 0; 521 } 522 523 /* 524 * return the length of a file 525 */ 526 extern long 527 sysfilelen(Biobuf *fp) 528 { 529 Dir *d; 530 long rv; 531 532 d = dirfstat(Bfildes(fp)); 533 if(d == nil) 534 return -1; 535 rv = d->length; 536 free(d); 537 return rv; 538 } 539 540 /* 541 * remove a file 542 */ 543 extern int 544 sysremove(char *path) 545 { 546 return remove(path); 547 } 548 549 /* 550 * rename a file, fails unless both are in the same directory 551 */ 552 extern int 553 sysrename(char *old, char *new) 554 { 555 Dir d; 556 char *obase; 557 char *nbase; 558 int rv; 559 560 obase = strrchr(old, '/'); 561 nbase = strrchr(new, '/'); 562 if(obase){ 563 if(nbase == 0) 564 return -1; 565 if(strncmp(old, new, obase-old) != 0) 566 return -1; 567 nbase++; 568 } else { 569 if(nbase) 570 return -1; 571 nbase = new; 572 } 573 nulldir(&d); 574 d.name = nbase; 575 return dirwstat(old, &d); 576 } 577 578 /* 579 * see if a file exists 580 */ 581 extern int 582 sysexist(char *file) 583 { 584 Dir *d; 585 586 d = dirstat(file); 587 if(d == nil) 588 return 0; 589 free(d); 590 return 1; 591 } 592 593 /* 594 * kill a process or process group 595 */ 596 597 static int 598 stomp(int pid, char *file) 599 { 600 char name[64]; 601 int fd; 602 603 snprint(name, sizeof(name), "/proc/%d/%s", pid, file); 604 fd = open(name, 1); 605 if(fd < 0) 606 return -1; 607 if(write(fd, "die: yankee pig dog\n", sizeof("die: yankee pig dog\n") - 1) <= 0){ 608 close(fd); 609 return -1; 610 } 611 close(fd); 612 return 0; 613 614 } 615 616 /* 617 * kill a process 618 */ 619 extern int 620 syskill(int pid) 621 { 622 return stomp(pid, "note"); 623 624 } 625 626 /* 627 * kill a process group 628 */ 629 extern int 630 syskillpg(int pid) 631 { 632 return stomp(pid, "notepg"); 633 } 634 635 extern int 636 sysdetach(void) 637 { 638 if(rfork(RFENVG|RFNAMEG|RFNOTEG) < 0) { 639 werrstr("rfork failed"); 640 return -1; 641 } 642 return 0; 643 } 644 645 /* 646 * catch a write on a closed pipe 647 */ 648 static int *closedflag; 649 static int 650 catchpipe(void *a, char *msg) 651 { 652 static char *foo = "sys: write on closed pipe"; 653 654 USED(a); 655 if(strncmp(msg, foo, strlen(foo)) == 0){ 656 if(closedflag) 657 *closedflag = 1; 658 return 1; 659 } 660 return 0; 661 } 662 void 663 pipesig(int *flagp) 664 { 665 closedflag = flagp; 666 atnotify(catchpipe, 1); 667 } 668 void 669 pipesigoff(void) 670 { 671 atnotify(catchpipe, 0); 672 } 673 674 void 675 exit(int i) 676 { 677 char buf[32]; 678 679 if(i == 0) 680 exits(0); 681 snprint(buf, sizeof(buf), "%d", i); 682 exits(buf); 683 } 684 685 static int 686 islikeatty(int fd) 687 { 688 Dir *d; 689 int rv; 690 691 d = dirfstat(fd); 692 if(d == nil) 693 return 0; 694 rv = strcmp(d->name, "cons") == 0; 695 free(d); 696 return rv; 697 } 698 699 extern int 700 holdon(void) 701 { 702 int fd; 703 704 if(!islikeatty(0)) 705 return -1; 706 707 fd = open("/dev/consctl", OWRITE); 708 write(fd, "holdon", 6); 709 710 return fd; 711 } 712 713 extern int 714 sysopentty(void) 715 { 716 return open("/dev/cons", ORDWR); 717 } 718 719 extern void 720 holdoff(int fd) 721 { 722 write(fd, "holdoff", 7); 723 close(fd); 724 } 725 726 extern int 727 sysfiles(void) 728 { 729 return 128; 730 } 731 732 /* 733 * expand a path relative to the user's mailbox directory 734 * 735 * if the path starts with / or ./, don't change it 736 * 737 */ 738 extern String * 739 mboxpath(char *path, char *user, String *to, int dot) 740 { 741 if (dot || *path=='/' || strncmp(path, "./", 2) == 0 742 || strncmp(path, "../", 3) == 0) { 743 to = s_append(to, path); 744 } else { 745 to = s_append(to, MAILROOT); 746 to = s_append(to, "/box/"); 747 to = s_append(to, user); 748 to = s_append(to, "/"); 749 to = s_append(to, path); 750 } 751 return to; 752 } 753 754 extern String * 755 mboxname(char *user, String *to) 756 { 757 return mboxpath("mbox", user, to, 0); 758 } 759 760 extern String * 761 deadletter(String *to) /* pass in sender??? */ 762 { 763 char *cp; 764 765 cp = getlog(); 766 if(cp == 0) 767 return 0; 768 return mboxpath("dead.letter", cp, to, 0); 769 } 770 771 char * 772 homedir(char *user) 773 { 774 USED(user); 775 return getenv("home"); 776 } 777 778 String * 779 readlock(String *file) 780 { 781 char *cp; 782 783 cp = getlog(); 784 if(cp == 0) 785 return 0; 786 return mboxpath("reading", cp, file, 0); 787 } 788 789 String * 790 username(String *from) 791 { 792 int n; 793 Biobuf *bp; 794 char *p, *q; 795 String *s; 796 797 bp = Bopen("/adm/keys.who", OREAD); 798 if(bp == 0) 799 bp = Bopen("/adm/netkeys.who", OREAD); 800 if(bp == 0) 801 return 0; 802 803 s = 0; 804 n = strlen(s_to_c(from)); 805 for(;;) { 806 p = Brdline(bp, '\n'); 807 if(p == 0) 808 break; 809 p[Blinelen(bp)-1] = 0; 810 if(strncmp(p, s_to_c(from), n)) 811 continue; 812 p += n; 813 if(*p != ' ' && *p != '\t') /* must be full match */ 814 continue; 815 while(*p && (*p == ' ' || *p == '\t')) 816 p++; 817 if(*p == 0) 818 continue; 819 for(q = p; *q; q++) 820 if(('0' <= *q && *q <= '9') || *q == '<') 821 break; 822 while(q > p && q[-1] != ' ' && q[-1] != '\t') 823 q--; 824 while(q > p && (q[-1] == ' ' || q[-1] == '\t')) 825 q--; 826 *q = 0; 827 s = s_new(); 828 s_append(s, "\""); 829 s_append(s, p); 830 s_append(s, "\""); 831 break; 832 } 833 Bterm(bp); 834 return s; 835 } 836 837 char * 838 remoteaddr(int fd, char *dir) 839 { 840 char buf[128], *p; 841 int n; 842 843 if(dir == 0){ 844 if(fd2path(fd, buf, sizeof(buf)) != 0) 845 return ""; 846 847 /* parse something of the form /net/tcp/nnnn/data */ 848 p = strrchr(buf, '/'); 849 if(p == 0) 850 return ""; 851 strncpy(p+1, "remote", sizeof(buf)-(p-buf)-2); 852 } else 853 snprint(buf, sizeof buf, "%s/remote", dir); 854 buf[sizeof(buf)-1] = 0; 855 856 fd = open(buf, OREAD); 857 if(fd < 0) 858 return ""; 859 n = read(fd, buf, sizeof(buf)-1); 860 close(fd); 861 if(n > 0){ 862 buf[n] = 0; 863 p = strchr(buf, '!'); 864 if(p) 865 *p = 0; 866 return strdup(buf); 867 } 868 return ""; 869 } 870 871 // create a mailbox 872 int 873 creatembox(char *user, char *folder) 874 { 875 char *p; 876 String *mailfile; 877 char buf[512]; 878 int fd; 879 Dir *d; 880 Mlock *ml; 881 882 mailfile = s_new(); 883 if(folder == 0) 884 mboxname(user, mailfile); 885 else { 886 snprint(buf, sizeof(buf), "%s/mbox", folder); 887 mboxpath(buf, user, mailfile, 0); 888 } 889 890 // don't destroy existing mailbox 891 if(access(s_to_c(mailfile), 0) == 0){ 892 fprint(2, "mailbox already exists\n"); 893 return -1; 894 } 895 fprint(2, "creating new mbox: %s\n", s_to_c(mailfile)); 896 897 // make sure preceding levels exist 898 for(p = s_to_c(mailfile); p; p++) { 899 if(*p == '/') /* skip leading or consecutive slashes */ 900 continue; 901 p = strchr(p, '/'); 902 if(p == 0) 903 break; 904 *p = 0; 905 if(access(s_to_c(mailfile), 0) != 0){ 906 if((fd = create(s_to_c(mailfile), OREAD, DMDIR|0711)) < 0){ 907 fprint(2, "couldn't create %s\n", s_to_c(mailfile)); 908 return -1; 909 } 910 close(fd); 911 } 912 *p = '/'; 913 } 914 915 // create the mbox 916 fd = create(s_to_c(mailfile), OREAD, 0622|DMAPPEND|DMEXCL); 917 if(fd < 0){ 918 fprint(2, "couldn't create %s\n", s_to_c(mailfile)); 919 return -1; 920 } 921 d = dirfstat(fd); 922 if(d == nil){ 923 close(fd); 924 fprint(2, "couldn't chmod %s\n", s_to_c(mailfile)); 925 return -1; 926 } 927 d->mode = 0622|DMAPPEND|DMEXCL; 928 if(dirfwstat(fd, d) < 0) 929 fprint(2, "couldn't chmod %s\n", s_to_c(mailfile)); 930 free(d); 931 close(fd); 932 933 /* 934 * create the lock file if it doesn't exist 935 */ 936 ml = trylock(s_to_c(mailfile)); 937 if(ml != nil) 938 sysunlock(ml); 939 940 return 0; 941 } 942