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