blob: b439cfde1180fe496b3c50ba8de6c946203d0a1e [file] [log] [blame]
#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;
sysmode |= OLOCK;
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());
if(h == nil)
return 0;
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; 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;
}