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