]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/upas/common/libsys.c
f0b8f35a411c4bd6fb2cf683d771b874ff796329
[plan9front.git] / sys / src / cmd / upas / common / libsys.c
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 (we use one lock per directory)
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                 s_nappend(lp, path, cp - path + 1);
64         s_append(lp, "L.mbox");
65
66         return lp;
67 }
68
69 int
70 syscreatelocked(char *path, int mode, int perm)
71 {
72         return create(path, mode, DMEXCL|perm);
73 }
74
75 int
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
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
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 *
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 *
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
226 syslockrefresh(Mlock *l)
227 {
228         char buf[1];
229
230         pread(l->fd, buf, 1, 0);
231 }
232
233 extern void
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 *
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
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
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
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
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
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 *
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 *
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**
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 *
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
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
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
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
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
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
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
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
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
641 syskill(int pid)
642 {
643         return stomp(pid, "note");
644         
645 }
646
647 /*
648  *  kill a process group
649  */
650 extern int
651 syskillpg(int pid)
652 {
653         return stomp(pid, "notepg");
654 }
655
656 extern int
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
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
684 pipesig(int *flagp)
685 {
686         closedflag = flagp;
687         atnotify(catchpipe, 1);
688 }
689 void
690 pipesigoff(void)
691 {
692         atnotify(catchpipe, 0);
693 }
694
695 void
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
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
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
733 sysopentty(void)
734 {
735         return open("/dev/cons", ORDWR);
736 }
737
738 extern void
739 holdoff(int fd)
740 {
741         write(fd, "holdoff", 7);
742         close(fd);
743 }
744
745 extern int
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 *
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 *
774 mboxname(char *user, String *to)
775 {
776         return mboxpath("mbox", user, to, 0);
777 }
778
779 extern String *
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 *
791 homedir(char *user)
792 {
793         USED(user);
794         return getenv("home");
795 }
796
797 String *
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 *
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 *
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
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
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 }