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