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's
11 */
12 int nsysfile=3;
14 static char err[Errlen];
16 /*
17 * return the date
18 */
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 user
34 */
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 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");
59 return lp;
60 }
62 int
63 syscreatelocked(char *path, int mode, int perm)
64 {
65 return create(path, mode, DMEXCL|perm);
66 }
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 }
75 int
76 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 int
85 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));
112 l->fd = fd;
113 return 0;
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;
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;
136 } else
137 free(d);
139 return 1; /* try again later */
142 #define LSECS 5*60
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)
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 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;
175 noway:
176 s_free(l->name);
177 free(l);
178 return nil;
181 /*
182 * like lock except don't wait
183 */
184 extern Mlock *
185 trylock(char *path)
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;
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;
213 _exits(0);
215 return l;
218 extern void
219 syslockrefresh(Mlock *l)
221 char buf[1];
223 pread(l->fd, buf, 1, 0);
226 extern void
227 sysunlock(Mlock *l)
229 if(l == 0)
230 return;
231 if(l->name){
232 s_free(l->name);
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);
241 /*
242 * Open a file. The modes are:
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)
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 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;
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;
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;
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;
341 bp = (Biobuf*)malloc(sizeof(Biobuf));
342 if(bp == 0){
343 close(fd);
344 return 0;
346 memset(bp, 0, sizeof(Biobuf));
347 Binit(bp, fd, sysmode&~OTRUNC);
349 if(append)
350 Bseek(bp, 0, 2);
351 return bp;
354 /*
355 * close the file, etc.
356 */
357 int
358 sysclose(Biobuf *bp)
360 int rv;
362 rv = Bterm(bp);
363 close(Bfildes(bp));
364 free(bp);
365 return rv;
368 /*
369 * create a file
370 */
371 int
372 syscreate(char *file, int mode, ulong perm)
374 return create(file, mode, perm);
377 /*
378 * make a directory
379 */
380 int
381 sysmkdir(char *file, ulong perm)
383 int fd;
385 if((fd = create(file, OREAD, DMDIR|perm)) < 0)
386 return -1;
387 close(fd);
388 return 0;
391 /*
392 * change the group of a file
393 */
394 int
395 syschgrp(char *file, char *group)
397 Dir nd;
399 if(group == 0)
400 return -1;
401 nulldir(&nd);
402 nd.gid = group;
403 return dirwstat(file, &nd);
406 extern int
407 sysdirreadall(int fd, Dir **d)
409 return dirreadall(fd, d);
412 /*
413 * read in the system name
414 */
415 static char *unix_hostname_read(void);
416 extern char *
417 sysname_read(void)
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;
430 extern char *
431 alt_sysname_read(void)
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;
444 static char *
445 unix_hostname_read(void)
447 static char hostname[256];
449 if(gethostname(hostname, sizeof hostname) < 0)
450 return nil;
451 return hostname;
454 /*
455 * get all names
456 */
457 extern char**
458 sysnames_read(void)
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++)
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;
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)
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;
507 return 0;
510 /*
511 * return true if the last error message meant file
512 * did not exist.
513 */
514 extern int
515 e_nonexistent(void)
517 rerrstr(err, sizeof(err));
518 return strcmp(err, "file does not exist") == 0;
521 /*
522 * return true if the last error message meant file
523 * was locked.
524 */
525 extern int
526 e_locked(void)
528 rerrstr(err, sizeof(err));
529 return strcmp(err, "open/create -- file is locked") == 0;
532 /*
533 * return the length of a file
534 */
535 extern long
536 sysfilelen(Biobuf *fp)
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;
549 /*
550 * remove a file
551 */
552 extern int
553 sysremove(char *path)
555 return remove(path);
558 /*
559 * rename a file, fails unless both are in the same directory
560 */
561 extern int
562 sysrename(char *old, char *new)
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;
581 nulldir(&d);
582 d.name = nbase;
583 return dirwstat(old, &d);
586 /*
587 * see if a file exists
588 */
589 extern int
590 sysexist(char *file)
592 Dir *d;
594 d = dirstat(file);
595 if(d == nil)
596 return 0;
597 free(d);
598 return 1;
601 /*
602 * return nonzero if file is a directory
603 */
604 extern int
605 sysisdir(char *file)
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;
618 /*
619 * kill a process
620 */
621 extern int
622 syskill(int pid)
624 return postnote(PNPROC, pid, "kill");
627 /*
628 * kill a process group
629 */
630 extern int
631 syskillpg(int pid)
633 return postnote(PNGROUP, pid, "kill");
636 extern int
637 sysdetach(void)
639 if(rfork(RFENVG|RFNAMEG|RFNOTEG) < 0) {
640 werrstr("rfork failed");
641 return -1;
643 return 0;
646 /*
647 * catch a write on a closed pipe
648 */
649 static int *closedflag;
650 static int
651 catchpipe(void *a, char *msg)
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;
661 return 0;
663 void
664 pipesig(int *flagp)
666 closedflag = flagp;
667 atnotify(catchpipe, 1);
669 void
670 pipesigoff(void)
672 atnotify(catchpipe, 0);
675 extern int
676 holdon(void)
678 /* XXX talk to 9term? */
679 return -1;
682 extern int
683 sysopentty(void)
685 return open("/dev/tty", ORDWR);
688 extern void
689 holdoff(int fd)
691 write(fd, "holdoff", 7);
692 close(fd);
695 extern int
696 sysfiles(void)
698 return 128;
701 /*
702 * expand a path relative to the user's mailbox directory
704 * if the path starts with / or ./, don't change it
706 */
707 extern String *
708 mboxpath(char *path, char *user, String *to, int dot)
710 char *dir;
711 String *s;
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;
726 s_free(s);
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);
734 return to;
737 extern String *
738 mboxname(char *user, String *to)
740 return mboxpath("mbox", user, to, 0);
743 extern String *
744 deadletter(String *to) /* pass in sender??? */
746 char *cp;
748 cp = getlog();
749 if(cp == 0)
750 return 0;
751 return mboxpath("dead.letter", cp, to, 0);
754 String *
755 readlock(String *file)
757 char *cp;
759 cp = getlog();
760 if(cp == 0)
761 return 0;
762 return mboxpath("reading", cp, file, 0);
765 String *
766 username(String *from)
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;
781 return nil;
784 char *
785 homedir(char *user)
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;
796 return nil;
799 char *
800 remoteaddr(int fd, char *dir)
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;
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)
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;
828 d = dirfstat(fd);
829 if(d == nil){
830 fprint(2, "couldn't stat %s\n", file);
831 return -1;
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;
842 /* create a mailbox */
843 int
844 creatembox(char *user, char *folder)
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);
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;
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;
878 *p = '/';
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 exist
887 */
888 ml = trylock(s_to_c(mailfile));
889 if(ml != nil)
890 sysunlock(ml);
892 return 0;