| #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) |
| { |
| return getuser(); |
| } |
| #if 0 /* jpc */ |
| 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; |
| } |
| #endif /* jpc */ |
| /* |
| * 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; |
| Ndb* db; |
| Ndbs s; |
| int n; |
| char *cp; |
| |
| if(namev) |
| return namev; |
| |
| /* free(csgetvalue(0, "sys", alt_sysname_read(), "dom", &t)); jpc */ |
| db = ndbopen(unsharp("#9/ndb/local")); |
| free(ndbgetvalue(db, &s, "sys", sysname(),"dom", &t)); |
| /* t = nil; /* jpc */ |
| /* fprint(2,"csgetvalue called: fixme"); /* jpc */ |
| |
| 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 |
| exit9(int i) |
| { |
| char buf[32]; |
| |
| if(i == 0) |
| exits(0); |
| snprint(buf, sizeof(buf), "%d", i); |
| exits(buf); |
| } |
| |
| static int |
| islikeatty(int fd) |
| { |
| Dir *d; |
| int rv; |
| |
| d = dirfstat(fd); |
| if(d == nil) |
| return 0; |
| rv = strcmp(d->name, "cons") == 0; |
| free(d); |
| return rv; |
| } |
| |
| #if 0 |
| /* jpc */ |
| 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; |
| } |
| #endif |
| |
| 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, unsharp(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){ |
| fprint(2,"remoteaddr: called fd2path: fixme\n"); /* jpc |
| 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; |
| } |