plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

libsys.c (13462B)


      1 #include <u.h>
      2 #include <sys/types.h>
      3 #include <pwd.h>
      4 #include <netdb.h>
      5 #include "common.h"
      6 #include <auth.h>
      7 #include <ndb.h>
      8 
      9 /*
     10  *  number of predefined fd's
     11  */
     12 int nsysfile=3;
     13 
     14 static char err[Errlen];
     15 
     16 /*
     17  *  return the date
     18  */
     19 extern char *
     20 thedate(void)
     21 {
     22 	static char now[64];
     23 	char *cp;
     24 
     25 	strcpy(now, ctime(time(0)));
     26 	cp = strchr(now, '\n');
     27 	if(cp)
     28 		*cp = 0;
     29 	return now;
     30 }
     31 
     32 /*
     33  *  return the user id of the current user
     34  */
     35 extern char *
     36 getlog(void)
     37 {
     38 	return getuser();
     39 }
     40 
     41 /*
     42  *  return the lock name (we use one lock per directory)
     43  */
     44 static String *
     45 lockname(char *path)
     46 {
     47 	String *lp;
     48 	char *cp;
     49 
     50 	/*
     51 	 *  get the name of the lock file
     52 	 */
     53 	lp = s_new();
     54 	cp = strrchr(path, '/');
     55 	if(cp)
     56 		s_nappend(lp, path, cp - path + 1);
     57 	s_append(lp, "L.mbox");
     58 
     59 	return lp;
     60 }
     61 
     62 int
     63 syscreatelocked(char *path, int mode, int perm)
     64 {
     65 	return create(path, mode, DMEXCL|perm);
     66 }
     67 
     68 int
     69 sysopenlocked(char *path, int mode)
     70 {
     71 /*	return open(path, OEXCL|mode);/**/
     72 	return open(path, mode);		/* until system call is fixed */
     73 }
     74 
     75 int
     76 sysunlockfile(int fd)
     77 {
     78 	return close(fd);
     79 }
     80 
     81 /*
     82  *  try opening a lock file.  If it doesn't exist try creating it.
     83  */
     84 static int
     85 openlockfile(Mlock *l)
     86 {
     87 	int fd;
     88 	Dir *d;
     89 	Dir nd;
     90 	char *p;
     91 
     92 	fd = open(s_to_c(l->name), OREAD);
     93 	if(fd >= 0){
     94 		l->fd = fd;
     95 		return 0;
     96 	}
     97 
     98 	d = dirstat(s_to_c(l->name));
     99 	if(d == nil){
    100 		/* file doesn't exist */
    101 		/* try creating it */
    102 		fd = create(s_to_c(l->name), OREAD, DMEXCL|0666);
    103 		if(fd >= 0){
    104 			nulldir(&nd);
    105 			nd.mode = DMEXCL|0666;
    106 			if(dirfwstat(fd, &nd) < 0){
    107 				/* if we can't chmod, don't bother */
    108 				/* live without the lock but log it */
    109 				syslog(0, "mail", "lock error: %s: %r", s_to_c(l->name));
    110 				remove(s_to_c(l->name));
    111 			}
    112 			l->fd = fd;
    113 			return 0;
    114 		}
    115 
    116 		/* couldn't create */
    117 		/* do we have write access to the directory? */
    118 		p = strrchr(s_to_c(l->name), '/');
    119 		if(p != 0){
    120 			*p = 0;
    121 			fd = access(s_to_c(l->name), 2);
    122 			*p = '/';
    123 			if(fd < 0){
    124 				/* live without the lock but log it */
    125 				syslog(0, "mail", "lock error: %s: %r", s_to_c(l->name));
    126 				return 0;
    127 			}
    128 		} else {
    129 			fd = access(".", 2);
    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 		}
    136 	} else
    137 		free(d);
    138 
    139 	return 1; /* try again later */
    140 }
    141 
    142 #define LSECS 5*60
    143 
    144 /*
    145  *  Set a lock for a particular file.  The lock is a file in the same directory
    146  *  and has L. prepended to the name of the last element of the file name.
    147  */
    148 extern Mlock *
    149 syslock(char *path)
    150 {
    151 	Mlock *l;
    152 	int tries;
    153 
    154 	l = mallocz(sizeof(Mlock), 1);
    155 	if(l == 0)
    156 		return nil;
    157 
    158 	l->name = lockname(path);
    159 
    160 	/*
    161 	 *  wait LSECS seconds for it to unlock
    162 	 */
    163 	for(tries = 0; tries < LSECS*2; tries++){
    164 		switch(openlockfile(l)){
    165 		case 0:
    166 			return l;
    167 		case 1:
    168 			sleep(500);
    169 			break;
    170 		default:
    171 			goto noway;
    172 		}
    173 	}
    174 
    175 noway:
    176 	s_free(l->name);
    177 	free(l);
    178 	return nil;
    179 }
    180 
    181 /*
    182  *  like lock except don't wait
    183  */
    184 extern Mlock *
    185 trylock(char *path)
    186 {
    187 	Mlock *l;
    188 	char buf[1];
    189 	int fd;
    190 
    191 	l = malloc(sizeof(Mlock));
    192 	if(l == 0)
    193 		return 0;
    194 
    195 	l->name = lockname(path);
    196 	if(openlockfile(l) != 0){
    197 		s_free(l->name);
    198 		free(l);
    199 		return 0;
    200 	}
    201 
    202 	/* fork process to keep lock alive */
    203 	switch(l->pid = rfork(RFPROC)){
    204 	default:
    205 		break;
    206 	case 0:
    207 		fd = l->fd;
    208 		for(;;){
    209 			sleep(1000*60);
    210 			if(pread(fd, buf, 1, 0) < 0)
    211 				break;
    212 		}
    213 		_exits(0);
    214 	}
    215 	return l;
    216 }
    217 
    218 extern void
    219 syslockrefresh(Mlock *l)
    220 {
    221 	char buf[1];
    222 
    223 	pread(l->fd, buf, 1, 0);
    224 }
    225 
    226 extern void
    227 sysunlock(Mlock *l)
    228 {
    229 	if(l == 0)
    230 		return;
    231 	if(l->name){
    232 		s_free(l->name);
    233 	}
    234 	if(l->fd >= 0)
    235 		close(l->fd);
    236 	if(l->pid > 0)
    237 		postnote(PNPROC, l->pid, "time to die");
    238 	free(l);
    239 }
    240 
    241 /*
    242  *  Open a file.  The modes are:
    243  *
    244  *	l	- locked
    245  *	a	- set append permissions
    246  *	r	- readable
    247  *	w	- writable
    248  *	A	- append only (doesn't exist in Bio)
    249  */
    250 extern Biobuf *
    251 sysopen(char *path, char *mode, ulong perm)
    252 {
    253 	int sysperm;
    254 	int sysmode;
    255 	int fd;
    256 	int docreate;
    257 	int append;
    258 	int truncate;
    259 	Dir *d, nd;
    260 	Biobuf *bp;
    261 
    262 	/*
    263 	 *  decode the request
    264 	 */
    265 	sysperm = 0;
    266 	sysmode = -1;
    267 	docreate = 0;
    268 	append = 0;
    269 	truncate = 0;
    270  	for(; mode && *mode; mode++)
    271 		switch(*mode){
    272 		case 'A':
    273 			sysmode = OWRITE;
    274 			append = 1;
    275 			break;
    276 		case 'c':
    277 			docreate = 1;
    278 			break;
    279 		case 'l':
    280 			sysperm |= DMEXCL;
    281 			sysmode |= OLOCK;
    282 			break;
    283 		case 'a':
    284 			sysperm |= DMAPPEND;
    285 			break;
    286 		case 'w':
    287 			if(sysmode == -1)
    288 				sysmode = OWRITE;
    289 			else
    290 				sysmode = ORDWR;
    291 			break;
    292 		case 'r':
    293 			if(sysmode == -1)
    294 				sysmode = OREAD;
    295 			else
    296 				sysmode = ORDWR;
    297 			break;
    298 		case 't':
    299 			truncate = 1;
    300 			break;
    301 		default:
    302 			break;
    303 		}
    304 	switch(sysmode){
    305 	case OREAD:
    306 	case OWRITE:
    307 	case ORDWR:
    308 		break;
    309 	default:
    310 		if(sysperm&DMAPPEND)
    311 			sysmode = OWRITE;
    312 		else
    313 			sysmode = OREAD;
    314 		break;
    315 	}
    316 
    317 	/*
    318 	 *  create file if we need to
    319 	 */
    320 	if(truncate)
    321 		sysmode |= OTRUNC;
    322 	fd = open(path, sysmode);
    323 	if(fd < 0){
    324 		d = dirstat(path);
    325 		if(d == nil){
    326 			if(docreate == 0)
    327 				return 0;
    328 
    329 			fd = create(path, sysmode, sysperm|perm);
    330 			if(fd < 0)
    331 				return 0;
    332 			nulldir(&nd);
    333 			nd.mode = sysperm|perm;
    334 			dirfwstat(fd, &nd);
    335 		} else {
    336 			free(d);
    337 			return 0;
    338 		}
    339 	}
    340 
    341 	bp = (Biobuf*)malloc(sizeof(Biobuf));
    342 	if(bp == 0){
    343 		close(fd);
    344 		return 0;
    345 	}
    346 	memset(bp, 0, sizeof(Biobuf));
    347 	Binit(bp, fd, sysmode&~OTRUNC);
    348 
    349 	if(append)
    350 		Bseek(bp, 0, 2);
    351 	return bp;
    352 }
    353 
    354 /*
    355  *  close the file, etc.
    356  */
    357 int
    358 sysclose(Biobuf *bp)
    359 {
    360 	int rv;
    361 
    362 	rv = Bterm(bp);
    363 	close(Bfildes(bp));
    364 	free(bp);
    365 	return rv;
    366 }
    367 
    368 /*
    369  *  create a file
    370  */
    371 int
    372 syscreate(char *file, int mode, ulong perm)
    373 {
    374 	return create(file, mode, perm);
    375 }
    376 
    377 /*
    378  *  make a directory
    379  */
    380 int
    381 sysmkdir(char *file, ulong perm)
    382 {
    383 	int fd;
    384 
    385 	if((fd = create(file, OREAD, DMDIR|perm)) < 0)
    386 		return -1;
    387 	close(fd);
    388 	return 0;
    389 }
    390 
    391 /*
    392  *  change the group of a file
    393  */
    394 int
    395 syschgrp(char *file, char *group)
    396 {
    397 	Dir nd;
    398 
    399 	if(group == 0)
    400 		return -1;
    401 	nulldir(&nd);
    402 	nd.gid = group;
    403 	return dirwstat(file, &nd);
    404 }
    405 
    406 extern int
    407 sysdirreadall(int fd, Dir **d)
    408 {
    409 	return dirreadall(fd, d);
    410 }
    411 
    412 /*
    413  *  read in the system name
    414  */
    415 static char *unix_hostname_read(void);
    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 	strecpy(name, name+sizeof name, cp);
    428 	return name;
    429 }
    430 extern char *
    431 alt_sysname_read(void)
    432 {
    433 	char *cp;
    434 	static char name[128];
    435 
    436 	cp = getenv("sysname");
    437 	if(cp == 0 || *cp == 0)
    438 		cp = unix_hostname_read();
    439 	if(cp == 0 || *cp == 0)
    440 		return 0;
    441 	strecpy(name, name+sizeof name, cp);
    442 	return name;
    443 }
    444 static char *
    445 unix_hostname_read(void)
    446 {
    447 	static char hostname[256];
    448 
    449 	if(gethostname(hostname, sizeof hostname) < 0)
    450 		return nil;
    451 	return hostname;
    452 }
    453 
    454 /*
    455  *  get all names
    456  */
    457 extern char**
    458 sysnames_read(void)
    459 {
    460 	static char **namev;
    461 	struct hostent *h;
    462 	char **p, **a;
    463 
    464 	if(namev)
    465 		return namev;
    466 
    467 	h = gethostbyname(alt_sysname_read());
    468 	if(h == nil)
    469 		return 0;
    470 
    471 	for(p=h->h_aliases; *p; p++)
    472 		;
    473 
    474 	namev = malloc((2+p-h->h_aliases)*sizeof namev[0]);
    475 	if(namev == 0)
    476 		return 0;
    477 
    478 	a = namev;
    479 	*a++ = strdup(h->h_name);
    480 	for(p=h->h_aliases; *p; p++)
    481 		*a++ = strdup(*p);
    482 	*a = 0;
    483 
    484 	return namev;
    485 }
    486 
    487 /*
    488  *  read in the domain name.
    489  *  chop off beginning pieces until we find one with an mx record.
    490  */
    491 extern char *
    492 domainname_read(void)
    493 {
    494 	char **namev, *p;
    495 	Ndbtuple *t;
    496 
    497 	for(namev = sysnames_read(); namev && *namev; namev++){
    498 		if(strchr(*namev, '.')){
    499 			for(p=*namev-1; p && *++p; p=strchr(p, '.')){
    500 				if((t = dnsquery(nil, p, "mx")) != nil){
    501 					ndbfree(t);
    502 					return p;
    503 				}
    504 			}
    505 		}
    506 	}
    507 	return 0;
    508 }
    509 
    510 /*
    511  *  return true if the last error message meant file
    512  *  did not exist.
    513  */
    514 extern int
    515 e_nonexistent(void)
    516 {
    517 	rerrstr(err, sizeof(err));
    518 	return strcmp(err, "file does not exist") == 0;
    519 }
    520 
    521 /*
    522  *  return true if the last error message meant file
    523  *  was locked.
    524  */
    525 extern int
    526 e_locked(void)
    527 {
    528 	rerrstr(err, sizeof(err));
    529 	return strcmp(err, "open/create -- file is locked") == 0;
    530 }
    531 
    532 /*
    533  *  return the length of a file
    534  */
    535 extern long
    536 sysfilelen(Biobuf *fp)
    537 {
    538 	Dir *d;
    539 	long rv;
    540 
    541 	d = dirfstat(Bfildes(fp));
    542 	if(d == nil)
    543 		return -1;
    544 	rv = d->length;
    545 	free(d);
    546 	return rv;
    547 }
    548 
    549 /*
    550  *  remove a file
    551  */
    552 extern int
    553 sysremove(char *path)
    554 {
    555 	return remove(path);
    556 }
    557 
    558 /*
    559  *  rename a file, fails unless both are in the same directory
    560  */
    561 extern int
    562 sysrename(char *old, char *new)
    563 {
    564 	Dir d;
    565 	char *obase;
    566 	char *nbase;
    567 
    568 	obase = strrchr(old, '/');
    569 	nbase = strrchr(new, '/');
    570 	if(obase){
    571 		if(nbase == 0)
    572 			return -1;
    573 		if(strncmp(old, new, obase-old) != 0)
    574 			return -1;
    575 		nbase++;
    576 	} else {
    577 		if(nbase)
    578 			return -1;
    579 		nbase = new;
    580 	}
    581 	nulldir(&d);
    582 	d.name = nbase;
    583 	return dirwstat(old, &d);
    584 }
    585 
    586 /*
    587  *  see if a file exists
    588  */
    589 extern int
    590 sysexist(char *file)
    591 {
    592 	Dir	*d;
    593 
    594 	d = dirstat(file);
    595 	if(d == nil)
    596 		return 0;
    597 	free(d);
    598 	return 1;
    599 }
    600 
    601 /*
    602  *  return nonzero if file is a directory
    603  */
    604 extern int
    605 sysisdir(char *file)
    606 {
    607 	Dir	*d;
    608 	int	rv;
    609 
    610 	d = dirstat(file);
    611 	if(d == nil)
    612 		return 0;
    613 	rv = d->mode & DMDIR;
    614 	free(d);
    615 	return rv;
    616 }
    617 
    618 /*
    619  *  kill a process
    620  */
    621 extern int
    622 syskill(int pid)
    623 {
    624 	return postnote(PNPROC, pid, "kill");
    625 }
    626 
    627 /*
    628  *  kill a process group
    629  */
    630 extern int
    631 syskillpg(int pid)
    632 {
    633 	return postnote(PNGROUP, pid, "kill");
    634 }
    635 
    636 extern int
    637 sysdetach(void)
    638 {
    639 	if(rfork(RFENVG|RFNAMEG|RFNOTEG) < 0) {
    640 		werrstr("rfork failed");
    641 		return -1;
    642 	}
    643 	return 0;
    644 }
    645 
    646 /*
    647  *  catch a write on a closed pipe
    648  */
    649 static int *closedflag;
    650 static int
    651 catchpipe(void *a, char *msg)
    652 {
    653 	static char *foo = "sys: write on closed pipe";
    654 
    655 	USED(a);
    656 	if(strncmp(msg, foo, strlen(foo)) == 0){
    657 		if(closedflag)
    658 			*closedflag = 1;
    659 		return 1;
    660 	}
    661 	return 0;
    662 }
    663 void
    664 pipesig(int *flagp)
    665 {
    666 	closedflag = flagp;
    667 	atnotify(catchpipe, 1);
    668 }
    669 void
    670 pipesigoff(void)
    671 {
    672 	atnotify(catchpipe, 0);
    673 }
    674 
    675 extern int
    676 holdon(void)
    677 {
    678 	/* XXX talk to 9term? */
    679 	return -1;
    680 }
    681 
    682 extern int
    683 sysopentty(void)
    684 {
    685 	return open("/dev/tty", ORDWR);
    686 }
    687 
    688 extern void
    689 holdoff(int fd)
    690 {
    691 	write(fd, "holdoff", 7);
    692 	close(fd);
    693 }
    694 
    695 extern int
    696 sysfiles(void)
    697 {
    698 	return 128;
    699 }
    700 
    701 /*
    702  *  expand a path relative to the user's mailbox directory
    703  *
    704  *  if the path starts with / or ./, don't change it
    705  *
    706  */
    707 extern String *
    708 mboxpath(char *path, char *user, String *to, int dot)
    709 {
    710 	char *dir;
    711 	String *s;
    712 
    713 	if (dot || *path=='/' || strncmp(path, "./", 2) == 0
    714 			      || strncmp(path, "../", 3) == 0) {
    715 		to = s_append(to, path);
    716 	} else {
    717 		if ((dir = homedir(user)) != nil) {
    718 			s = s_copy(dir);
    719 			s_append(s, "/mail/");
    720 			if(access(s_to_c(s), AEXIST) >= 0){
    721 				to = s_append(to, s_to_c(s));
    722 				s_free(s);
    723 				to = s_append(to, path);
    724 				return to;
    725 			}
    726 			s_free(s);
    727 		}
    728 		to = s_append(to, MAILROOT);
    729 		to = s_append(to, "/box/");
    730 		to = s_append(to, user);
    731 		to = s_append(to, "/");
    732 		to = s_append(to, path);
    733 	}
    734 	return to;
    735 }
    736 
    737 extern String *
    738 mboxname(char *user, String *to)
    739 {
    740 	return mboxpath("mbox", user, to, 0);
    741 }
    742 
    743 extern String *
    744 deadletter(String *to)		/* pass in sender??? */
    745 {
    746 	char *cp;
    747 
    748 	cp = getlog();
    749 	if(cp == 0)
    750 		return 0;
    751 	return mboxpath("dead.letter", cp, to, 0);
    752 }
    753 
    754 String *
    755 readlock(String *file)
    756 {
    757 	char *cp;
    758 
    759 	cp = getlog();
    760 	if(cp == 0)
    761 		return 0;
    762 	return mboxpath("reading", cp, file, 0);
    763 }
    764 
    765 String *
    766 username(String *from)
    767 {
    768 	String* s;
    769 	struct passwd* pw;
    770 
    771 	setpwent();
    772 	while((pw = getpwent()) != nil){
    773 		if(strcmp(s_to_c(from), pw->pw_name) == 0){
    774 			s = s_new();
    775 			s_append(s, "\"");
    776 			s_append(s, pw->pw_gecos);
    777 			s_append(s, "\"");
    778 			return s;
    779 		}
    780 	}
    781 	return nil;
    782 }
    783 
    784 char *
    785 homedir(char *user)
    786 {
    787 	static char buf[1024];
    788 	struct passwd* pw;
    789 
    790 	setpwent();
    791 	while((pw = getpwent()) != nil)
    792 		if(strcmp(user, pw->pw_name) == 0){
    793 			strecpy(buf, buf+sizeof buf, pw->pw_dir);
    794 			return buf;
    795 		}
    796 	return nil;
    797 }
    798 
    799 char *
    800 remoteaddr(int fd, char *dir)
    801 {
    802 	char *raddr;
    803 	NetConnInfo *nci;
    804 
    805 	if((nci = getnetconninfo(dir, fd)) == nil)
    806 		return nil;
    807 	raddr = strdup(nci->raddr);
    808 	freenetconninfo(nci);
    809 	return raddr;
    810 }
    811 
    812 /*  create a file and  */
    813 /*	1) ensure the modes we asked for */
    814 /*	2) make gid == uid */
    815 static int
    816 docreate(char *file, int perm)
    817 {
    818 	int fd;
    819 	Dir ndir;
    820 	Dir *d;
    821 
    822 	/*  create the mbox */
    823 	fd = create(file, OREAD, perm);
    824 	if(fd < 0){
    825 		fprint(2, "couldn't create %s\n", file);
    826 		return -1;
    827 	}
    828 	d = dirfstat(fd);
    829 	if(d == nil){
    830 		fprint(2, "couldn't stat %s\n", file);
    831 		return -1;
    832 	}
    833 	nulldir(&ndir);
    834 	ndir.mode = perm;
    835 	ndir.gid = d->uid;
    836 	if(dirfwstat(fd, &ndir) < 0)
    837 		fprint(2, "couldn't chmod %s: %r\n", file);
    838 	close(fd);
    839 	return 0;
    840 }
    841 
    842 /*  create a mailbox */
    843 int
    844 creatembox(char *user, char *folder)
    845 {
    846 	char *p;
    847 	String *mailfile;
    848 	char buf[512];
    849 	Mlock *ml;
    850 
    851 	mailfile = s_new();
    852 	if(folder == 0)
    853 		mboxname(user, mailfile);
    854 	else {
    855 		snprint(buf, sizeof(buf), "%s/mbox", folder);
    856 		mboxpath(buf, user, mailfile, 0);
    857 	}
    858 
    859 	/* don't destroy existing mailbox */
    860 	if(access(s_to_c(mailfile), 0) == 0){
    861 		fprint(2, "mailbox already exists\n");
    862 		return -1;
    863 	}
    864 	fprint(2, "creating new mbox: %s\n", s_to_c(mailfile));
    865 
    866 	/*  make sure preceding levels exist */
    867 	for(p = s_to_c(mailfile); p; p++) {
    868 		if(*p == '/')	/* skip leading or consecutive slashes */
    869 			continue;
    870 		p = strchr(p, '/');
    871 		if(p == 0)
    872 			break;
    873 		*p = 0;
    874 		if(access(s_to_c(mailfile), 0) != 0){
    875 			if(docreate(s_to_c(mailfile), DMDIR|0711) < 0)
    876 				return -1;
    877 		}
    878 		*p = '/';
    879 	}
    880 
    881 	/*  create the mbox */
    882 	if(docreate(s_to_c(mailfile), 0622|DMAPPEND|DMEXCL) < 0)
    883 		return -1;
    884 
    885 	/*
    886 	 *  create the lock file if it doesn't exist
    887 	 */
    888 	ml = trylock(s_to_c(mailfile));
    889 	if(ml != nil)
    890 		sysunlock(ml);
    891 
    892 	return 0;
    893 }