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