xref: /plan9/sys/src/cmd/upas/common/libsys.c (revision 4d44ba9b9ee4246ddbd96c7fcaf0918ab92ab35a)
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 *
thedate(void)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 *
getlog(void)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 *
lockname(char * path)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
syscreatelocked(char * path,int mode,int perm)70 syscreatelocked(char *path, int mode, int perm)
71 {
72 	return create(path, mode, DMEXCL|perm);
73 }
74 
75 int
sysopenlocked(char * path,int mode)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
sysunlockfile(int fd)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
openlockfile(Mlock * l)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 *
syslock(char * path)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 *
trylock(char * path)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
syslockrefresh(Mlock * l)226 syslockrefresh(Mlock *l)
227 {
228 	char buf[1];
229 
230 	pread(l->fd, buf, 1, 0);
231 }
232 
233 extern void
sysunlock(Mlock * l)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 *
sysopen(char * path,char * mode,ulong perm)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
sysclose(Biobuf * bp)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
syscreate(char * file,int mode,ulong perm)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
sysmkdir(char * file,ulong perm)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
syschgrp(char * file,char * group)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
sysdirreadall(int fd,Dir ** d)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 *
sysname_read(void)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 *
alt_sysname_read(void)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**
sysnames_read(void)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 *
domainname_read(void)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
e_nonexistent(void)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
e_locked(void)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
sysfilelen(Biobuf * fp)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
sysremove(char * path)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
sysrename(char * old,char * new)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
sysexist(char * file)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
sysisdir(char * file)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
stomp(int pid,char * file)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
syskill(int pid)641 syskill(int pid)
642 {
643 	return stomp(pid, "note");
644 
645 }
646 
647 /*
648  *  kill a process group
649  */
650 extern int
syskillpg(int pid)651 syskillpg(int pid)
652 {
653 	return stomp(pid, "notepg");
654 }
655 
656 extern int
sysdetach(void)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
catchpipe(void * a,char * msg)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
pipesig(int * flagp)684 pipesig(int *flagp)
685 {
686 	closedflag = flagp;
687 	atnotify(catchpipe, 1);
688 }
689 void
pipesigoff(void)690 pipesigoff(void)
691 {
692 	atnotify(catchpipe, 0);
693 }
694 
695 void
exit(int i)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
islikeatty(int fd)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
holdon(void)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
sysopentty(void)733 sysopentty(void)
734 {
735 	return open("/dev/cons", ORDWR);
736 }
737 
738 extern void
holdoff(int fd)739 holdoff(int fd)
740 {
741 	write(fd, "holdoff", 7);
742 	close(fd);
743 }
744 
745 extern int
sysfiles(void)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 *
mboxpath(char * path,char * user,String * to,int dot)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 *
mboxname(char * user,String * to)774 mboxname(char *user, String *to)
775 {
776 	return mboxpath("mbox", user, to, 0);
777 }
778 
779 extern String *
deadletter(String * to)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 *
homedir(char * user)791 homedir(char *user)
792 {
793 	USED(user);
794 	return getenv("home");
795 }
796 
797 String *
readlock(String * file)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 *
username(String * from)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 *
remoteaddr(int fd,char * dir)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
docreate(char * file,int perm)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
creatembox(char * user,char * folder)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 }
972