/*
* 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-2001
*/
#include "mailer.h"
#include <sfio.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include "scheduler.h"
#include "prototypes.h"
#include "mail.h"
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#include "libz.h"
#include "libc.h"
#define SKIPSPACE(Y) while (*Y == ' ' || *Y == '\t' || *Y == '\n') ++Y
#define SKIPTEXT(Y) while (*Y && *Y != ' ' && *Y != '\t' && *Y != '\n') ++Y
#define SKIPDIGIT(Y) while ('0' <= *Y && *Y <= '9') ++Y
static void celink __((struct config_entry *, struct config_entry **, struct config_entry **, int copy));
static int readtoken __((Sfio_t *fp, char *buf, int buflen, int *linenump));
static int paramparse __((char *line));
#define RCKEYARGS __((char *key, char *arg, struct config_entry *ce))
static int rc_command RCKEYARGS;
static int rc_expform RCKEYARGS;
static int rc_expiry RCKEYARGS;
static int rc_expiry2 RCKEYARGS;
static int rc_group RCKEYARGS;
static int rc_interval RCKEYARGS;
static int rc_maxchannel RCKEYARGS;
static int rc_maxring RCKEYARGS;
static int rc_maxta RCKEYARGS;
static int rc_maxthr RCKEYARGS;
static int rc_idlemax RCKEYARGS;
static int rc_retries RCKEYARGS;
static int rc_reporttimes RCKEYARGS;
static int rc_user RCKEYARGS;
static int rc_skew RCKEYARGS;
static int rc_bychannel RCKEYARGS;
static int rc_ageorder RCKEYARGS;
static int rc_queueonly RCKEYARGS;
static int rc_wakeuprestartonly RCKEYARGS;
static int rc_deliveryform RCKEYARGS;
static int rc_overfeed RCKEYARGS;
static int rc_priority RCKEYARGS;
static int rc_nice RCKEYARGS;
static int rc_syspriority RCKEYARGS;
static int rc_sysnice RCKEYARGS;
extern int errno;
struct config_entry *default_entry = NULL;
struct config_entry *rrcf_head = NULL;
/* where the MAILQv2 authentication dataset file is ? */
const char * mq2authfile = NULL;
static struct rckeyword {
const char *name;
int (*parsef)();
} rckeys[] = {
{ "ageorder", rc_ageorder }, /* boolean */
{ "bychannel", rc_bychannel }, /* boolean */
{ "command", rc_command }, /* array of strings */
{ "deliveryform", rc_deliveryform }, /* string */
{ "expiry", rc_expiry }, /* time */
{ "expiry2", rc_expiry2 }, /* time */
{ "expiryform", rc_expform }, /* string */
{ "group", rc_group }, /* number */
{ "idlemax", rc_idlemax }, /* time */
{ "interval", rc_interval }, /* time */
{ "maxchannel", rc_maxchannel }, /* number */
{ "maxchannels", rc_maxchannel }, /* number */
{ "maxring", rc_maxring }, /* number */
{ "maxrings", rc_maxring }, /* number */
{ "maxta", rc_maxta }, /* number */
{ "maxthr", rc_maxthr }, /* number */
{ "maxtransport", rc_maxta }, /* number */
{ "maxtransports", rc_maxta }, /* number */
{ "nice", rc_nice }, /* number */
{ "overfeed", rc_overfeed }, /* number */
{ "priority", rc_priority }, /* number */
{ "queueonly", rc_queueonly }, /* boolean */
{ "reporttimes", rc_reporttimes }, /* array of numbers */
{ "retries", rc_retries }, /* array of numbers */
{ "skew", rc_skew }, /* number */
{ "sysnice", rc_sysnice }, /* number */
{ "syspriority", rc_syspriority }, /* number */
{ "user", rc_user }, /* number */
{ "wakeuprestartonly", rc_wakeuprestartonly }, /* boolean */
{ NULL, 0 }
};
#define ISS(s) ((s)?(s):"<NULL>")
void
defaultconfigentry(ce,defaults)
struct config_entry *ce, *defaults;
{
if (defaults && ce != defaults) {
/* Use the configurations script defaults.. */
ce->next = NULL;
#if 0
ce->mark = 0;
#endif
ce->interval = defaults->interval;
ce->idlemax = defaults->idlemax;
ce->expiry = defaults->expiry;
ce->expiry2 = defaults->expiry2;
ce->expiryform = defaults->expiryform;
ce->uid = defaults->uid;
ce->gid = defaults->gid;
ce->command = defaults->command;
ce->flags = defaults->flags;
ce->maxkids = defaults->maxkids;
ce->maxkidChannel = defaults->maxkidChannel;
ce->maxkidThread = defaults->maxkidThread;
ce->maxkidThreads = defaults->maxkidThreads;
ce->argv = defaults->argv;
ce->nretries = defaults->nretries;
ce->retries = defaults->retries;
ce->reporttimes[0] = defaults->reporttimes[0];
ce->reporttimes[1] = defaults->reporttimes[1];
ce->reporttimes[2] = defaults->reporttimes[2];
ce->reporttimes[3] = defaults->reporttimes[3];
ce->skew = defaults->skew;
ce->deliveryform = defaults->deliveryform;
ce->overfeed = defaults->overfeed;
ce->priority = defaults->priority;
} else if (defaults == NULL) {
/* Compile these defaults in.. Only for the "*" / "* / *" entry.. */
ce->next = NULL;
#if 0
ce->mark = 0;
#endif
ce->interval = -1;
ce->idlemax = -1;
ce->expiry = -1;
ce->expiry2 = -1;
ce->expiryform = NULL;
ce->uid = -1;
ce->gid = -1;
ce->command = NULL;
ce->flags = 0;
ce->maxkids = -1;
ce->maxkidChannel = -1;
ce->maxkidThread = 1;
ce->maxkidThreads = -1;
ce->argv = NULL;
ce->nretries = 0;
ce->retries = NULL;
ce->reporttimes[0] = 0;
ce->reporttimes[1] = 0;
ce->reporttimes[2] = 0;
ce->reporttimes[3] = 0;
ce->skew = 5;
ce->deliveryform = NULL;
ce->overfeed = 0;
ce->priority = 0; /* nice(0) -- no change */
}
}
void
vtxprint(vp)
struct vertex *vp;
{
int i;
struct config_entry *ce = &(vp->thgrp->ce);
if (vp->orig[L_CHANNEL] != NULL && vp->orig[L_HOST] != NULL)
sfprintf(sfstdout, "%s/%s", vp->orig[L_CHANNEL]->name,
vp->orig[L_HOST]->name);
else
sfprintf(sfstdout, "%s/%s", ISS(ce->channel), ISS(ce->host));
sfprintf(sfstdout," %p mark %d\n", ce, ce->mark);
sfprintf(sfstdout,"\tinterval %d\n", (int)ce->interval);
sfprintf(sfstdout,"\tidlemax %d\n", ce->idlemax);
sfprintf(sfstdout,"\texpiry %d\n", (int)ce->expiry);
sfprintf(sfstdout,"\texpiry2 %d\n", (int)ce->expiry2);
sfprintf(sfstdout,"\texpiryform %s\n", ISS(ce->expiryform));
sfprintf(sfstdout,"\tdeliveryform %s\n", ISS(ce->deliveryform));
sfprintf(sfstdout,"\tuid %d\n", ce->uid);
sfprintf(sfstdout,"\tgid %d\n", ce->gid);
sfprintf(sfstdout,"\tcommand %s\n", ISS(ce->command));
sfprintf(sfstdout,"\tflags:");
if (ce->flags == 0)
sfprintf(sfstdout," (none)");
else {
if (ce->flags & CFG_WITHHOST) sfprintf(sfstdout," WITHHOST");
if (ce->flags & CFG_AGEORDER) sfprintf(sfstdout," AGEORDER");
if (ce->flags & CFG_QUEUEONLY) sfprintf(sfstdout," QUEUEONLY");
if (ce->flags & CFG_WAKEUPRESTARTONLY)sfprintf(sfstdout,
" WAKEUPRESTARTONLY");
}
sfprintf(sfstdout,"\n");
sfprintf(sfstdout,"\tmaxkids %d\n", ce->maxkids);
sfprintf(sfstdout,"\tmaxkidChannel %d\n", ce->maxkidChannel);
sfprintf(sfstdout,"\tmaxkidThread %d\n", ce->maxkidThread);
sfprintf(sfstdout,"\tmaxkidThreads %d\n", ce->maxkidThreads);
sfprintf(sfstdout,"\toverfeed %d\n", ce->overfeed);
if (ce->priority >= 80)
sfprintf(sfstdout,"\tpriority %d\n", ce->priority - 100);
else
sfprintf(sfstdout,"\tnice %d\n", ce->priority);
if (ce->argv != NULL) {
for (i = 0; ce->argv[i] != NULL; ++i)
sfprintf(sfstdout,"\targv[%d] = %s\n", i, ce->argv[i]);
}
sfprintf(sfstdout,"\tnretries %d\n", ce->nretries);
if (ce->nretries > 0) {
sfprintf(sfstdout,"\tretries = (");
for (i = 0; i < ce->nretries ; ) {
sfprintf(sfstdout,"%d", ce->retries[i]);
++i;
if (i < ce->nretries)
sfprintf(sfstdout," ");
}
sfprintf(sfstdout,")\n");
}
sfprintf(sfstdout,"\treporttimes = (");
for (i = 0; i < 4 ; ++i ) {
sfprintf(sfstdout,"%d", ce->reporttimes[i]);
if (i < 3)
sfprintf(sfstdout," ");
}
sfprintf(sfstdout,")\n");
sfprintf(sfstdout,"\tskew %d\n", ce->skew);
}
static void
celink(ce, headp, tailp, copy)
struct config_entry *ce;
struct config_entry **headp, **tailp;
int copy;
{
if (ce == default_entry && *headp != NULL && *tailp != NULL)
return; /* XX: ?? */
if ((*headp) == NULL)
(*headp) = (*tailp) = ce;
else {
(*tailp)->next = ce;
(*tailp) = ce;
if (!copy) return;
for (ce = (*headp); ce != (*tailp); ce = ce->next) {
if (verbose)
sfprintf(sfstdout,"celink() ce = %p mark=%d\n", ce, ce->mark);
if (ce->mark == 0) continue;
ce->mark = 0;
ce->interval = (*tailp)->interval;
ce->idlemax = (*tailp)->idlemax;
ce->expiry = (*tailp)->expiry;
ce->expiry2 = (*tailp)->expiry2;
ce->expiryform = (*tailp)->expiryform;
ce->uid = (*tailp)->uid;
ce->gid = (*tailp)->gid;
ce->command = (*tailp)->command;
ce->flags = (*tailp)->flags;
ce->maxkids = (*tailp)->maxkids;
ce->maxkidChannel = (*tailp)->maxkidChannel;
ce->maxkidThread = (*tailp)->maxkidThread;
ce->maxkidThreads = (*tailp)->maxkidThreads;
ce->argv = (*tailp)->argv;
ce->nretries = (*tailp)->nretries;
ce->retries = (*tailp)->retries;
ce->overfeed = (*tailp)->overfeed;
ce->priority = (*tailp)->priority;
}
}
}
struct config_entry *
readconfig(file)
const char *file;
{
char *cp, *s, *a, line[BUFSIZ];
int errflag, n;
struct config_entry *ce, *head, *tail;
struct rckeyword *rckp;
struct vertex v;
Sfio_t *fp;
int linenum = 0;
int attrs = 0;
ce = head = tail = NULL;
errflag = 0;
if ((fp = sfopen(NULL, file, "r")) == NULL) {
sfprintf(sfstderr, "%s: %s: %s\n",
progname, file, strerror(errno));
return NULL;
}
while ((n = readtoken(fp, line, sizeof line, &linenum)) != -1) {
if (verbose)
sfprintf(sfstdout, "read '%s' %d\n", line, n);
if (n == 1) {
/* Selector entry - or "PARAM" */
if (cistrncmp(line,"PARAM",5) == 0) {
if (paramparse(line+5)) {
sfprintf(sfstderr, "%s: illegal syntax at %s:%d\n",
progname, file, linenum);
++errflag;
}
continue;;
}
if (ce != NULL)
celink(ce, &head, &tail, attrs);
attrs = 0;
ce = (struct config_entry *)emalloc(sizeof (struct config_entry));
memset((void*)ce, 0, sizeof(ce));
if (verbose) sfprintf(sfstdout,"CE= %p mark=1\n", ce);
defaultconfigentry(ce,NULL);
ce->mark = 1;
if ((s = strchr(line, '/')) != NULL) {
*s = 0;
ce->channel = strsave(line);
*s = '/';
ce->host = strsave(s+1);
} else {
ce->channel = strsave(line);
ce->host = strsave("*");
}
if (strcmp(line,"*/*") == 0 || strcmp(line,"*") == 0) {
/* The default entry.. */
if (default_entry != NULL) {
if (ce->channel) free (ce->channel);
if (ce->host) free(ce->host);
free(ce);
ce = default_entry;
}
defaultconfigentry(ce,default_entry);
if (default_entry == NULL)
default_entry = ce;
} else
defaultconfigentry(ce,default_entry);
} else if (ce != NULL) {
a = NULL;
if ((cp = strchr(line, '=')) != NULL) {
char *p = cp-1;
*cp = '\0';
while (p >= line && (*p == ' ' || *p == '\t'))
*p-- = '\0';
a = cp+1;
SKIPSPACE(a);
if (*a == '"') {
++a;
cp = a;
while (*cp && *cp != '"') {
if (*cp == '\\' && cp[1] != 0)
++cp;
++cp;
}
if (*cp)
*cp = '\0';
}
}
if (ce && ce->mark) {
if (verbose)
sfprintf(sfstdout," reading entry, ce = %p, mark=0\n", ce);
ce->mark = 0;
}
for (rckp = &rckeys[0]; rckp->name != NULL ; ++rckp)
if (cistrcmp(rckp->name, line) == 0) {
errflag += (*rckp->parsef)(line, a, ce);
break;
}
if (rckp->name == NULL) {
sfprintf(sfstderr,
"%s: unknown keyword %s in %s:%d\n",
progname, line, file, linenum);
++errflag;
}
if (!errflag) attrs = 1;
} else {
sfprintf(sfstderr, "%s: illegal syntax at %s:%d\n",
progname, file, linenum);
++errflag;
}
}
if (ce != NULL)
celink(ce, &head, &tail, 1);
sfclose(fp);
if (verbose) {
struct threadgroup tg;
v.orig[L_CHANNEL] = v.orig[L_HOST] = NULL;
v.thgrp = &tg;
for (ce = head; ce != NULL; ce = ce->next) {
tg.ce = *ce;
vtxprint(&v);
}
}
return errflag ? NULL : head;
}
static int
readtoken(fp, buf, buflen, linenump)
Sfio_t *fp;
char *buf;
int buflen, *linenump;
{
static char line[BUFSIZ];
static char *lp = NULL;
char *elp;
int rv;
redo_readtoken:
if (lp == NULL) {
if (csfgets(line, sizeof line, fp) < 0)
return -1;
*linenump += 1;
lp = line;
}
/* Skip initial white-space */
SKIPSPACE(lp);
/* Now it is one of: a token, a comment start, or end of line */
if (*lp == '\0' || *lp == '#') {
/* Comment/EOL */
lp = NULL;
goto redo_readtoken;
}
/* Now we scan for the token + possible value */
elp = lp;
while (*elp && *elp != ' ' && *elp != '\t' && *elp != '\n' && *elp != '=' && *elp != '#')
++elp;
if (isspace(0xFF & *elp)) {
/* Allow spaces after the token and before '=' */
char *p = elp;
SKIPSPACE(p);
if (*p == '=')
elp = p;
}
/* Value indicator ? */
if (*elp == '=') {
/* Allow spaces between '=', and value */
++elp;
SKIPSPACE(elp);
if (*elp == '"') {
++elp;
while (*elp != '"' && *elp != '\0') {
if (*elp == '\\' && *(elp+1) == '\n') {
if (csfgets(elp, sizeof line - (elp - line), fp) < 0) {
sfprintf(sfstderr,
"%s: bad continuation line\n",
progname);
return -1;
}
}
++elp;
}
if (*elp == '\0') {
sfprintf(sfstderr,
"%s: missing end-quote in: %s\n",
progname, line);
return -1;
}
++elp;
} else {
SKIPTEXT(elp);
}
}
strncpy(buf, lp, elp-lp);
buf[elp-lp] = '\0';
rv = (lp == line);
if (*elp == '\0' || *elp == '\n')
lp = NULL;
else
lp = elp;
return rv;
}
struct config_entry *
rereadconfig(head, file)
struct config_entry *head;
const char *file;
{
struct config_entry *ce, *nce, *head2;
sfprintf(sfstderr,
"%s: reread configuration file: %s\n", progname, file);
/* free all the old config file entries */
for (ce = head; ce != NULL; ce = nce) {
nce = ce->next;
/* Process the default_entry cleanup as the LAST one */
if (ce == default_entry)
continue;
/* Free up all malloc()ed blocks */
if (ce->command != NULL &&
(default_entry == NULL ||
ce->command != default_entry->command))
free(ce->command);
if (ce->argv != NULL &&
(default_entry == NULL ||
ce->argv != default_entry->argv))
free((char *)ce->argv);
if (ce->retries != NULL && ce->nretries > 0 &&
(default_entry == NULL ||
ce->retries != default_entry->retries))
free((char *)ce->retries);
free((char *)ce);
/* Process the default_entry cleanup as the LAST one */
if (nce == NULL) {
nce = default_entry;
if (nce != NULL)
nce->next = NULL; /* It no longer has any followers.. */
default_entry = NULL;
}
}
/* read the new stuff in */
if ((head2 = readconfig(file)) == NULL) {
char *cp = emalloc(strlen(file)+50);
sprintf(cp, "null control file: %s", file);
die(1, cp);
/* NOTREACHED */
}
/* apply it to all the existing vertices */
rrcf_head = head2;
sp_scan(vtxredo, (struct spblk *)NULL, spt_mesh[L_CTLFILE]);
endpwent(); /* Close the databases */
endgrent();
return head;
}
static int rc_command(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
char *cp, **av, *argv[100];
int j;
ce->command = strsave(arg);
j = 0;
for (cp = ce->command; *cp;) {
argv[j++] = cp;
if (j >= (sizeof argv)/(sizeof argv[0]))
break;
SKIPTEXT(cp);
if (*cp == '\0') break;
*cp++ = '\0';
SKIPSPACE(cp);
}
argv[j++] = NULL;
if (j > 0) {
ce->argv = (char **)emalloc(sizeof (char *) * j);
memcpy((char *)ce->argv, (char *)&argv[0], sizeof (char *) * j);
}
if (!(ce->flags & CFG_WITHHOST)) {
for (av = &ce->argv[0]; *av != NULL; ++av)
if (strcmp(*av, replhost) == 0) {
ce->flags |= CFG_WITHHOST;
break;
}
}
/* ``replchannel'' need not matched, idle processing
takes it properly into account. */
return 0;
}
static int rc_expform(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->expiryform = strsave(arg);
return 0;
}
static int rc_expiry(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->expiry = parse_interval(arg,NULL);
return 0;
}
static int rc_expiry2(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->expiry2 = parse_interval(arg,NULL);
return 0;
}
static int rc_group(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
struct Zgroup *gr;
if (isascii(*arg) && isdigit(*arg))
ce->gid = atoi(arg);
else if ((gr = zgetgrnam(arg)) == NULL) {
sfprintf(sfstderr, "%s: unknown group: '%s'\n", progname, arg);
return 1;
} else
ce->gid = gr->gr_gid;
return 0;
}
static int rc_interval(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->interval = parse_interval(arg,NULL);
return 0;
}
static int rc_idlemax(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->idlemax = parse_interval(arg,NULL);
return 0;
}
static int rc_overfeed(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->overfeed = atoi(arg);
if (ce->overfeed < 0)
ce->overfeed = 0;
return 0;
}
static int rc_priority(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
if (sscanf(arg,"%d",&ce->priority) != 1 ||
ce->priority < -20 || ce->priority > 19) {
sfprintf(sfstderr, "%s: Bad UNIX priority value, acceptable in range: -20..19; input=\"%s\"\n", progname, arg);
return 1;
}
ce->priority += 100;
return 0;
}
static int rc_nice(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
if (sscanf(arg,"%d",&ce->priority) != 1 ||
ce->priority < -40 || ce->priority > 39) {
sfprintf(sfstderr, "%s: Bad UNIX nice offset value, acceptable in range: -40..39; input=\"%s\"\n", progname, arg);
return 1;
}
return 0;
}
static int rc_syspriority(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
int i;
if (sscanf(arg,"%d",&i) != 1 ||
i < -20 || i > 19) {
sfprintf(sfstderr, "%s: Bad UNIX priority value, acceptable in range: -20..19; input=\"%s\"\n", progname, arg);
return 1;
}
#if defined(HAVE_SETPRIORITY) && defined(HAVE_SYS_RESOURCE_H)
/* PRIO_PROCESS depends likely of HAVE_SYS_RESOURCE_H */
setpriority(PRIO_PROCESS, 0, i);
#endif
return 0;
}
static int rc_sysnice(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
int i;
if (sscanf(arg,"%d",&i) != 1 ||
i < -40 || i > 39) {
sfprintf(sfstderr, "%s: Bad UNIX nice offset value, acceptable in range: -40..39; input=\"%s\"\n", progname, arg);
return 1;
}
nice(i);
return 0;
}
static int rc_maxchannel(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->maxkidChannel = atoi(arg);
if (ce->maxkidChannel <= 0)
ce->maxkidChannel = 10000;
return 0;
}
static int rc_maxring(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->maxkidThreads = atoi(arg);
if (ce->maxkidThreads <= 0)
ce->maxkidThreads = 10000;
return 0;
}
static int rc_maxta(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->maxkids = atoi(arg);
if (ce->maxkids <= 0)
ce->maxkids = 10000;
if (ce->maxkids > global_maxkids)
ce->maxkids = global_maxkids;
return 0;
}
static int rc_maxthr(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->maxkidThread = atoi(arg);
if (ce->maxkidThread <= 0)
ce->maxkidThread = 1;
return 0;
}
static int rc_retries(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
int i, j, arr[100];
char c, *cp, *d;
j = 0;
for (cp = arg; *cp != '\0'; ++cp) {
SKIPSPACE(cp);
if (*cp == '\0')
break;
d = cp++;
SKIPTEXT(cp);
c = *cp;
*cp = '\0';
i = atoi(d);
if (i > 0)
arr[j++] = i;
else {
sfprintf(sfstderr,
"%s: not a numeric factor: %s\n",
progname, d);
return 1;
}
if (j >= (sizeof arr)/(sizeof arr[0]))
break;
*cp = c;
if (*cp == '\0')
break;
}
if (j > 0) {
ce->retries = (int *)emalloc((u_int)(sizeof (int) * j));
memcpy((char *)ce->retries, (char *)&arr[0], sizeof (int) * j);
ce->nretries = j;
} else {
sfprintf(sfstderr, "%s: empty retry factor list\n", progname);
return 1;
}
return 0;
}
static int rc_reporttimes(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
int i, j, arr[_CFTAG_RCPTDELAYSIZE];
char c, *cp, *d;
for (j = 0; j < sizeof(arr)/(sizeof(arr[0])); ++j) arr[j] = 0;
j = 0;
for (cp = arg; *cp != '\0'; ++cp) {
SKIPSPACE(cp);
if (*cp == '\0')
break;
d = cp++;
SKIPTEXT(cp);
c = *cp;
*cp = '\0';
i = parse_interval(d, NULL);
if (i > 0)
arr[j++] = i;
else {
sfprintf(sfstderr,
"%s: not a numeric factor: %s\n",
progname, d);
return 1;
}
if (j >= (sizeof arr)/(sizeof arr[0]))
break;
*cp = c;
if (*cp == '\0')
break;
}
for (j = 0; j < sizeof(arr)/(sizeof(arr[0])); ++j)
ce->reporttimes[j] = arr[j];
return 0;
}
static int rc_user(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
struct Zpasswd *pw;
if (isascii(*arg) && isdigit(*arg))
ce->uid = atoi(arg);
else if ((pw = zgetpwnam(arg)) == NULL) {
sfprintf(sfstderr, "%s: unknown user: %s\n", progname, arg);
return 1;
} else
ce->uid = pw->pw_uid;
return 0;
}
static int rc_skew(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
int v;
if (!isascii(*arg) || !isdigit(*arg) || (v = atoi(arg)) < 1) {
sfprintf(sfstderr, "%s: bad skew value: %s\n", progname, arg);
return 1;
}
ce->skew = v;
return 0;
}
static int rc_bychannel(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
return 0;
}
static int rc_ageorder(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->flags |= CFG_AGEORDER;
return 0;
}
static int rc_deliveryform(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->deliveryform = strsave(arg);
return 0;
}
static int rc_queueonly(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->flags |= CFG_QUEUEONLY;
return 0;
}
static int rc_wakeuprestartonly(key, arg, ce)
char *key, *arg;
struct config_entry *ce;
{
ce->flags |= CFG_WAKEUPRESTARTONLY;
return 0;
}
extern int mailqmode;
char *zenvexpand(line)
char *line;
{
char *s = line;
char *v;
const char *e;
char end_c;
int spotlen;
int e_len;
if (!line) return NULL;
for (;*s;++s) {
if (*s == '$') {
if (s[1] == '{') {
v = s;
for (;*s && *s != '}'; ++s) ;
end_c = *s;
*s = 0;
spotlen = s - v + 1;
e = getzenv(v+2);
*s = end_c;
if (!e) continue; /* No such zenv-variable :-/ */
e_len = strlen(e);
if (e_len > spotlen) {
/* Must expand a bit */
char *n = malloc(strlen(line)+e_len-spotlen+2);
int p = v - line;
if (!n) continue; /* alloc failure */
if (p > 0)
memcpy(n, line, p);
s = n + p;
memcpy(s, e, e_len);
s += e_len;
p += spotlen;
strcpy(s, line + p); /* Tail */
free(line);
line = n;
} else {
/* New data has same or smaller size */
memcpy(v, e, e_len);
if (e_len < spotlen) /* Smaller size */
strcpy(v + e_len, v + spotlen);
v += e_len;
s = v;
}
}
}
}
return line;
}
static int paramparse(line)
char *line;
{
char *s, *a = NULL;
if ((s = strchr(line, '=')) != NULL) {
char *p = s-1;
*s = '\0';
while (p >= line && (*p == ' ' || *p == '\t'))
*p-- = '\0';
a = s+1;
SKIPSPACE(a);
if (*a == '"') {
++a;
s = a;
while (*s && *s != '"') {
if (*s == '\\' && s[1] != 0)
++s;
++s;
}
if (*s)
*s = '\0';
}
}
if (cistrcmp(line,"authfile")==0 && a) {
if (mq2authfile)
free((void*)mq2authfile);
mq2authfile = zenvexpand(strsave(a));
if (mq2authfile && access(mq2authfile,R_OK)==0)
mailqmode = 2;
return 0;
}
if (cistrcmp(line,"mailqsock")==0 && a) {
if (mailqsock)
free((void*)mailqsock);
mailqsock = zenvexpand(strsave(a));
return 0;
}
if (cistrcmp(line,"msgwriteasync")==0 && a) {
msgwriteasync = atoi(s);
return 0;
}
if (cistrcmp(line,"notifysock")==0 && a) {
if (notifysock)
free((void*)notifysock);
notifysock = zenvexpand(strsave(a));
return 0;
}
if (cistrcmp(line,"global-report-interval")==0 && a) {
global_report_interval = parse_interval(a, NULL);
return 0;
}
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1