Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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;
}