889 lines
13 KiB
C
889 lines
13 KiB
C
#include <u.h>
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#include <netdb.h>
|
|
#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();
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
static char *unix_hostname_read(void);
|
|
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)
|
|
{
|
|
char *cp;
|
|
static char name[128];
|
|
|
|
cp = getenv("sysname");
|
|
if(cp == 0 || *cp == 0)
|
|
cp = unix_hostname_read();
|
|
if(cp == 0 || *cp == 0)
|
|
return 0;
|
|
strecpy(name, name+sizeof name, cp);
|
|
return name;
|
|
}
|
|
static char *
|
|
unix_hostname_read(void)
|
|
{
|
|
static char hostname[256];
|
|
|
|
if(gethostname(hostname, sizeof hostname) < 0)
|
|
return nil;
|
|
return hostname;
|
|
}
|
|
|
|
/*
|
|
* get all names
|
|
*/
|
|
extern char**
|
|
sysnames_read(void)
|
|
{
|
|
static char **namev;
|
|
struct hostent *h;
|
|
char **p, **a;
|
|
|
|
if(namev)
|
|
return namev;
|
|
|
|
h = gethostbyname(alt_sysname_read());
|
|
for(p=h->h_aliases; *p; p++)
|
|
;
|
|
|
|
namev = malloc((2+p-h->h_aliases)*sizeof namev[0]);
|
|
if(namev == 0)
|
|
return 0;
|
|
|
|
a = namev;
|
|
*a++ = strdup(h->h_name);
|
|
for(p=h->h_aliases; *p; p++)
|
|
*a++ = strdup(*p);
|
|
*a = 0;
|
|
|
|
return namev;
|
|
}
|
|
|
|
/*
|
|
* read in the domain name.
|
|
* chop off beginning pieces until we find one with an mx record.
|
|
*/
|
|
extern char *
|
|
domainname_read(void)
|
|
{
|
|
char **namev, *p;
|
|
Ndbtuple *t;
|
|
|
|
for(namev = sysnames_read(); *namev; namev++){
|
|
if(strchr(*namev, '.')){
|
|
for(p=*namev-1; p && *++p; p=strchr(p, '.')){
|
|
if((t = dnsquery(nil, p, "mx")) != nil){
|
|
ndbfree(t);
|
|
return p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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
|
|
*/
|
|
extern int
|
|
syskill(int pid)
|
|
{
|
|
return postnote(PNPROC, pid, "kill");
|
|
}
|
|
|
|
/*
|
|
* kill a process group
|
|
*/
|
|
extern int
|
|
syskillpg(int pid)
|
|
{
|
|
return postnote(PNGROUP, pid, "kill");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
extern int
|
|
holdon(void)
|
|
{
|
|
/* XXX talk to 9term? */
|
|
return -1;
|
|
}
|
|
|
|
extern int
|
|
sysopentty(void)
|
|
{
|
|
return open("/dev/tty", 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)
|
|
{
|
|
char *dir;
|
|
String *s;
|
|
|
|
if (dot || *path=='/' || strncmp(path, "./", 2) == 0
|
|
|| strncmp(path, "../", 3) == 0) {
|
|
to = s_append(to, path);
|
|
} else {
|
|
if ((dir = homedir(user)) != nil) {
|
|
s = s_copy(dir);
|
|
s_append(s, "/mail/");
|
|
if(access(s_to_c(s), AEXIST) >= 0){
|
|
to = s_append(to, s_to_c(s));
|
|
s_free(s);
|
|
to = s_append(to, path);
|
|
return to;
|
|
}
|
|
s_free(s);
|
|
}
|
|
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);
|
|
}
|
|
|
|
String *
|
|
readlock(String *file)
|
|
{
|
|
char *cp;
|
|
|
|
cp = getlog();
|
|
if(cp == 0)
|
|
return 0;
|
|
return mboxpath("reading", cp, file, 0);
|
|
}
|
|
|
|
String *
|
|
username(String *from)
|
|
{
|
|
String* s;
|
|
struct passwd* pw;
|
|
|
|
setpwent();
|
|
while((pw = getpwent()) != nil){
|
|
if(strcmp(s_to_c(from), pw->pw_name) == 0){
|
|
s = s_new();
|
|
s_append(s, "\"");
|
|
s_append(s, pw->pw_gecos);
|
|
s_append(s, "\"");
|
|
return s;
|
|
}
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
char *
|
|
homedir(char *user)
|
|
{
|
|
static char buf[1024];
|
|
struct passwd* pw;
|
|
|
|
setpwent();
|
|
while((pw = getpwent()) != nil)
|
|
if(strcmp(user, pw->pw_name) == 0){
|
|
strecpy(buf, buf+sizeof buf, pw->pw_dir);
|
|
return buf;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
char *
|
|
remoteaddr(int fd, char *dir)
|
|
{
|
|
char *raddr;
|
|
NetConnInfo *nci;
|
|
|
|
if((nci = getnetconninfo(dir, fd)) == nil)
|
|
return nil;
|
|
raddr = strdup(nci->raddr);
|
|
freenetconninfo(nci);
|
|
return raddr;
|
|
}
|
|
|
|
/* 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;
|
|
}
|