xref: /plan9/sys/src/cmd/upas/common/libsys.c (revision a84536681645e23c630ce4ef2e5c3b284d4c590b)
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 	strcpy(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 	Dir *d;
710 	int rv;
711 
712 	d = dirfstat(fd);
713 	if(d == nil)
714 		return 0;
715 	rv = strcmp(d->name, "cons") == 0;
716 	free(d);
717 	return rv;
718 }
719 
720 extern int
721 holdon(void)
722 {
723 	int fd;
724 
725 	if(!islikeatty(0))
726 		return -1;
727 
728 	fd = open("/dev/consctl", OWRITE);
729 	write(fd, "holdon", 6);
730 
731 	return fd;
732 }
733 
734 extern int
735 sysopentty(void)
736 {
737 	return open("/dev/cons", ORDWR);
738 }
739 
740 extern void
741 holdoff(int fd)
742 {
743 	write(fd, "holdoff", 7);
744 	close(fd);
745 }
746 
747 extern int
748 sysfiles(void)
749 {
750 	return 128;
751 }
752 
753 /*
754  *  expand a path relative to the user's mailbox directory
755  *
756  *  if the path starts with / or ./, don't change it
757  *
758  */
759 extern String *
760 mboxpath(char *path, char *user, String *to, int dot)
761 {
762 	if (dot || *path=='/' || strncmp(path, "./", 2) == 0
763 			      || strncmp(path, "../", 3) == 0) {
764 		to = s_append(to, path);
765 	} else {
766 		to = s_append(to, MAILROOT);
767 		to = s_append(to, "/box/");
768 		to = s_append(to, user);
769 		to = s_append(to, "/");
770 		to = s_append(to, path);
771 	}
772 	return to;
773 }
774 
775 extern String *
776 mboxname(char *user, String *to)
777 {
778 	return mboxpath("mbox", user, to, 0);
779 }
780 
781 extern String *
782 deadletter(String *to)		/* pass in sender??? */
783 {
784 	char *cp;
785 
786 	cp = getlog();
787 	if(cp == 0)
788 		return 0;
789 	return mboxpath("dead.letter", cp, to, 0);
790 }
791 
792 char *
793 homedir(char *user)
794 {
795 	USED(user);
796 	return getenv("home");
797 }
798 
799 String *
800 readlock(String *file)
801 {
802 	char *cp;
803 
804 	cp = getlog();
805 	if(cp == 0)
806 		return 0;
807 	return mboxpath("reading", cp, file, 0);
808 }
809 
810 String *
811 username(String *from)
812 {
813 	int n;
814 	Biobuf *bp;
815 	char *p, *q;
816 	String *s;
817 
818 	bp = Bopen("/adm/keys.who", OREAD);
819 	if(bp == 0)
820 		bp = Bopen("/adm/netkeys.who", OREAD);
821 	if(bp == 0)
822 		return 0;
823 
824 	s = 0;
825 	n = strlen(s_to_c(from));
826 	for(;;) {
827 		p = Brdline(bp, '\n');
828 		if(p == 0)
829 			break;
830 		p[Blinelen(bp)-1] = 0;
831 		if(strncmp(p, s_to_c(from), n))
832 			continue;
833 		p += n;
834 		if(*p != ' ' && *p != '\t')	/* must be full match */
835 			continue;
836 		while(*p && (*p == ' ' || *p == '\t'))
837 				p++;
838 		if(*p == 0)
839 			continue;
840 		for(q = p; *q; q++)
841 			if(('0' <= *q && *q <= '9') || *q == '<')
842 				break;
843 		while(q > p && q[-1] != ' ' && q[-1] != '\t')
844 			q--;
845 		while(q > p && (q[-1] == ' ' || q[-1] == '\t'))
846 			q--;
847 		*q = 0;
848 		s = s_new();
849 		s_append(s, "\"");
850 		s_append(s, p);
851 		s_append(s, "\"");
852 		break;
853 	}
854 	Bterm(bp);
855 	return s;
856 }
857 
858 char *
859 remoteaddr(int fd, char *dir)
860 {
861 	char buf[128], *p;
862 	int n;
863 
864 	if(dir == 0){
865 		if(fd2path(fd, buf, sizeof(buf)) != 0)
866 			return "";
867 
868 		/* parse something of the form /net/tcp/nnnn/data */
869 		p = strrchr(buf, '/');
870 		if(p == 0)
871 			return "";
872 		strncpy(p+1, "remote", sizeof(buf)-(p-buf)-2);
873 	} else
874 		snprint(buf, sizeof buf, "%s/remote", dir);
875 	buf[sizeof(buf)-1] = 0;
876 
877 	fd = open(buf, OREAD);
878 	if(fd < 0)
879 		return "";
880 	n = read(fd, buf, sizeof(buf)-1);
881 	close(fd);
882 	if(n > 0){
883 		buf[n] = 0;
884 		p = strchr(buf, '!');
885 		if(p)
886 			*p = 0;
887 		return strdup(buf);
888 	}
889 	return "";
890 }
891 
892 //  create a file and
893 //	1) ensure the modes we asked for
894 //	2) make gid == uid
895 static int
896 docreate(char *file, int perm)
897 {
898 	int fd;
899 	Dir ndir;
900 	Dir *d;
901 
902 	//  create the mbox
903 	fd = create(file, OREAD, perm);
904 	if(fd < 0){
905 		fprint(2, "couldn't create %s\n", file);
906 		return -1;
907 	}
908 	d = dirfstat(fd);
909 	if(d == nil){
910 		fprint(2, "couldn't stat %s\n", file);
911 		return -1;
912 	}
913 	nulldir(&ndir);
914 	ndir.mode = perm;
915 	ndir.gid = d->uid;
916 	if(dirfwstat(fd, &ndir) < 0)
917 		fprint(2, "couldn't chmod %s: %r\n", file);
918 	close(fd);
919 	return 0;
920 }
921 
922 //  create a mailbox
923 int
924 creatembox(char *user, char *folder)
925 {
926 	char *p;
927 	String *mailfile;
928 	char buf[512];
929 	Mlock *ml;
930 
931 	mailfile = s_new();
932 	if(folder == 0)
933 		mboxname(user, mailfile);
934 	else {
935 		snprint(buf, sizeof(buf), "%s/mbox", folder);
936 		mboxpath(buf, user, mailfile, 0);
937 	}
938 
939 	// don't destroy existing mailbox
940 	if(access(s_to_c(mailfile), 0) == 0){
941 		fprint(2, "mailbox already exists\n");
942 		return -1;
943 	}
944 	fprint(2, "creating new mbox: %s\n", s_to_c(mailfile));
945 
946 	//  make sure preceding levels exist
947 	for(p = s_to_c(mailfile); p; p++) {
948 		if(*p == '/')	/* skip leading or consecutive slashes */
949 			continue;
950 		p = strchr(p, '/');
951 		if(p == 0)
952 			break;
953 		*p = 0;
954 		if(access(s_to_c(mailfile), 0) != 0){
955 			if(docreate(s_to_c(mailfile), DMDIR|0711) < 0)
956 				return -1;
957 		}
958 		*p = '/';
959 	}
960 
961 	//  create the mbox
962 	if(docreate(s_to_c(mailfile), 0622|DMAPPEND|DMEXCL) < 0)
963 		return -1;
964 
965 	/*
966 	 *  create the lock file if it doesn't exist
967 	 */
968 	ml = trylock(s_to_c(mailfile));
969 	if(ml != nil)
970 		sysunlock(ml);
971 
972 	return 0;
973 }
974