Blob
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>9 /*10 * number of predefined fd's11 */12 int nsysfile=3;14 static char err[Errlen];16 /*17 * return the date18 */19 extern char *20 thedate(void)21 {22 static char now[64];23 char *cp;25 strcpy(now, ctime(time(0)));26 cp = strchr(now, '\n');27 if(cp)28 *cp = 0;29 return now;30 }32 /*33 * return the user id of the current user34 */35 extern char *36 getlog(void)37 {38 return getuser();39 }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;50 /*51 * get the name of the lock file52 */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");59 return lp;60 }62 int63 syscreatelocked(char *path, int mode, int perm)64 {65 return create(path, mode, DMEXCL|perm);66 }68 int69 sysopenlocked(char *path, int mode)70 {71 /* return open(path, OEXCL|mode);/**/72 return open(path, mode); /* until system call is fixed */73 }75 int76 sysunlockfile(int fd)77 {78 return close(fd);79 }81 /*82 * try opening a lock file. If it doesn't exist try creating it.83 */84 static int85 openlockfile(Mlock *l)86 {87 int fd;88 Dir *d;89 Dir nd;90 char *p;92 fd = open(s_to_c(l->name), OREAD);93 if(fd >= 0){94 l->fd = fd;95 return 0;96 }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 }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 } else137 free(d);139 return 1; /* try again later */140 }142 #define LSECS 5*60144 /*145 * Set a lock for a particular file. The lock is a file in the same directory146 * 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;154 l = mallocz(sizeof(Mlock), 1);155 if(l == 0)156 return nil;158 l->name = lockname(path);160 /*161 * wait LSECS seconds for it to unlock162 */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 }175 noway:176 s_free(l->name);177 free(l);178 return nil;179 }181 /*182 * like lock except don't wait183 */184 extern Mlock *185 trylock(char *path)186 {187 Mlock *l;188 char buf[1];189 int fd;191 l = malloc(sizeof(Mlock));192 if(l == 0)193 return 0;195 l->name = lockname(path);196 if(openlockfile(l) != 0){197 s_free(l->name);198 free(l);199 return 0;200 }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 }218 extern void219 syslockrefresh(Mlock *l)220 {221 char buf[1];223 pread(l->fd, buf, 1, 0);224 }226 extern void227 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 }241 /*242 * Open a file. The modes are:243 *244 * l - locked245 * a - set append permissions246 * r - readable247 * w - writable248 * 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;262 /*263 * decode the request264 */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 else290 sysmode = ORDWR;291 break;292 case 'r':293 if(sysmode == -1)294 sysmode = OREAD;295 else296 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 else313 sysmode = OREAD;314 break;315 }317 /*318 * create file if we need to319 */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;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 }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);349 if(append)350 Bseek(bp, 0, 2);351 return bp;352 }354 /*355 * close the file, etc.356 */357 int358 sysclose(Biobuf *bp)359 {360 int rv;362 rv = Bterm(bp);363 close(Bfildes(bp));364 free(bp);365 return rv;366 }368 /*369 * create a file370 */371 int372 syscreate(char *file, int mode, ulong perm)373 {374 return create(file, mode, perm);375 }377 /*378 * make a directory379 */380 int381 sysmkdir(char *file, ulong perm)382 {383 int fd;385 if((fd = create(file, OREAD, DMDIR|perm)) < 0)386 return -1;387 close(fd);388 return 0;389 }391 /*392 * change the group of a file393 */394 int395 syschgrp(char *file, char *group)396 {397 Dir nd;399 if(group == 0)400 return -1;401 nulldir(&nd);402 nd.gid = group;403 return dirwstat(file, &nd);404 }406 extern int407 sysdirreadall(int fd, Dir **d)408 {409 return dirreadall(fd, d);410 }412 /*413 * read in the system name414 */415 static char *unix_hostname_read(void);416 extern char *417 sysname_read(void)418 {419 static char name[128];420 char *cp;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];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];449 if(gethostname(hostname, sizeof hostname) < 0)450 return nil;451 return hostname;452 }454 /*455 * get all names456 */457 extern char**458 sysnames_read(void)459 {460 static char **namev;461 struct hostent *h;462 char **p, **a;464 if(namev)465 return namev;467 h = gethostbyname(alt_sysname_read());468 if(h == nil)469 return 0;471 for(p=h->h_aliases; *p; p++)472 ;474 namev = malloc((2+p-h->h_aliases)*sizeof namev[0]);475 if(namev == 0)476 return 0;478 a = namev;479 *a++ = strdup(h->h_name);480 for(p=h->h_aliases; *p; p++)481 *a++ = strdup(*p);482 *a = 0;484 return namev;485 }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;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 }510 /*511 * return true if the last error message meant file512 * did not exist.513 */514 extern int515 e_nonexistent(void)516 {517 rerrstr(err, sizeof(err));518 return strcmp(err, "file does not exist") == 0;519 }521 /*522 * return true if the last error message meant file523 * was locked.524 */525 extern int526 e_locked(void)527 {528 rerrstr(err, sizeof(err));529 return strcmp(err, "open/create -- file is locked") == 0;530 }532 /*533 * return the length of a file534 */535 extern long536 sysfilelen(Biobuf *fp)537 {538 Dir *d;539 long rv;541 d = dirfstat(Bfildes(fp));542 if(d == nil)543 return -1;544 rv = d->length;545 free(d);546 return rv;547 }549 /*550 * remove a file551 */552 extern int553 sysremove(char *path)554 {555 return remove(path);556 }558 /*559 * rename a file, fails unless both are in the same directory560 */561 extern int562 sysrename(char *old, char *new)563 {564 Dir d;565 char *obase;566 char *nbase;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 }586 /*587 * see if a file exists588 */589 extern int590 sysexist(char *file)591 {592 Dir *d;594 d = dirstat(file);595 if(d == nil)596 return 0;597 free(d);598 return 1;599 }601 /*602 * return nonzero if file is a directory603 */604 extern int605 sysisdir(char *file)606 {607 Dir *d;608 int rv;610 d = dirstat(file);611 if(d == nil)612 return 0;613 rv = d->mode & DMDIR;614 free(d);615 return rv;616 }618 /*619 * kill a process620 */621 extern int622 syskill(int pid)623 {624 return postnote(PNPROC, pid, "kill");625 }627 /*628 * kill a process group629 */630 extern int631 syskillpg(int pid)632 {633 return postnote(PNGROUP, pid, "kill");634 }636 extern int637 sysdetach(void)638 {639 if(rfork(RFENVG|RFNAMEG|RFNOTEG) < 0) {640 werrstr("rfork failed");641 return -1;642 }643 return 0;644 }646 /*647 * catch a write on a closed pipe648 */649 static int *closedflag;650 static int651 catchpipe(void *a, char *msg)652 {653 static char *foo = "sys: write on closed pipe";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 void664 pipesig(int *flagp)665 {666 closedflag = flagp;667 atnotify(catchpipe, 1);668 }669 void670 pipesigoff(void)671 {672 atnotify(catchpipe, 0);673 }675 extern int676 holdon(void)677 {678 /* XXX talk to 9term? */679 return -1;680 }682 extern int683 sysopentty(void)684 {685 return open("/dev/tty", ORDWR);686 }688 extern void689 holdoff(int fd)690 {691 write(fd, "holdoff", 7);692 close(fd);693 }695 extern int696 sysfiles(void)697 {698 return 128;699 }701 /*702 * expand a path relative to the user's mailbox directory703 *704 * if the path starts with / or ./, don't change it705 *706 */707 extern String *708 mboxpath(char *path, char *user, String *to, int dot)709 {710 char *dir;711 String *s;713 if (dot || *path=='/' || strncmp(path, "./", 2) == 0714 || 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 }737 extern String *738 mboxname(char *user, String *to)739 {740 return mboxpath("mbox", user, to, 0);741 }743 extern String *744 deadletter(String *to) /* pass in sender??? */745 {746 char *cp;748 cp = getlog();749 if(cp == 0)750 return 0;751 return mboxpath("dead.letter", cp, to, 0);752 }754 String *755 readlock(String *file)756 {757 char *cp;759 cp = getlog();760 if(cp == 0)761 return 0;762 return mboxpath("reading", cp, file, 0);763 }765 String *766 username(String *from)767 {768 String* s;769 struct passwd* pw;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 }784 char *785 homedir(char *user)786 {787 static char buf[1024];788 struct passwd* pw;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 }799 char *800 remoteaddr(int fd, char *dir)801 {802 char *raddr;803 NetConnInfo *nci;805 if((nci = getnetconninfo(dir, fd)) == nil)806 return nil;807 raddr = strdup(nci->raddr);808 freenetconninfo(nci);809 return raddr;810 }812 /* create a file and */813 /* 1) ensure the modes we asked for */814 /* 2) make gid == uid */815 static int816 docreate(char *file, int perm)817 {818 int fd;819 Dir ndir;820 Dir *d;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 }842 /* create a mailbox */843 int844 creatembox(char *user, char *folder)845 {846 char *p;847 String *mailfile;848 char buf[512];849 Mlock *ml;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 }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));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 }881 /* create the mbox */882 if(docreate(s_to_c(mailfile), 0622|DMAPPEND|DMEXCL) < 0)883 return -1;885 /*886 * create the lock file if it doesn't exist887 */888 ml = trylock(s_to_c(mailfile));889 if(ml != nil)890 sysunlock(ml);892 return 0;893 }