/*
* Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
* This will be free software, but only when it is finished.
*/
/*
* Lots of modifications (new guts, more or less..) by
* Matti Aarnio <mea@nic.funet.fi> (copyright) 1992-2003
*/
/*
* ZMailer router, main and miscellany routines.
*/
#include "mailer.h"
#include <grp.h>
#include <pwd.h>
#include <signal.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/file.h>
#include "mail.h"
#include "zsyslog.h"
#include "zmsignal.h"
#include "interpret.h"
#include "splay.h"
#include "prototypes.h"
extern const char *postoffice; /* At libzmailer.a: mail.c */
extern struct shCmd fnctns[];
extern time_t time __((time_t *));
static void initialize __((const char *configfile, int argc, const char *argv[]));
static void logit __((const char *file, const char *id, const char *from, const char *to));
extern const char *progname; /* At zmsh_init() et.friends */
const char * mailshare;
const char * myhostname = 0;
const char * pidfile = PID_ROUTER;
const char * logfn;
time_t now;
extern memtypes stickymem;
extern conscell *s_value;
#include "shmmib.h"
int mustexit = 0;
int canexit = 0;
int router_id = 0;
int deferit;
int deferuid;
int savefile = 0;
const char * zshopts = "-O";
int nosyslog = 1;
int routerdirloops = 0;
int do_hdr_warning = 0;
int workermode = 0;
int nrouters = 1;
int
main(argc, argv)
int argc;
const char *argv[];
{
int c, errflg, daemonflg, killflg, interactiveflg, tac;
int version;
long offout, offerr;
char *config;
const char *tav[20], *av[3], *cp;
#ifdef XMEM
FILE *fp;
#endif /* XMEM */
#ifdef HAVE_SETGROUPS
/* We null supplementary groups list entirely */
setgroups(0, NULL);
#endif
#if 1 /* LINE BUFFERED */
setvbuf(stdout, (char *)NULL, _IOLBF, 0);
setvbuf(stderr, (char *)NULL, _IOLBF, 0);
#else /* NOT BUFFERED AT ALL */
setvbuf(stdout, (char *)NULL, _IONBF, 0);
setvbuf(stderr, (char *)NULL, _IONBF, 0);
#endif
progname = strrchr(argv[0], '/');
if (progname == NULL)
progname = argv[0];
else
++progname;
logfn = config = NULL;
errflg = daemonflg = killflg = interactiveflg = version = 0;
tac = 0;
nrouters = 1;
while (1) {
c = zgetopt(argc, (char*const*)argv, "m:n:dikf:o:t:L:P:r:sSVwWZ:");
if (c == EOF)
break;
switch (c) {
case 'd': /* become a daemon */
daemonflg = 1;
break;
case 'm':
#ifdef XMEM
{ /* Rewritten to be portable... Storing to
fileno()" is not guaranteed to success.. */
int fd = open(zoptarg, O_RDWR|O_CREAT|O_TRUNC,0644);
if (fd >= 0) {
dup2(fd,30); /* we ASSUME have far less fd's
in use.. */
close(fd);
fp = fdopen(30,"w+");
if (!fp) break;
mal_setstatsfile(fp);
mal_trace(1);
mal_debug(3);
}
}
#endif /* XMEM */
break;
case 'n':
nrouters = atoi(zoptarg);
if (nrouters < 1)
nrouters = 1;
break;
case 'o':
zshopts = zoptarg;
break;
case 't':
if (tac < (sizeof tav)/(sizeof tav[0]))
tav[++tac] = zoptarg;
else {
fprintf(stderr, "Too many trace options!\n");
fprintf(stderr, "Ignoring '%s'\n", zoptarg);
}
break;
case 'f': /* override default config file */
config = (char*) zoptarg;
break;
case 'i': /* first read config file, then read from tty */
interactiveflg = 1;
break;
case 'k': /* kill the previous daemon upon startup */
killflg = 1;
break;
case 's':
stability = !stability;
break;
case 'S': /* Logging also always to SYSLOG,
not only at serious stuff */
nosyslog = 0;
break;
case 'L': /* override default log file */
logfn = zoptarg;
break;
case 'P': /* override default postoffice */
postoffice = zoptarg;
break;
case 'V':
version = 1;
break;
case 'w':
workermode = 1;
break;
case 'W':
do_hdr_warning = !do_hdr_warning;
break;
case 'r':
routerdirloops = atoi(zoptarg);
if (routerdirloops < 0)
routerdirloops = 0;
break;
case 'Z':
if (readzenv(zoptarg) == 0)
++errflg;
break;
case '?':
default:
++errflg;
break;
}
}
if (errflg || (interactiveflg && daemonflg)) {
fprintf(stderr,
"Usage: %s [ -dikV -n #routers -t traceflag -f configfile -L logfile -P postoffice -Z zenvfile]\n",
progname);
exit(128+errflg);
}
time(&now);
mailshare = getzenv("MAILSHARE");
if (mailshare == NULL)
mailshare = MAILSHARE;
if (config == NULL) {
/* we don't need to remember this for long */
config = smalloc(MEM_TEMP, 3 + (u_int)(strlen(mailshare)
+ strlen(progname)
+ strlen(cf_suffix)));
sprintf(config, "%s/%s.%s",
mailshare, progname, cf_suffix);
}
if (postoffice == NULL &&
(postoffice = getzenv("POSTOFFICE")) == NULL)
postoffice = POSTOFFICE;
if (daemonflg || killflg) {
/* Daemon attaches the SHM block, and may complain, but will not
give up.. instead uses builtin fallback */
int r = Z_SHM_MIB_Attach (1); /* R/W mode */
if (r < 0) {
/* Error processing -- magic set of constants: */
switch (r) {
case -1:
/* fprintf(stderr, "No ZENV variable: SNMPSHAREDFILE\n"); */
break;
case -2:
perror("Failed to open for exclusively creating of the SHMSHAREDFILE");
break;
case -3:
perror("Failure during creation fill of SGMSHAREDFILE");
break;
case -4:
perror("Failed to open the SHMSHAREDFILE at all");
break;
case -5:
perror("The SHMSHAREDFILE isn't of proper size! ");
break;
case -6:
perror("Failed to mmap() of SHMSHAREDFILE into memory");
break;
case -7:
fprintf(stderr, "The SHMSHAREDFILE has magic value mismatch!\n");
break;
default:
break;
}
/* return; NO giving up! */
}
}
if (killflg && !daemonflg) {
killprevious(-SIGTERM, pidfile);
exit(0);
}
getnobody();
c = zoptind; /* save optind since builtins can interfere with it */
if (daemonflg && logfn == NULL) {
if ((cp = (char *) getzenv("LOGDIR")) != NULL)
logdir = cp;
logfn = smalloc(MEM_PERM, 2 + (u_int)(strlen(logdir)
+ strlen(progname)));
sprintf((char*)logfn, "%s/%s", logdir, progname);
}
if (logfn != NULL) {
/* loginit is a signal handler, so can't pass log */
if (loginit(SIGHUP) < 0) /* do setlinebuf() there */
die(1, "log initialization failure");
} else
signal(SIGHUP, SIG_IGN); /* no surprises please */
if (version || interactiveflg || tac > 0) {
prversion("router");
if (version)
exit(0);
putc('\n', stderr);
}
if (tac > 0) { /* turn on some trace/debug flags */
tav[0] = "debug";
/* lax, no NULL guard on end of tav */
run_trace(++tac, tav);
}
stickymem = MEM_PERM;
/* We (and our children) run with SIGPIPE ignored.. */
SIGNAL_HANDLE(SIGPIPE, SIG_IGN);
initialize(config, argc - c, &argv[c]);
stickymem = MEM_TEMP; /* this is the default allocation type */
offout = ftell(stdout);
offerr = ftell(stderr);
#ifdef MALLOC_TRACE
mal_leaktrace(1);
#endif /* MALLOC_TRACE */
if (daemonflg) {
if (chdir(postoffice) < 0 || chdir(ROUTERDIR) < 0)
fprintf(stderr, "%s: cannot chdir.\n", progname);
/* XX: check if another daemon is running already */
if (offout < ftell(stdout) || offerr < ftell(stderr)) {
fprintf(stderr, "%d %d %d %d\n",
(int) offout, (int) ftell(stdout),
(int) offerr, (int) ftell(stderr));
fprintf(stderr, "%s: daemon not started.\n", progname);
die(1, "errors during startup");
}
if (tac == 0) /* leave worldy matters behind */
detach();
printf("%s: router daemon (%s)\n\tstarted at %s\n",
progname, Version, rfc822date(&now));
if (killflg)
if (killprevious(-SIGTERM, pidfile) != 0) {
/* Indicates failure at pidfile creation! */
fprintf(stderr,"router can't create pidfile ?? Disk full ??\n");
exit(2);
}
if (nrouters > 0) {
int pgrp, ppid = getpid();
#ifndef GETPGRP_VOID /* We assume getpgrp() and setpgrp() calling
conventions match in the machine.. */
pgrp = getpgrp(ppid); /* BSD -style */
#else
pgrp = getpgrp(); /* SysV/POSIX -style */
#endif
if (pgrp != ppid) {
fprintf(stderr, "process group %d != pid %d\n",
pgrp, ppid);
fprintf(stderr, "%s: daemon not started.\n", progname);
die(1, "capability error during startup");
}
}
router_id = getpid();
}
/* Each (sub-)process does openlog() all by themselves */
zopenlog("router", LOG_PID, LOG_MAIL);
if (c < argc) {
savefile = 1;
/*
* we need to use a local variable (c) because zoptind is global
* and can (and will) be modified by the funcall()'s we do.
*/
do {
av[0] = "process";
av[1] = argv[c];
av[2] = NULL;
#ifdef XMEM
write(30, "\n", 1);
#endif /* XMEM */
s_apply(2, &av[0]); /* "process" filename */
} while (++c < argc);
} else if (daemonflg) {
av[0] = "daemon";
av[1] = NULL;
run_daemon(1, &av[0]);
/* NOTREACHED */
} else if (interactiveflg) {
#ifdef MALLOC_TRACE
zshtoplevel(NULL);
#else /* !MALLOC_TRACE */
trapexit(zshtoplevel(NULL));
#endif /* MALLOC_TRACE */
/* NOTREACHED */
}
#ifdef MALLOC_TRACE
dbfree();
zshfree();
#endif /* MALLOC_TRACE */
/* if (mustexit)
die(0, "signal"); */
#ifdef MALLOC_TRACE
die(0, "malloc trace");
#endif /* MALLOC_TRACE */
trapexit(0);
/* NOTREACHED */
return 0;
}
/* Run around and gather the necessary information for starting operation */
static void
initialize(configfile, argc, argv)
const char *configfile;
int argc;
const char *argv[];
{
struct Zgroup *grp;
struct sptree_init *sptip;
int ac;
const char **cpp;
const char **av;
const char *zconfig;
av = (const char **)emalloc((5+argc)*(sizeof (char *)));
/* initialize shell */
ac = 0;
av[ac++] = progname;
av[ac++] = "-s";
av[ac++] = zshopts;
while (argc-- > 0)
av[ac++] = *argv++;
av[ac] = NULL;
staticprot(&s_value);
zshinit(ac, av);
/* add builtin router functions to list of builtin shell functions */
{
register struct shCmd *shcmdp;
for (shcmdp = &fnctns[0]; shcmdp->name != NULL; ++shcmdp)
sp_install(symbol(shcmdp->name),
(void*)shcmdp, 0, spt_builtins);
}
/* initialize splay trees in router */
av[0] = "relation";
av[1] = "-t";
av[4] = NULL;
for (sptip = &splaytrees[0]; sptip->spta != NULL; ++sptip) {
if (sptip->incore_name != NULL) {
if (sptip->spta == &spt_headers)
av[2] = "header";
else
av[2] = "incore";
av[3] = sptip->incore_name;
if (run_relation(4, av) == 0)
*(sptip->spta) = icdbspltree(sptip->incore_name);
} else
*(sptip->spta) = sp_init();
}
init_header();
/* trusted users */
for (cpp = default_trusted; cpp != NULL && *cpp != NULL; ++cpp)
add_incoresp(*cpp, "", spt_goodguys);
if (files_group != NULL) {
if ((grp = zgetgrnam(files_group)) == NULL)
files_gid = -1;
else
files_gid = grp->gr_gid;
}
zconfig = getzenv("ZCONFIG");
if (zconfig)
v_set("ZCONFIG", zconfig);
/* source the router config file */
ac = 0;
av[ac++] = ".";
if (strchr(configfile, '/') == NULL) {
av[ac] = emalloc(strlen(configfile)+sizeof "./"+1);
sprintf((char*)av[ac++], "./%s", configfile);
} else
av[ac++] = configfile;
av[ac] = NULL;
setfreefd();
sh_include(ac, av);
if (av[1] != configfile)
free((void*)av[1]);
free((char *)av);
}
int
login_to_uid(name)
const char *name;
{
struct Zpasswd *pw;
uid_t uid;
char buf[BUFSIZ];
char *cp;
struct spblk *spl;
spl = lookup_incoresp(name, spt_loginmap);
if (spl == NULL) {
memtypes oval = stickymem;
stickymem = MEM_MALLOC;
pw = zgetpwnam(name);
if (!pw)
pw = zgetpwnam(name);
if (pw == NULL) {
uid = nobody;
} else {
uid = pw->pw_uid;
cp = strsave(pw->pw_name);
sp_install(uid, cp, 0L, spt_uidmap);
fullname(pw->pw_gecos,buf,sizeof buf,pw->pw_name);
add_incoresp(cp, buf, spt_fullnamemap);
}
addd_incoresp(name, (void*)((long)uid), spt_loginmap);
stickymem = oval;
} else
uid = (long)(spl->data);
return uid;
}
const char *
uidpwnam(uid)
int uid;
{
struct Zpasswd *pw;
register const char *cp;
struct spblk *spl;
char buf[BUFSIZ];
spl = sp_lookup((u_long)uid, spt_uidmap);
if (spl == NULL) {
pw = zgetpwuid((uid_t)uid);
if (pw == NULL) {
/* memory shall be temporary in
its nature for this data! */
sprintf(buf, "uid#%d", uid);
cp = strsave(buf);
deferuid = 1;
} else {
memtypes oval = stickymem;
stickymem = MEM_MALLOC;
cp = strsave(pw->pw_name);
addd_incoresp(pw->pw_name, (void*)((long)(pw->pw_uid)), spt_loginmap);
fullname(pw->pw_gecos,buf, sizeof buf, pw->pw_name);
add_incoresp(pw->pw_name, buf, spt_fullnamemap);
sp_install((u_int)uid, cp, 0L, spt_uidmap);
stickymem = oval;
}
} else
cp = spl->data;
return cp;
}
/* Can we trust this person? */
int
isgoodguy(uid)
int uid;
{
const char *name;
spkey_t spk;
struct spblk *spl;
/*
* If you're wondering about this comparison... I had to store
* *something* in the splay tree; I'm really using it as a boolean.
*/
name = uidpwnam(uid);
spk = symbol_lookup_db(name, spt_goodguys->symbols);
spl = NULL;
if (spk != (spkey_t)0)
spl = sp_lookup(spk, spt_goodguys);
return spl != NULL;
}
#define MAXSAFESIZE 700 /* syslog dumps core if we have much over this */
void
logmessage(e)
struct envelope *e;
{
int n, len;
const char *from;
char *to, *cp;
char buf[MAXSAFESIZE];
struct header *h;
struct addr *p;
struct address *ap;
from = NULL;
for (h = e->e_eHeaders; h != NULL; h = h->h_next) {
if (h->h_descriptor->class == eFrom
&& h->h_contents.a != NULL) {
p = h->h_contents.a->a_tokens;
if (p != NULL) {
from = saveAddress(p);
break;
}
}
}
if (from == NULL)
from = "?from?";
n = 0;
to = buf;
for (h = e->e_eHeaders; h != NULL; h = h->h_next) {
if (h->h_descriptor->class == eTo && h->h_contents.a != NULL) {
for (ap = h->h_contents.a; ap != NULL; ap=ap->a_next) {
cp = saveAddress(ap->a_tokens);
len = strlen(cp);
if (to + len + 2 >= buf + sizeof buf) {
/* print what we've got so far */
if (to != buf) {
*to = '\0';
logit(e->e_file,
e->e_messageid,
from, buf);
to = buf;
}
if (to + len + 2 >= buf + sizeof buf)
/* Can't fit it in.. */
continue;
} else {
if (n) *to++ = ',';
*to++ = ' ';
}
memcpy(to, cp, len);
to += len;
*to = 0;
++n;
}
}
}
if (to != buf) {
*to = '\0';
logit(e->e_file, e->e_messageid, from, buf);
}
}
/*
* All the strangeness in the logit() routine is because syslog() is
* broken; it can only handle a certain size buffer before it'll dump core.
* We do the same processing for stdout so that stdout can be fed into
* logger without having to fix or customize a vendor program. Sigh.
*/
static void
logit(file, id, from, to)
const char *file, *id, *from, *to;
{
int flen, baselen;
if (id == NULL)
id = file;
baselen = strlen(file) + strlen(id) + 4;
flen = strlen(from);
if (flen > 0)
printf("%.200s: file: %s %s => %s\n", id, file, from, to);
else
printf("%.200s: file: %s %s\n", id, file, to);
if (!nosyslog) {
if (flen > 0)
zsyslog((LOG_INFO, "%.200s: file: %.150s %.200s => %.200s",
id, file, from, to));
else
zsyslog((LOG_INFO, "%.200s: file: %.150s %.200s", id, file, to));
}
}
const char *
mail_host()
{
return myhostname;
}
extern void printfds __((void));
void
printfds()
{
int i, topfd = getdtablesize();
struct stat fst;
for (i=0; i < topfd; ++i) {
long flags = fcntl(i,F_GETFL,0);
if (flags >= 0) {
fstat(i,&fst);
if (S_ISREG(fst.st_mode))
fprintf(stderr," %d",i);
else if (S_ISDIR(fst.st_mode))
fprintf(stderr," %dd",i);
else if (S_ISCHR(fst.st_mode))
fprintf(stderr," %dC",i);
else if (S_ISBLK(fst.st_mode))
fprintf(stderr," %dB",i);
else
fprintf(stderr," %d(?)",i);
}
}
fprintf(stderr,"\n");
fflush(stderr);
}
syntax highlighted by Code2HTML, v. 0.9.1