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