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