summaryrefslogtreecommitdiff
path: root/sys/src/cmd/upas/common
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/upas/common
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/upas/common')
-rwxr-xr-xsys/src/cmd/upas/common/appendfiletombox.c164
-rwxr-xr-xsys/src/cmd/upas/common/aux.c149
-rwxr-xr-xsys/src/cmd/upas/common/become.c28
-rwxr-xr-xsys/src/cmd/upas/common/common.h80
-rwxr-xr-xsys/src/cmd/upas/common/config.c11
-rwxr-xr-xsys/src/cmd/upas/common/libsys.c971
-rwxr-xr-xsys/src/cmd/upas/common/mail.c57
-rwxr-xr-xsys/src/cmd/upas/common/makefile18
-rwxr-xr-xsys/src/cmd/upas/common/mkfile24
-rwxr-xr-xsys/src/cmd/upas/common/process.c173
-rwxr-xr-xsys/src/cmd/upas/common/sys.h85
11 files changed, 1760 insertions, 0 deletions
diff --git a/sys/src/cmd/upas/common/appendfiletombox.c b/sys/src/cmd/upas/common/appendfiletombox.c
new file mode 100755
index 000000000..16bdd48aa
--- /dev/null
+++ b/sys/src/cmd/upas/common/appendfiletombox.c
@@ -0,0 +1,164 @@
+#include "common.h"
+
+enum {
+ Buffersize = 64*1024,
+};
+
+typedef struct Inbuf Inbuf;
+struct Inbuf
+{
+ char buf[Buffersize];
+ char *wp;
+ char *rp;
+ int eof;
+ int in;
+ int out;
+ int last;
+ ulong bytes;
+};
+
+static Inbuf*
+allocinbuf(int in, int out)
+{
+ Inbuf *b;
+
+ b = mallocz(sizeof(Inbuf), 1);
+ if(b == nil)
+ sysfatal("reading mailbox: %r");
+ b->rp = b->wp = b->buf;
+ b->in = in;
+ b->out = out;
+ return b;
+}
+
+/* should only be called at start of file or when b->rp[-1] == '\n' */
+static int
+fill(Inbuf *b, int addspace)
+{
+ int i, n;
+
+ if(b->eof && b->wp - b->rp == 0)
+ return 0;
+
+ n = b->rp - b->buf;
+ if(n > 0){
+ i = write(b->out, b->buf, n);
+ if(i != n)
+ return -1;
+ b->last = b->buf[n-1];
+ b->bytes += n;
+ }
+ if(addspace){
+ if(write(b->out, " ", 1) != 1)
+ return -1;
+ b->last = ' ';
+ b->bytes++;
+ }
+
+ n = b->wp - b->rp;
+ memmove(b->buf, b->rp, n);
+ b->rp = b->buf;
+ b->wp = b->rp + n;
+
+ i = read(b->in, b->buf+n, sizeof(b->buf)-n);
+ if(i < 0)
+ return -1;
+ b->wp += i;
+
+ return b->wp - b->rp;
+}
+
+enum { Fromlen = sizeof "From " - 1, };
+
+/* code to escape ' '*From' ' at the beginning of a line */
+int
+appendfiletombox(int in, int out)
+{
+ int addspace, n, sol;
+ char *p;
+ Inbuf *b;
+
+ seek(out, 0, 2);
+
+ b = allocinbuf(in, out);
+ addspace = 0;
+ sol = 1;
+
+ for(;;){
+ if(b->wp - b->rp < Fromlen){
+ /*
+ * not enough unread bytes in buffer to match "From ",
+ * so get some more. We must only inject a space at
+ * the start of a line (one that begins with "From ").
+ */
+ if (b->rp == b->buf || b->rp[-1] == '\n') {
+ n = fill(b, addspace);
+ addspace = 0;
+ } else
+ n = fill(b, 0);
+ if(n < 0)
+ goto error;
+ if(n == 0)
+ break;
+ if(n < Fromlen){ /* still can't match? */
+ b->rp = b->wp;
+ continue;
+ }
+ }
+
+ /* state machine looking for ' '*From' ' */
+ if(!sol){
+ p = memchr(b->rp, '\n', b->wp - b->rp);
+ if(p == nil)
+ b->rp = b->wp;
+ else{
+ b->rp = p+1;
+ sol = 1;
+ }
+ continue;
+ } else {
+ if(*b->rp == ' ' || strncmp(b->rp, "From ", Fromlen) != 0){
+ b->rp++;
+ continue;
+ }
+ addspace = 1;
+ sol = 0;
+ }
+ }
+
+ /* mailbox entries always terminate with two newlines */
+ n = b->last == '\n' ? 1 : 2;
+ if(write(out, "\n\n", n) != n)
+ goto error;
+ n += b->bytes;
+ free(b);
+ return n;
+error:
+ free(b);
+ return -1;
+}
+
+int
+appendfiletofile(int in, int out)
+{
+ int n;
+ Inbuf *b;
+
+ seek(out, 0, 2);
+
+ b = allocinbuf(in, out);
+ for(;;){
+ n = fill(b, 0);
+ if(n < 0)
+ goto error;
+ if(n == 0)
+ break;
+ b->rp = b->wp;
+ }
+ n = b->bytes;
+ free(b);
+ return n;
+error:
+ free(b);
+ return -1;
+}
diff --git a/sys/src/cmd/upas/common/aux.c b/sys/src/cmd/upas/common/aux.c
new file mode 100755
index 000000000..8786acb50
--- /dev/null
+++ b/sys/src/cmd/upas/common/aux.c
@@ -0,0 +1,149 @@
+#include "common.h"
+
+/* expand a path relative to some `.' */
+extern String *
+abspath(char *path, char *dot, String *to)
+{
+ if (*path == '/') {
+ to = s_append(to, path);
+ } else {
+ to = s_append(to, dot);
+ to = s_append(to, "/");
+ to = s_append(to, path);
+ }
+ return to;
+}
+
+/* return a pointer to the base component of a pathname */
+extern char *
+basename(char *path)
+{
+ char *cp;
+
+ cp = strrchr(path, '/');
+ return cp==0 ? path : cp+1;
+}
+
+/* append a sub-expression match onto a String */
+extern void
+append_match(Resub *subexp, String *sp, int se)
+{
+ char *cp, *ep;
+
+ cp = subexp[se].sp;
+ ep = subexp[se].ep;
+ for (; cp < ep; cp++)
+ s_putc(sp, *cp);
+ s_terminate(sp);
+}
+
+/*
+ * check for shell characters in a String
+ */
+static char *illegalchars = "\r\n";
+
+extern int
+shellchars(char *cp)
+{
+ char *sp;
+
+ for(sp=illegalchars; *sp; sp++)
+ if(strchr(cp, *sp))
+ return 1;
+ return 0;
+}
+
+static char *specialchars = " ()<>{};=\\'\`^&|";
+static char *escape = "%%";
+
+int
+hexchar(int x)
+{
+ x &= 0xf;
+ if(x < 10)
+ return '0' + x;
+ else
+ return 'A' + x - 10;
+}
+
+/*
+ * rewrite a string to escape shell characters
+ */
+extern String*
+escapespecial(String *s)
+{
+ String *ns;
+ char *sp;
+
+ for(sp = specialchars; *sp; sp++)
+ if(strchr(s_to_c(s), *sp))
+ break;
+ if(*sp == 0)
+ return s;
+
+ ns = s_new();
+ for(sp = s_to_c(s); *sp; sp++){
+ if(strchr(specialchars, *sp)){
+ s_append(ns, escape);
+ s_putc(ns, hexchar(*sp>>4));
+ s_putc(ns, hexchar(*sp));
+ } else
+ s_putc(ns, *sp);
+ }
+ s_terminate(ns);
+ s_free(s);
+ return ns;
+}
+
+int
+hex2uint(char x)
+{
+ if(x >= '0' && x <= '9')
+ return x - '0';
+ if(x >= 'A' && x <= 'F')
+ return (x - 'A') + 10;
+ if(x >= 'a' && x <= 'f')
+ return (x - 'a') + 10;
+ return -512;
+}
+
+/*
+ * rewrite a string to remove shell characters escapes
+ */
+extern String*
+unescapespecial(String *s)
+{
+ int c;
+ String *ns;
+ char *sp;
+ uint n;
+
+ if(strstr(s_to_c(s), escape) == 0)
+ return s;
+ n = strlen(escape);
+
+ ns = s_new();
+ for(sp = s_to_c(s); *sp; sp++){
+ if(strncmp(sp, escape, n) == 0){
+ c = (hex2uint(sp[n])<<4) | hex2uint(sp[n+1]);
+ if(c < 0)
+ s_putc(ns, *sp);
+ else {
+ s_putc(ns, c);
+ sp += n+2-1;
+ }
+ } else
+ s_putc(ns, *sp);
+ }
+ s_terminate(ns);
+ s_free(s);
+ return ns;
+
+}
+
+int
+returnable(char *path)
+{
+
+ return strcmp(path, "/dev/null") != 0;
+}
diff --git a/sys/src/cmd/upas/common/become.c b/sys/src/cmd/upas/common/become.c
new file mode 100755
index 000000000..8c012d212
--- /dev/null
+++ b/sys/src/cmd/upas/common/become.c
@@ -0,0 +1,28 @@
+#include "common.h"
+#include <auth.h>
+#include <ndb.h>
+
+/*
+ * become powerless user
+ */
+int
+become(char **cmd, char *who)
+{
+ int fd;
+
+ USED(cmd);
+ if(strcmp(who, "none") == 0) {
+ fd = open("#c/user", OWRITE);
+ if(fd < 0 || write(fd, "none", strlen("none")) < 0) {
+ werrstr("can't become none");
+ return -1;
+ }
+ close(fd);
+ if(newns("none", 0)) {
+ werrstr("can't set new namespace");
+ return -1;
+ }
+ }
+ return 0;
+}
+
diff --git a/sys/src/cmd/upas/common/common.h b/sys/src/cmd/upas/common/common.h
new file mode 100755
index 000000000..3f9acbd18
--- /dev/null
+++ b/sys/src/cmd/upas/common/common.h
@@ -0,0 +1,80 @@
+#include "sys.h"
+
+/* format of REMOTE FROM lines */
+extern char *REMFROMRE;
+extern int REMSENDERMATCH;
+extern int REMDATEMATCH;
+extern int REMSYSMATCH;
+
+/* format of mailbox FROM lines */
+#define IS_HEADER(p) ((p)[0]=='F'&&(p)[1]=='r'&&(p)[2]=='o'&&(p)[3]=='m'&&(p)[4]==' ')
+#define IS_TRAILER(p) ((p)[0]=='m'&&(p)[1]=='o'&&(p)[2]=='r'&&(p)[3]=='F'&&(p)[4]=='\n')
+extern char *FROMRE;
+extern int SENDERMATCH;
+extern int DATEMATCH;
+
+enum
+{
+ Elemlen= 28,
+ Errlen= ERRMAX,
+ Pathlen= 256,
+};
+enum { Atnoteunknown, Atnoterecog };
+
+/*
+ * routines in mail.c
+ */
+extern int print_header(Biobuf*, char*, char*);
+extern int print_remote_header(Biobuf*, char*, char*, char*);
+extern int parse_header(char*, String*, String*);
+
+/*
+ * routines in aux.c
+ */
+extern String *abspath(char*, char*, String*);
+extern String *mboxpath(char*, char*, String*, int);
+extern char *basename(char*);
+extern int delivery_status(String*);
+extern void append_match(Resub*, String*, int);
+extern int shellchars(char*);
+extern String* escapespecial(String*);
+extern String* unescapespecial(String*);
+extern int returnable(char*);
+
+/* in copymessage */
+extern int appendfiletombox(int, int);
+extern int appendfiletofile(int, int);
+
+/* mailbox types */
+#define MF_NORMAL 0
+#define MF_PIPE 1
+#define MF_FORWARD 2
+#define MF_NOMBOX 3
+#define MF_NOTMBOX 4
+
+/* a pipe between parent and child*/
+typedef struct {
+ Biobuf bb;
+ Biobuf *fp; /* parent process end*/
+ int fd; /* child process end*/
+} stream;
+
+/* a child process*/
+typedef struct process{
+ stream *std[3]; /* standard fd's*/
+ int pid; /* process identifier*/
+ int status; /* exit status*/
+ Waitmsg *waitmsg;
+} process;
+
+extern stream *instream(void);
+extern stream *outstream(void);
+extern void stream_free(stream*);
+extern process *noshell_proc_start(char**, stream*, stream*, stream*, int, char*);
+extern process *proc_start(char*, stream*, stream*, stream*, int, char*);
+extern int proc_wait(process*);
+extern int proc_free(process*);
+extern int proc_kill(process*);
+
+/* tell compiler we're using a value so it won't complain */
+#define USE(x) if(x)
diff --git a/sys/src/cmd/upas/common/config.c b/sys/src/cmd/upas/common/config.c
new file mode 100755
index 000000000..f322783ef
--- /dev/null
+++ b/sys/src/cmd/upas/common/config.c
@@ -0,0 +1,11 @@
+#include "common.h"
+
+char *MAILROOT = "/mail";
+char *UPASLOG = "/sys/log";
+char *UPASLIB = "/mail/lib";
+char *UPASBIN= "/bin/upas";
+char *UPASTMP = "/mail/tmp";
+char *SHELL = "/bin/rc";
+char *POST = "/sys/lib/post/dispatch";
+
+int MBOXMODE = 0662;
diff --git a/sys/src/cmd/upas/common/libsys.c b/sys/src/cmd/upas/common/libsys.c
new file mode 100755
index 000000000..f0b8f35a4
--- /dev/null
+++ b/sys/src/cmd/upas/common/libsys.c
@@ -0,0 +1,971 @@
+#include "common.h"
+#include <auth.h>
+#include <ndb.h>
+
+/*
+ * number of predefined fd's
+ */
+int nsysfile=3;
+
+static char err[Errlen];
+
+/*
+ * return the date
+ */
+extern char *
+thedate(void)
+{
+ static char now[64];
+ char *cp;
+
+ strcpy(now, ctime(time(0)));
+ cp = strchr(now, '\n');
+ if(cp)
+ *cp = 0;
+ return now;
+}
+
+/*
+ * return the user id of the current user
+ */
+extern char *
+getlog(void)
+{
+ static char user[64];
+ int fd;
+ int n;
+
+ fd = open("/dev/user", 0);
+ if(fd < 0)
+ return nil;
+ if((n=read(fd, user, sizeof(user)-1)) <= 0)
+ return nil;
+ close(fd);
+ user[n] = 0;
+ return user;
+}
+
+/*
+ * return the lock name (we use one lock per directory)
+ */
+static String *
+lockname(char *path)
+{
+ String *lp;
+ char *cp;
+
+ /*
+ * get the name of the lock file
+ */
+ lp = s_new();
+ cp = strrchr(path, '/');
+ if(cp)
+ s_nappend(lp, path, cp - path + 1);
+ s_append(lp, "L.mbox");
+
+ return lp;
+}
+
+int
+syscreatelocked(char *path, int mode, int perm)
+{
+ return create(path, mode, DMEXCL|perm);
+}
+
+int
+sysopenlocked(char *path, int mode)
+{
+/* return open(path, OEXCL|mode);/**/
+ return open(path, mode); /* until system call is fixed */
+}
+
+int
+sysunlockfile(int fd)
+{
+ return close(fd);
+}
+
+/*
+ * try opening a lock file. If it doesn't exist try creating it.
+ */
+static int
+openlockfile(Mlock *l)
+{
+ int fd;
+ Dir *d;
+ Dir nd;
+ char *p;
+
+ fd = open(s_to_c(l->name), OREAD);
+ if(fd >= 0){
+ l->fd = fd;
+ return 0;
+ }
+
+ d = dirstat(s_to_c(l->name));
+ if(d == nil){
+ /* file doesn't exist */
+ /* try creating it */
+ fd = create(s_to_c(l->name), OREAD, DMEXCL|0666);
+ if(fd >= 0){
+ nulldir(&nd);
+ nd.mode = DMEXCL|0666;
+ if(dirfwstat(fd, &nd) < 0){
+ /* if we can't chmod, don't bother */
+ /* live without the lock but log it */
+ syslog(0, "mail", "lock error: %s: %r", s_to_c(l->name));
+ remove(s_to_c(l->name));
+ }
+ l->fd = fd;
+ return 0;
+ }
+
+ /* couldn't create */
+ /* do we have write access to the directory? */
+ p = strrchr(s_to_c(l->name), '/');
+ if(p != 0){
+ *p = 0;
+ fd = access(s_to_c(l->name), 2);
+ *p = '/';
+ if(fd < 0){
+ /* live without the lock but log it */
+ syslog(0, "mail", "lock error: %s: %r", s_to_c(l->name));
+ return 0;
+ }
+ } else {
+ fd = access(".", 2);
+ if(fd < 0){
+ /* live without the lock but log it */
+ syslog(0, "mail", "lock error: %s: %r", s_to_c(l->name));
+ return 0;
+ }
+ }
+ } else
+ free(d);
+
+ return 1; /* try again later */
+}
+
+#define LSECS 5*60
+
+/*
+ * Set a lock for a particular file. The lock is a file in the same directory
+ * and has L. prepended to the name of the last element of the file name.
+ */
+extern Mlock *
+syslock(char *path)
+{
+ Mlock *l;
+ int tries;
+
+ l = mallocz(sizeof(Mlock), 1);
+ if(l == 0)
+ return nil;
+
+ l->name = lockname(path);
+
+ /*
+ * wait LSECS seconds for it to unlock
+ */
+ for(tries = 0; tries < LSECS*2; tries++){
+ switch(openlockfile(l)){
+ case 0:
+ return l;
+ case 1:
+ sleep(500);
+ break;
+ default:
+ goto noway;
+ }
+ }
+
+noway:
+ s_free(l->name);
+ free(l);
+ return nil;
+}
+
+/*
+ * like lock except don't wait
+ */
+extern Mlock *
+trylock(char *path)
+{
+ Mlock *l;
+ char buf[1];
+ int fd;
+
+ l = malloc(sizeof(Mlock));
+ if(l == 0)
+ return 0;
+
+ l->name = lockname(path);
+ if(openlockfile(l) != 0){
+ s_free(l->name);
+ free(l);
+ return 0;
+ }
+
+ /* fork process to keep lock alive */
+ switch(l->pid = rfork(RFPROC)){
+ default:
+ break;
+ case 0:
+ fd = l->fd;
+ for(;;){
+ sleep(1000*60);
+ if(pread(fd, buf, 1, 0) < 0)
+ break;
+ }
+ _exits(0);
+ }
+ return l;
+}
+
+extern void
+syslockrefresh(Mlock *l)
+{
+ char buf[1];
+
+ pread(l->fd, buf, 1, 0);
+}
+
+extern void
+sysunlock(Mlock *l)
+{
+ if(l == 0)
+ return;
+ if(l->name){
+ s_free(l->name);
+ }
+ if(l->fd >= 0)
+ close(l->fd);
+ if(l->pid > 0)
+ postnote(PNPROC, l->pid, "time to die");
+ free(l);
+}
+
+/*
+ * Open a file. The modes are:
+ *
+ * l - locked
+ * a - set append permissions
+ * r - readable
+ * w - writable
+ * A - append only (doesn't exist in Bio)
+ */
+extern Biobuf *
+sysopen(char *path, char *mode, ulong perm)
+{
+ int sysperm;
+ int sysmode;
+ int fd;
+ int docreate;
+ int append;
+ int truncate;
+ Dir *d, nd;
+ Biobuf *bp;
+
+ /*
+ * decode the request
+ */
+ sysperm = 0;
+ sysmode = -1;
+ docreate = 0;
+ append = 0;
+ truncate = 0;
+ for(; mode && *mode; mode++)
+ switch(*mode){
+ case 'A':
+ sysmode = OWRITE;
+ append = 1;
+ break;
+ case 'c':
+ docreate = 1;
+ break;
+ case 'l':
+ sysperm |= DMEXCL;
+ break;
+ case 'a':
+ sysperm |= DMAPPEND;
+ break;
+ case 'w':
+ if(sysmode == -1)
+ sysmode = OWRITE;
+ else
+ sysmode = ORDWR;
+ break;
+ case 'r':
+ if(sysmode == -1)
+ sysmode = OREAD;
+ else
+ sysmode = ORDWR;
+ break;
+ case 't':
+ truncate = 1;
+ break;
+ default:
+ break;
+ }
+ switch(sysmode){
+ case OREAD:
+ case OWRITE:
+ case ORDWR:
+ break;
+ default:
+ if(sysperm&DMAPPEND)
+ sysmode = OWRITE;
+ else
+ sysmode = OREAD;
+ break;
+ }
+
+ /*
+ * create file if we need to
+ */
+ if(truncate)
+ sysmode |= OTRUNC;
+ fd = open(path, sysmode);
+ if(fd < 0){
+ d = dirstat(path);
+ if(d == nil){
+ if(docreate == 0)
+ return 0;
+
+ fd = create(path, sysmode, sysperm|perm);
+ if(fd < 0)
+ return 0;
+ nulldir(&nd);
+ nd.mode = sysperm|perm;
+ dirfwstat(fd, &nd);
+ } else {
+ free(d);
+ return 0;
+ }
+ }
+
+ bp = (Biobuf*)malloc(sizeof(Biobuf));
+ if(bp == 0){
+ close(fd);
+ return 0;
+ }
+ memset(bp, 0, sizeof(Biobuf));
+ Binit(bp, fd, sysmode&~OTRUNC);
+
+ if(append)
+ Bseek(bp, 0, 2);
+ return bp;
+}
+
+/*
+ * close the file, etc.
+ */
+int
+sysclose(Biobuf *bp)
+{
+ int rv;
+
+ rv = Bterm(bp);
+ close(Bfildes(bp));
+ free(bp);
+ return rv;
+}
+
+/*
+ * create a file
+ */
+int
+syscreate(char *file, int mode, ulong perm)
+{
+ return create(file, mode, perm);
+}
+
+/*
+ * make a directory
+ */
+int
+sysmkdir(char *file, ulong perm)
+{
+ int fd;
+
+ if((fd = create(file, OREAD, DMDIR|perm)) < 0)
+ return -1;
+ close(fd);
+ return 0;
+}
+
+/*
+ * change the group of a file
+ */
+int
+syschgrp(char *file, char *group)
+{
+ Dir nd;
+
+ if(group == 0)
+ return -1;
+ nulldir(&nd);
+ nd.gid = group;
+ return dirwstat(file, &nd);
+}
+
+extern int
+sysdirreadall(int fd, Dir **d)
+{
+ return dirreadall(fd, d);
+}
+
+/*
+ * read in the system name
+ */
+extern char *
+sysname_read(void)
+{
+ static char name[128];
+ char *cp;
+
+ cp = getenv("site");
+ if(cp == 0 || *cp == 0)
+ cp = alt_sysname_read();
+ if(cp == 0 || *cp == 0)
+ cp = "kremvax";
+ strecpy(name, name+sizeof name, cp);
+ return name;
+}
+extern char *
+alt_sysname_read(void)
+{
+ static char name[128];
+ int n, fd;
+
+ fd = open("/dev/sysname", OREAD);
+ if(fd < 0)
+ return 0;
+ n = read(fd, name, sizeof(name)-1);
+ close(fd);
+ if(n <= 0)
+ return 0;
+ name[n] = 0;
+ return name;
+}
+
+/*
+ * get all names
+ */
+extern char**
+sysnames_read(void)
+{
+ static char **namev;
+ Ndbtuple *t, *nt;
+ int n;
+ char *cp;
+
+ if(namev)
+ return namev;
+
+ free(csgetvalue(0, "sys", alt_sysname_read(), "dom", &t));
+
+ n = 0;
+ for(nt = t; nt; nt = nt->entry)
+ if(strcmp(nt->attr, "dom") == 0)
+ n++;
+
+ namev = (char**)malloc(sizeof(char *)*(n+3));
+
+ if(namev){
+ n = 0;
+ namev[n++] = strdup(sysname_read());
+ cp = alt_sysname_read();
+ if(cp)
+ namev[n++] = strdup(cp);
+ for(nt = t; nt; nt = nt->entry)
+ if(strcmp(nt->attr, "dom") == 0)
+ namev[n++] = strdup(nt->val);
+ namev[n] = 0;
+ }
+ if(t)
+ ndbfree(t);
+
+ return namev;
+}
+
+/*
+ * read in the domain name
+ */
+extern char *
+domainname_read(void)
+{
+ char **namev;
+
+ for(namev = sysnames_read(); *namev; namev++)
+ if(strchr(*namev, '.'))
+ return *namev;
+ return 0;
+}
+
+/*
+ * return true if the last error message meant file
+ * did not exist.
+ */
+extern int
+e_nonexistent(void)
+{
+ rerrstr(err, sizeof(err));
+ return strcmp(err, "file does not exist") == 0;
+}
+
+/*
+ * return true if the last error message meant file
+ * was locked.
+ */
+extern int
+e_locked(void)
+{
+ rerrstr(err, sizeof(err));
+ return strcmp(err, "open/create -- file is locked") == 0;
+}
+
+/*
+ * return the length of a file
+ */
+extern long
+sysfilelen(Biobuf *fp)
+{
+ Dir *d;
+ long rv;
+
+ d = dirfstat(Bfildes(fp));
+ if(d == nil)
+ return -1;
+ rv = d->length;
+ free(d);
+ return rv;
+}
+
+/*
+ * remove a file
+ */
+extern int
+sysremove(char *path)
+{
+ return remove(path);
+}
+
+/*
+ * rename a file, fails unless both are in the same directory
+ */
+extern int
+sysrename(char *old, char *new)
+{
+ Dir d;
+ char *obase;
+ char *nbase;
+
+ obase = strrchr(old, '/');
+ nbase = strrchr(new, '/');
+ if(obase){
+ if(nbase == 0)
+ return -1;
+ if(strncmp(old, new, obase-old) != 0)
+ return -1;
+ nbase++;
+ } else {
+ if(nbase)
+ return -1;
+ nbase = new;
+ }
+ nulldir(&d);
+ d.name = nbase;
+ return dirwstat(old, &d);
+}
+
+/*
+ * see if a file exists
+ */
+extern int
+sysexist(char *file)
+{
+ Dir *d;
+
+ d = dirstat(file);
+ if(d == nil)
+ return 0;
+ free(d);
+ return 1;
+}
+
+/*
+ * return nonzero if file is a directory
+ */
+extern int
+sysisdir(char *file)
+{
+ Dir *d;
+ int rv;
+
+ d = dirstat(file);
+ if(d == nil)
+ return 0;
+ rv = d->mode & DMDIR;
+ free(d);
+ return rv;
+}
+
+/*
+ * kill a process or process group
+ */
+
+static int
+stomp(int pid, char *file)
+{
+ char name[64];
+ int fd;
+
+ snprint(name, sizeof(name), "/proc/%d/%s", pid, file);
+ fd = open(name, 1);
+ if(fd < 0)
+ return -1;
+ if(write(fd, "die: yankee pig dog\n", sizeof("die: yankee pig dog\n") - 1) <= 0){
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return 0;
+
+}
+
+/*
+ * kill a process
+ */
+extern int
+syskill(int pid)
+{
+ return stomp(pid, "note");
+
+}
+
+/*
+ * kill a process group
+ */
+extern int
+syskillpg(int pid)
+{
+ return stomp(pid, "notepg");
+}
+
+extern int
+sysdetach(void)
+{
+ if(rfork(RFENVG|RFNAMEG|RFNOTEG) < 0) {
+ werrstr("rfork failed");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * catch a write on a closed pipe
+ */
+static int *closedflag;
+static int
+catchpipe(void *a, char *msg)
+{
+ static char *foo = "sys: write on closed pipe";
+
+ USED(a);
+ if(strncmp(msg, foo, strlen(foo)) == 0){
+ if(closedflag)
+ *closedflag = 1;
+ return 1;
+ }
+ return 0;
+}
+void
+pipesig(int *flagp)
+{
+ closedflag = flagp;
+ atnotify(catchpipe, 1);
+}
+void
+pipesigoff(void)
+{
+ atnotify(catchpipe, 0);
+}
+
+void
+exit(int i)
+{
+ char buf[32];
+
+ if(i == 0)
+ exits(0);
+ snprint(buf, sizeof(buf), "%d", i);
+ exits(buf);
+}
+
+static int
+islikeatty(int fd)
+{
+ char buf[64];
+
+ if(fd2path(fd, buf, sizeof buf) != 0)
+ return 0;
+
+ /* might be /mnt/term/dev/cons */
+ return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
+}
+
+extern int
+holdon(void)
+{
+ int fd;
+
+ if(!islikeatty(0))
+ return -1;
+
+ fd = open("/dev/consctl", OWRITE);
+ write(fd, "holdon", 6);
+
+ return fd;
+}
+
+extern int
+sysopentty(void)
+{
+ return open("/dev/cons", ORDWR);
+}
+
+extern void
+holdoff(int fd)
+{
+ write(fd, "holdoff", 7);
+ close(fd);
+}
+
+extern int
+sysfiles(void)
+{
+ return 128;
+}
+
+/*
+ * expand a path relative to the user's mailbox directory
+ *
+ * if the path starts with / or ./, don't change it
+ *
+ */
+extern String *
+mboxpath(char *path, char *user, String *to, int dot)
+{
+ if (dot || *path=='/' || strncmp(path, "./", 2) == 0
+ || strncmp(path, "../", 3) == 0) {
+ to = s_append(to, path);
+ } else {
+ to = s_append(to, MAILROOT);
+ to = s_append(to, "/box/");
+ to = s_append(to, user);
+ to = s_append(to, "/");
+ to = s_append(to, path);
+ }
+ return to;
+}
+
+extern String *
+mboxname(char *user, String *to)
+{
+ return mboxpath("mbox", user, to, 0);
+}
+
+extern String *
+deadletter(String *to) /* pass in sender??? */
+{
+ char *cp;
+
+ cp = getlog();
+ if(cp == 0)
+ return 0;
+ return mboxpath("dead.letter", cp, to, 0);
+}
+
+char *
+homedir(char *user)
+{
+ USED(user);
+ return getenv("home");
+}
+
+String *
+readlock(String *file)
+{
+ char *cp;
+
+ cp = getlog();
+ if(cp == 0)
+ return 0;
+ return mboxpath("reading", cp, file, 0);
+}
+
+String *
+username(String *from)
+{
+ int n;
+ Biobuf *bp;
+ char *p, *q;
+ String *s;
+
+ bp = Bopen("/adm/keys.who", OREAD);
+ if(bp == 0)
+ bp = Bopen("/adm/netkeys.who", OREAD);
+ if(bp == 0)
+ return 0;
+
+ s = 0;
+ n = strlen(s_to_c(from));
+ for(;;) {
+ p = Brdline(bp, '\n');
+ if(p == 0)
+ break;
+ p[Blinelen(bp)-1] = 0;
+ if(strncmp(p, s_to_c(from), n))
+ continue;
+ p += n;
+ if(*p != ' ' && *p != '\t') /* must be full match */
+ continue;
+ while(*p && (*p == ' ' || *p == '\t'))
+ p++;
+ if(*p == 0)
+ continue;
+ for(q = p; *q; q++)
+ if(('0' <= *q && *q <= '9') || *q == '<')
+ break;
+ while(q > p && q[-1] != ' ' && q[-1] != '\t')
+ q--;
+ while(q > p && (q[-1] == ' ' || q[-1] == '\t'))
+ q--;
+ *q = 0;
+ s = s_new();
+ s_append(s, "\"");
+ s_append(s, p);
+ s_append(s, "\"");
+ break;
+ }
+ Bterm(bp);
+ return s;
+}
+
+char *
+remoteaddr(int fd, char *dir)
+{
+ char buf[128], *p;
+ int n;
+
+ if(dir == 0){
+ if(fd2path(fd, buf, sizeof(buf)) != 0)
+ return "";
+
+ /* parse something of the form /net/tcp/nnnn/data */
+ p = strrchr(buf, '/');
+ if(p == 0)
+ return "";
+ strncpy(p+1, "remote", sizeof(buf)-(p-buf)-2);
+ } else
+ snprint(buf, sizeof buf, "%s/remote", dir);
+ buf[sizeof(buf)-1] = 0;
+
+ fd = open(buf, OREAD);
+ if(fd < 0)
+ return "";
+ n = read(fd, buf, sizeof(buf)-1);
+ close(fd);
+ if(n > 0){
+ buf[n] = 0;
+ p = strchr(buf, '!');
+ if(p)
+ *p = 0;
+ return strdup(buf);
+ }
+ return "";
+}
+
+// create a file and
+// 1) ensure the modes we asked for
+// 2) make gid == uid
+static int
+docreate(char *file, int perm)
+{
+ int fd;
+ Dir ndir;
+ Dir *d;
+
+ // create the mbox
+ fd = create(file, OREAD, perm);
+ if(fd < 0){
+ fprint(2, "couldn't create %s\n", file);
+ return -1;
+ }
+ d = dirfstat(fd);
+ if(d == nil){
+ fprint(2, "couldn't stat %s\n", file);
+ return -1;
+ }
+ nulldir(&ndir);
+ ndir.mode = perm;
+ ndir.gid = d->uid;
+ if(dirfwstat(fd, &ndir) < 0)
+ fprint(2, "couldn't chmod %s: %r\n", file);
+ close(fd);
+ return 0;
+}
+
+// create a mailbox
+int
+creatembox(char *user, char *folder)
+{
+ char *p;
+ String *mailfile;
+ char buf[512];
+ Mlock *ml;
+
+ mailfile = s_new();
+ if(folder == 0)
+ mboxname(user, mailfile);
+ else {
+ snprint(buf, sizeof(buf), "%s/mbox", folder);
+ mboxpath(buf, user, mailfile, 0);
+ }
+
+ // don't destroy existing mailbox
+ if(access(s_to_c(mailfile), 0) == 0){
+ fprint(2, "mailbox already exists\n");
+ return -1;
+ }
+ fprint(2, "creating new mbox: %s\n", s_to_c(mailfile));
+
+ // make sure preceding levels exist
+ for(p = s_to_c(mailfile); p; p++) {
+ if(*p == '/') /* skip leading or consecutive slashes */
+ continue;
+ p = strchr(p, '/');
+ if(p == 0)
+ break;
+ *p = 0;
+ if(access(s_to_c(mailfile), 0) != 0){
+ if(docreate(s_to_c(mailfile), DMDIR|0711) < 0)
+ return -1;
+ }
+ *p = '/';
+ }
+
+ // create the mbox
+ if(docreate(s_to_c(mailfile), 0622|DMAPPEND|DMEXCL) < 0)
+ return -1;
+
+ /*
+ * create the lock file if it doesn't exist
+ */
+ ml = trylock(s_to_c(mailfile));
+ if(ml != nil)
+ sysunlock(ml);
+
+ return 0;
+}
diff --git a/sys/src/cmd/upas/common/mail.c b/sys/src/cmd/upas/common/mail.c
new file mode 100755
index 000000000..5347c6550
--- /dev/null
+++ b/sys/src/cmd/upas/common/mail.c
@@ -0,0 +1,57 @@
+#include "common.h"
+
+/* format of REMOTE FROM lines */
+char *REMFROMRE =
+ "^>?From[ \t]+((\".*\")?[^\" \t]+?(\".*\")?[^\" \t]+?)[ \t]+(.+)[ \t]+remote[ \t]+from[ \t]+(.*)\n$";
+int REMSENDERMATCH = 1;
+int REMDATEMATCH = 4;
+int REMSYSMATCH = 5;
+
+/* format of LOCAL FROM lines */
+char *FROMRE =
+ "^>?From[ \t]+((\".*\")?[^\" \t]+?(\".*\")?[^\" \t]+?)[ \t]+(.+)\n$";
+int SENDERMATCH = 1;
+int DATEMATCH = 4;
+
+/* output a unix style local header */
+int
+print_header(Biobuf *fp, char *sender, char *date)
+{
+ return Bprint(fp, "From %s %s\n", sender, date);
+}
+
+/* output a unix style remote header */
+int
+print_remote_header(Biobuf *fp, char *sender, char *date, char *system)
+{
+ return Bprint(fp, "From %s %s remote from %s\n", sender, date, system);
+}
+
+/* parse a mailbox style header */
+int
+parse_header(char *line, String *sender, String *date)
+{
+ if (!IS_HEADER(line))
+ return -1;
+ line += sizeof("From ") - 1;
+ s_restart(sender);
+ while(*line==' '||*line=='\t')
+ line++;
+ if(*line == '"'){
+ s_putc(sender, *line++);
+ while(*line && *line != '"')
+ s_putc(sender, *line++);
+ s_putc(sender, *line++);
+ } else {
+ while(*line && *line != ' ' && *line != '\t')
+ s_putc(sender, *line++);
+ }
+ s_terminate(sender);
+ s_restart(date);
+ while(*line==' '||*line=='\t')
+ line++;
+ while(*line)
+ s_putc(date, *line++);
+ s_terminate(date);
+ return 0;
+}
diff --git a/sys/src/cmd/upas/common/makefile b/sys/src/cmd/upas/common/makefile
new file mode 100755
index 000000000..c7beae817
--- /dev/null
+++ b/sys/src/cmd/upas/common/makefile
@@ -0,0 +1,18 @@
+CFLAGS=${UNIX} -g -I. -I../libc -I../common -I/usr/include ${SCFLAGS}
+OBJS=mail.o aux.o string.o ${SYSOBJ}
+AR=ar
+.c.o: ; ${CC} -c ${CFLAGS} $*.c
+
+common.a: ${OBJS}
+ ${AR} cr common.a ${OBJS}
+ -ranlib common.a
+
+aux.o: aux.h string.h mail.h
+string.o: string.h mail.h
+mail.o: mail.h
+syslog.o: sys.h
+mail.h: sys.h
+
+clean:
+ -rm -f *.[oO] core a.out *.a *.sL common.a
+
diff --git a/sys/src/cmd/upas/common/mkfile b/sys/src/cmd/upas/common/mkfile
new file mode 100755
index 000000000..5d817b72b
--- /dev/null
+++ b/sys/src/cmd/upas/common/mkfile
@@ -0,0 +1,24 @@
+</$objtype/mkfile
+
+LIB=libcommon.a$O
+OFILES=aux.$O\
+ become.$O\
+ mail.$O\
+ process.$O\
+ libsys.$O\
+ config.$O\
+ appendfiletombox.$O\
+
+HFILES=common.h\
+ sys.h\
+
+UPDATE=\
+ mkfile\
+ ${OFILES:%.$O=%.c}\
+ $HFILES\
+
+</sys/src/cmd/mklib
+
+nuke:V:
+ mk clean
+ rm -f libcommon.a[$OS]
diff --git a/sys/src/cmd/upas/common/process.c b/sys/src/cmd/upas/common/process.c
new file mode 100755
index 000000000..f698c9600
--- /dev/null
+++ b/sys/src/cmd/upas/common/process.c
@@ -0,0 +1,173 @@
+#include "common.h"
+
+/* make a stream to a child process */
+extern stream *
+instream(void)
+{
+ stream *rv;
+ int pfd[2];
+
+ if ((rv = (stream *)malloc(sizeof(stream))) == 0)
+ return 0;
+ memset(rv, 0, sizeof(stream));
+ if (pipe(pfd) < 0)
+ return 0;
+ if(Binit(&rv->bb, pfd[1], OWRITE) < 0){
+ close(pfd[0]);
+ close(pfd[1]);
+ return 0;
+ }
+ rv->fp = &rv->bb;
+ rv->fd = pfd[0];
+ return rv;
+}
+
+/* make a stream from a child process */
+extern stream *
+outstream(void)
+{
+ stream *rv;
+ int pfd[2];
+
+ if ((rv = (stream *)malloc(sizeof(stream))) == 0)
+ return 0;
+ memset(rv, 0, sizeof(stream));
+ if (pipe(pfd) < 0)
+ return 0;
+ if (Binit(&rv->bb, pfd[0], OREAD) < 0){
+ close(pfd[0]);
+ close(pfd[1]);
+ return 0;
+ }
+ rv->fp = &rv->bb;
+ rv->fd = pfd[1];
+ return rv;
+}
+
+extern void
+stream_free(stream *sp)
+{
+ int fd;
+
+ close(sp->fd);
+ fd = Bfildes(sp->fp);
+ Bterm(sp->fp);
+ close(fd);
+ free((char *)sp);
+}
+
+/* start a new process */
+extern process *
+noshell_proc_start(char **av, stream *inp, stream *outp, stream *errp, int newpg, char *who)
+{
+ process *pp;
+ int i, n;
+
+ if ((pp = (process *)malloc(sizeof(process))) == 0) {
+ if (inp != 0)
+ stream_free(inp);
+ if (outp != 0)
+ stream_free(outp);
+ if (errp != 0)
+ stream_free(errp);
+ return 0;
+ }
+ pp->std[0] = inp;
+ pp->std[1] = outp;
+ pp->std[2] = errp;
+ switch (pp->pid = fork()) {
+ case -1:
+ proc_free(pp);
+ return 0;
+ case 0:
+ if(newpg)
+ sysdetach();
+ for (i=0; i<3; i++)
+ if (pp->std[i] != 0){
+ close(Bfildes(pp->std[i]->fp));
+ while(pp->std[i]->fd < 3)
+ pp->std[i]->fd = dup(pp->std[i]->fd, -1);
+ }
+ for (i=0; i<3; i++)
+ if (pp->std[i] != 0)
+ dup(pp->std[i]->fd, i);
+ for (n = sysfiles(); i < n; i++)
+ close(i);
+ if(who)
+ become(av, who);
+ exec(av[0], av);
+ perror("proc_start");
+ exits("proc_start");
+ default:
+ for (i=0; i<3; i++)
+ if (pp->std[i] != 0) {
+ close(pp->std[i]->fd);
+ pp->std[i]->fd = -1;
+ }
+ return pp;
+ }
+}
+
+/* start a new process under a shell */
+extern process *
+proc_start(char *cmd, stream *inp, stream *outp, stream *errp, int newpg, char *who)
+{
+ char *av[4];
+
+ av[0] = SHELL;
+ av[1] = "-c";
+ av[2] = cmd;
+ av[3] = 0;
+ return noshell_proc_start(av, inp, outp, errp, newpg, who);
+}
+
+/* wait for a process to stop */
+extern int
+proc_wait(process *pp)
+{
+ Waitmsg *status;
+ char err[Errlen];
+
+ for(;;){
+ status = wait();
+ if(status == nil){
+ rerrstr(err, sizeof(err));
+ if(strstr(err, "interrupt") == 0)
+ break;
+ }
+ if (status->pid==pp->pid)
+ break;
+ }
+ pp->pid = -1;
+ if(status == nil)
+ pp->status = -1;
+ else
+ pp->status = status->msg[0];
+ pp->waitmsg = status;
+ return pp->status;
+}
+
+/* free a process */
+extern int
+proc_free(process *pp)
+{
+ int i;
+
+ if(pp->std[1] == pp->std[2])
+ pp->std[2] = 0; /* avoid freeing it twice */
+ for (i = 0; i < 3; i++)
+ if (pp->std[i])
+ stream_free(pp->std[i]);
+ if (pp->pid >= 0)
+ proc_wait(pp);
+ free(pp->waitmsg);
+ free((char *)pp);
+ return 0;
+}
+
+/* kill a process */
+extern int
+proc_kill(process *pp)
+{
+ return syskill(pp->pid);
+}
diff --git a/sys/src/cmd/upas/common/sys.h b/sys/src/cmd/upas/common/sys.h
new file mode 100755
index 000000000..a50f27284
--- /dev/null
+++ b/sys/src/cmd/upas/common/sys.h
@@ -0,0 +1,85 @@
+/*
+ * System dependent header files for research
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <regexp.h>
+#include <bio.h>
+#include "String.h"
+
+/*
+ * for the lock routines in libsys.c
+ */
+typedef struct Mlock Mlock;
+struct Mlock {
+ int fd;
+ int pid;
+ String *name;
+};
+
+/*
+ * from config.c
+ */
+extern char *MAILROOT; /* root of mail system */
+extern char *UPASLOG; /* log directory */
+extern char *UPASLIB; /* upas library directory */
+extern char *UPASBIN; /* upas binary directory */
+extern char *UPASTMP; /* temporary directory */
+extern char *SHELL; /* path name of shell */
+extern char *POST; /* path name of post server addresses */
+extern int MBOXMODE; /* default mailbox protection mode */
+
+/*
+ * files in libsys.c
+ */
+extern char *sysname_read(void);
+extern char *alt_sysname_read(void);
+extern char *domainname_read(void);
+extern char **sysnames_read(void);
+extern char *getlog(void);
+extern char *thedate(void);
+extern Biobuf *sysopen(char*, char*, ulong);
+extern int sysopentty(void);
+extern int sysclose(Biobuf*);
+extern int sysmkdir(char*, ulong);
+extern int syschgrp(char*, char*);
+extern Mlock *syslock(char *);
+extern void sysunlock(Mlock *);
+extern void syslockrefresh(Mlock *);
+extern int e_nonexistent(void);
+extern int e_locked(void);
+extern long sysfilelen(Biobuf*);
+extern int sysremove(char*);
+extern int sysrename(char*, char*);
+extern int sysexist(char*);
+extern int sysisdir(char*);
+extern int syskill(int);
+extern int syskillpg(int);
+extern int syscreate(char*, int, ulong);
+extern Mlock *trylock(char *);
+extern void exit(int);
+extern void pipesig(int*);
+extern void pipesigoff(void);
+extern int holdon(void);
+extern void holdoff(int);
+extern int syscreatelocked(char*, int, int);
+extern int sysopenlocked(char*, int);
+extern int sysunlockfile(int);
+extern int sysfiles(void);
+extern int become(char**, char*);
+extern int sysdetach(void);
+extern int sysdirreadall(int, Dir**);
+extern String *username(String*);
+extern char* remoteaddr(int, char*);
+extern int creatembox(char*, char*);
+
+extern String *readlock(String*);
+extern char *homedir(char*);
+extern String *mboxname(char*, String*);
+extern String *deadletter(String*);
+
+/*
+ * maximum size for a file path
+ */
+#define MAXPATHLEN 128