#include "am.h"
#include <sys/stat.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
FILE *logfp;
int syslogging;
int xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS & ~XLOG_INFO;
int xlog_level_init = ~0;
struct opt_tab xlog_opt[] = {
{ "all", XLOG_ALL },
#ifdef DEBUG
{ "debug", XLOG_DEBUG },
#endif
{ "error", XLOG_ERROR },
{ "fatal", XLOG_FATAL },
{ "info", XLOG_INFO },
{ "map", XLOG_MAP },
{ "stats", XLOG_STATS },
{ "user", XLOG_USER },
{ "warn", XLOG_WARNING },
{ "warning", XLOG_WARNING },
{ 0, 0 }
};
__dead void
xmallocfailure(void)
{
plog(XLOG_FATAL, "Out of memory");
going_down(1);
abort();
}
void *
xmalloc(size_t len)
{
void *p;
int retries = 600;
do {
if ((p = malloc(len)) != NULL)
return p;
if (retries > 0) {
plog(XLOG_ERROR, "Retrying memory allocation");
sleep(1);
}
} while (--retries);
xmallocfailure();
}
void *
xreallocarray(void *ptr, size_t nmemb, size_t size)
{
ptr = reallocarray(ptr, nmemb, size);
if (ptr == NULL)
xmallocfailure();
return (ptr);
}
static void
expand_error(const char *f, char *e, int maxlen)
{
const char *p;
char *q;
int error = errno;
int len = 0;
for (p = f, q = e; (*q = *p) && len < maxlen; len++, q++, p++) {
if (p[0] == '%' && p[1] == 'm') {
char *errstr;
errstr = strerror(error);
if (errstr)
strlcpy(q, errstr, maxlen - (q - e));
else
snprintf(q, maxlen - (q - e),
"Error %d", error);
len += strlen(q) - 1;
q += strlen(q) - 1;
p++;
}
}
e[maxlen-1] = '\0';
}
static void
show_time_host_and_name(int lvl)
{
static time_t last_t = 0;
static char *last_ctime = 0;
time_t t = clocktime();
char *sev;
#if defined(DEBUG) && defined(PARANOID)
extern char **gargv;
#endif
if (t != last_t) {
last_ctime = ctime(&t);
last_t = t;
}
switch (lvl) {
case XLOG_FATAL: sev = "fatal:"; break;
case XLOG_ERROR: sev = "error:"; break;
case XLOG_USER: sev = "user: "; break;
case XLOG_WARNING: sev = "warn: "; break;
case XLOG_INFO: sev = "info: "; break;
case XLOG_DEBUG: sev = "debug:"; break;
case XLOG_MAP: sev = "map: "; break;
case XLOG_STATS: sev = "stats:"; break;
default: sev = "hmm: "; break;
}
fprintf(logfp, "%15.15s %s %s[%ld]/%s ",
last_ctime+4, hostname,
#if defined(DEBUG) && defined(PARANOID)
gargv[0],
#else
__progname,
#endif
(long)mypid,
sev);
}
void
plog(int lvl, const char *fmt, ...)
{
char efmt[1024];
va_list ap;
if (!(xlog_level & lvl))
return;
if (syslogging) {
switch(lvl) {
case XLOG_FATAL: lvl = LOG_CRIT; break;
case XLOG_ERROR: lvl = LOG_ERR; break;
case XLOG_USER: lvl = LOG_WARNING; break;
case XLOG_WARNING: lvl = LOG_WARNING; break;
case XLOG_INFO: lvl = LOG_INFO; break;
case XLOG_DEBUG: lvl = LOG_DEBUG; break;
case XLOG_MAP: lvl = LOG_DEBUG; break;
case XLOG_STATS: lvl = LOG_INFO; break;
default: lvl = LOG_ERR; break;
}
va_start(ap, fmt);
vsyslog(lvl, fmt, ap);
va_end(ap);
return;
}
expand_error(fmt, efmt, sizeof(efmt));
show_time_host_and_name(lvl);
va_start(ap, fmt);
vfprintf(logfp, efmt, ap);
va_end(ap);
fputc('\n', logfp);
fflush(logfp);
}
void
show_opts(int ch, struct opt_tab *opts)
{
int i;
int s = '{';
fprintf(stderr, "\t[-%c {no}", ch);
for (i = 0; opts[i].opt; i++) {
fprintf(stderr, "%c%s", s, opts[i].opt);
s = ',';
}
fputs("}]\n", stderr);
}
int
cmdoption(char *s, struct opt_tab *optb, int *flags)
{
char *p = s;
int errs = 0;
while (p && *p) {
int neg;
char *opt;
struct opt_tab *dp, *dpn = 0;
s = p;
p = strchr(p, ',');
if (p)
*p = '\0';
if (s[0] == 'n' && s[1] == 'o') {
opt = s + 2;
neg = 1;
} else {
opt = s;
neg = 0;
}
for (dp = optb; dp->opt; dp++) {
if (strcmp(opt, dp->opt) == 0)
break;
if (opt != s && !dpn && strcmp(s, dp->opt) == 0)
dpn = dp;
}
if (dp->opt || dpn) {
if (!dp->opt) {
dp = dpn;
neg = !neg;
}
if (neg)
*flags &= ~dp->flag;
else
*flags |= dp->flag;
} else {
plog(XLOG_USER, "option \"%s\" not recognised", s);
errs++;
}
if (p)
*p++ = ',';
}
return errs;
}
int
switch_option(char *opt)
{
int xl = xlog_level;
int rc = cmdoption(opt, xlog_opt, &xl);
if (rc) {
rc = EINVAL;
} else {
if (xlog_level_init == ~0)
xlog_level_init = xl;
else
xl |= xlog_level_init;
xlog_level = xl;
}
return rc;
}
int
switch_to_logfile(char *logfile)
{
FILE *new_logfp = stderr;
if (logfile) {
syslogging = 0;
if (strcmp(logfile, "/dev/stderr") == 0)
new_logfp = stderr;
else if (strcmp(logfile, "syslog") == 0) {
syslogging = 1;
new_logfp = stderr;
#if defined(LOG_CONS) && defined(LOG_NOWAIT)
openlog(__progname, LOG_PID|LOG_CONS|LOG_NOWAIT,
LOG_DAEMON);
#else
openlog(__progname, LOG_PID);
#endif
} else {
(void) umask(orig_umask);
new_logfp = fopen(logfile, "a");
umask(0);
}
}
if (!new_logfp && logfile) {
plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
return 1;
}
if (logfp && logfp != stderr)
(void) fclose(logfp);
logfp = new_logfp;
return 0;
}
time_t clock_valid = 0;
time_t xclock_valid = 0;
#ifndef clocktime
time_t
clocktime(void)
{
time_t now = time(&clock_valid);
if (xclock_valid > now) {
plog(XLOG_WARNING, "system clock reset");
reschedule_timeouts(now, xclock_valid);
}
return xclock_valid = now;
}
#endif