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 559 obase = strrchr(old, '/'); 560 nbase = strrchr(new, '/'); 561 if(obase){ 562 if(nbase == 0) 563 return -1; 564 if(strncmp(old, new, obase-old) != 0) 565 return -1; 566 nbase++; 567 } else { 568 if(nbase) 569 return -1; 570 nbase = new; 571 } 572 nulldir(&d); 573 d.name = nbase; 574 return dirwstat(old, &d); 575 } 576 577 /* 578 * see if a file exists 579 */ 580 extern int 581 sysexist(char *file) 582 { 583 Dir *d; 584 585 d = dirstat(file); 586 if(d == nil) 587 return 0; 588 free(d); 589 return 1; 590 } 591 592 /* 593 * return nonzero if file is a directory 594 */ 595 extern int 596 sysisdir(char *file) 597 { 598 Dir *d; 599 int rv; 600 601 d = dirstat(file); 602 if(d == nil) 603 return 0; 604 rv = d->mode & DMDIR; 605 free(d); 606 return rv; 607 } 608 609 /* 610 * kill a process or process group 611 */ 612 613 static int 614 stomp(int pid, char *file) 615 { 616 char name[64]; 617 int fd; 618 619 snprint(name, sizeof(name), "/proc/%d/%s", pid, file); 620 fd = open(name, 1); 621 if(fd < 0) 622 return -1; 623 if(write(fd, "die: yankee pig dog\n", sizeof("die: yankee pig dog\n") - 1) <= 0){ 624 close(fd); 625 return -1; 626 } 627 close(fd); 628 return 0; 629 630 } 631 632 /* 633 * kill a process 634 */ 635 extern int 636 syskill(int pid) 637 { 638 return stomp(pid, "note"); 639 640 } 641 642 /* 643 * kill a process group 644 */ 645 extern int 646 syskillpg(int pid) 647 { 648 return stomp(pid, "notepg"); 649 } 650 651 extern int 652 sysdetach(void) 653 { 654 if(rfork(RFENVG|RFNAMEG|RFNOTEG) < 0) { 655 werrstr("rfork failed"); 656 return -1; 657 } 658 return 0; 659 } 660 661 /* 662 * catch a write on a closed pipe 663 */ 664 static int *closedflag; 665 static int 666 catchpipe(void *a, char *msg) 667 { 668 static char *foo = "sys: write on closed pipe"; 669 670 USED(a); 671 if(strncmp(msg, foo, strlen(foo)) == 0){ 672 if(closedflag) 673 *closedflag = 1; 674 return 1; 675 } 676 return 0; 677 } 678 void 679 pipesig(int *flagp) 680 { 681 closedflag = flagp; 682 atnotify(catchpipe, 1); 683 } 684 void 685 pipesigoff(void) 686 { 687 atnotify(catchpipe, 0); 688 } 689 690 void 691 exit(int i) 692 { 693 char buf[32]; 694 695 if(i == 0) 696 exits(0); 697 snprint(buf, sizeof(buf), "%d", i); 698 exits(buf); 699 } 700 701 static int 702 islikeatty(int fd) 703 { 704 Dir *d; 705 int rv; 706 707 d = dirfstat(fd); 708 if(d == nil) 709 return 0; 710 rv = strcmp(d->name, "cons") == 0; 711 free(d); 712 return rv; 713 } 714 715 extern int 716 holdon(void) 717 { 718 int fd; 719 720 if(!islikeatty(0)) 721 return -1; 722 723 fd = open("/dev/consctl", OWRITE); 724 write(fd, "holdon", 6); 725 726 return fd; 727 } 728 729 extern int 730 sysopentty(void) 731 { 732 return open("/dev/cons", ORDWR); 733 } 734 735 extern void 736 holdoff(int fd) 737 { 738 write(fd, "holdoff", 7); 739 close(fd); 740 } 741 742 extern int 743 sysfiles(void) 744 { 745 return 128; 746 } 747 748 /* 749 * expand a path relative to the user's mailbox directory 750 * 751 * if the path starts with / or ./, don't change it 752 * 753 */ 754 extern String * 755 mboxpath(char *path, char *user, String *to, int dot) 756 { 757 if (dot || *path=='/' || strncmp(path, "./", 2) == 0 758 || strncmp(path, "../", 3) == 0) { 759 to = s_append(to, path); 760 } else { 761 to = s_append(to, MAILROOT); 762 to = s_append(to, "/box/"); 763 to = s_append(to, user); 764 to = s_append(to, "/"); 765 to = s_append(to, path); 766 } 767 return to; 768 } 769 770 extern String * 771 mboxname(char *user, String *to) 772 { 773 return mboxpath("mbox", user, to, 0); 774 } 775 776 extern String * 777 deadletter(String *to) /* pass in sender??? */ 778 { 779 char *cp; 780 781 cp = getlog(); 782 if(cp == 0) 783 return 0; 784 return mboxpath("dead.letter", cp, to, 0); 785 } 786 787 char * 788 homedir(char *user) 789 { 790 USED(user); 791 return getenv("home"); 792 } 793 794 String * 795 readlock(String *file) 796 { 797 char *cp; 798 799 cp = getlog(); 800 if(cp == 0) 801 return 0; 802 return mboxpath("reading", cp, file, 0); 803 } 804 805 String * 806 username(String *from) 807 { 808 int n; 809 Biobuf *bp; 810 char *p, *q; 811 String *s; 812 813 bp = Bopen("/adm/keys.who", OREAD); 814 if(bp == 0) 815 bp = Bopen("/adm/netkeys.who", OREAD); 816 if(bp == 0) 817 return 0; 818 819 s = 0; 820 n = strlen(s_to_c(from)); 821 for(;;) { 822 p = Brdline(bp, '\n'); 823 if(p == 0) 824 break; 825 p[Blinelen(bp)-1] = 0; 826 if(strncmp(p, s_to_c(from), n)) 827 continue; 828 p += n; 829 if(*p != ' ' && *p != '\t') /* must be full match */ 830 continue; 831 while(*p && (*p == ' ' || *p == '\t')) 832 p++; 833 if(*p == 0) 834 continue; 835 for(q = p; *q; q++) 836 if(('0' <= *q && *q <= '9') || *q == '<') 837 break; 838 while(q > p && q[-1] != ' ' && q[-1] != '\t') 839 q--; 840 while(q > p && (q[-1] == ' ' || q[-1] == '\t')) 841 q--; 842 *q = 0; 843 s = s_new(); 844 s_append(s, "\""); 845 s_append(s, p); 846 s_append(s, "\""); 847 break; 848 } 849 Bterm(bp); 850 return s; 851 } 852 853 char * 854 remoteaddr(int fd, char *dir) 855 { 856 char buf[128], *p; 857 int n; 858 859 if(dir == 0){ 860 if(fd2path(fd, buf, sizeof(buf)) != 0) 861 return ""; 862 863 /* parse something of the form /net/tcp/nnnn/data */ 864 p = strrchr(buf, '/'); 865 if(p == 0) 866 return ""; 867 strncpy(p+1, "remote", sizeof(buf)-(p-buf)-2); 868 } else 869 snprint(buf, sizeof buf, "%s/remote", dir); 870 buf[sizeof(buf)-1] = 0; 871 872 fd = open(buf, OREAD); 873 if(fd < 0) 874 return ""; 875 n = read(fd, buf, sizeof(buf)-1); 876 close(fd); 877 if(n > 0){ 878 buf[n] = 0; 879 p = strchr(buf, '!'); 880 if(p) 881 *p = 0; 882 return strdup(buf); 883 } 884 return ""; 885 } 886 887 // create a mailbox 888 int 889 creatembox(char *user, char *folder) 890 { 891 char *p; 892 String *mailfile; 893 char buf[512]; 894 int fd; 895 Dir *d; 896 Mlock *ml; 897 898 mailfile = s_new(); 899 if(folder == 0) 900 mboxname(user, mailfile); 901 else { 902 snprint(buf, sizeof(buf), "%s/mbox", folder); 903 mboxpath(buf, user, mailfile, 0); 904 } 905 906 // don't destroy existing mailbox 907 if(access(s_to_c(mailfile), 0) == 0){ 908 fprint(2, "mailbox already exists\n"); 909 return -1; 910 } 911 fprint(2, "creating new mbox: %s\n", s_to_c(mailfile)); 912 913 // make sure preceding levels exist 914 for(p = s_to_c(mailfile); p; p++) { 915 if(*p == '/') /* skip leading or consecutive slashes */ 916 continue; 917 p = strchr(p, '/'); 918 if(p == 0) 919 break; 920 *p = 0; 921 if(access(s_to_c(mailfile), 0) != 0){ 922 if((fd = create(s_to_c(mailfile), OREAD, DMDIR|0711)) < 0){ 923 fprint(2, "couldn't create %s\n", s_to_c(mailfile)); 924 return -1; 925 } 926 close(fd); 927 } 928 *p = '/'; 929 } 930 931 // create the mbox 932 fd = create(s_to_c(mailfile), OREAD, 0622|DMAPPEND|DMEXCL); 933 if(fd < 0){ 934 fprint(2, "couldn't create %s\n", s_to_c(mailfile)); 935 return -1; 936 } 937 d = dirfstat(fd); 938 if(d == nil){ 939 close(fd); 940 fprint(2, "couldn't chmod %s\n", s_to_c(mailfile)); 941 return -1; 942 } 943 d->mode = 0622|DMAPPEND|DMEXCL; 944 if(dirfwstat(fd, d) < 0) 945 fprint(2, "couldn't chmod %s\n", s_to_c(mailfile)); 946 free(d); 947 close(fd); 948 949 /* 950 * create the lock file if it doesn't exist 951 */ 952 ml = trylock(s_to_c(mailfile)); 953 if(ml != nil) 954 sysunlock(ml); 955 956 return 0; 957 } 958