#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <syslog.h>
#include <assert.h>
#include <sys/types.h>
#include "common.h"
#include "logging.h"
const char *prog_name = "none";
int log_level;
static int curlogfd = -1;
static const char *curfname;
static const char *stderr_name = "stderr";
#define SMALLSTR 254
static int
dowrite(int fd, const void *buf, int len)
{
int retv;
const uint8_t *bp = (uint8_t *)buf;
while (len > 0) {
retv = write(fd, bp, len);
if (retv == 0) {
break;
}
if (retv == -1) {
if (errno != EINTR)
break;
} else {
bp += retv;
len -= retv;
}
}
if (len <= 0)
return (bp - (uint8_t *)buf);
return (retv);
}
static int
doclose(void)
{
int retval = 0;
if (curlogfd == -1)
return (0);
if ((curlogfd != STDERR_FILENO) || (curfname != stderr_name))
retval = close(curlogfd);
curlogfd = -1;
return (retval);
}
static void
vlogat(int loglev, const char *fmt, va_list args)
{
char timbuf[64];
char regbuf[SMALLSTR+2];
char *ostr;
int timlen;
int slen;
char *nstr;
int err1, err2;
int sloglev;
int retv;
va_list args2;
static int xlate_loglev[] = {
LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG
};
if (loglev >= log_level)
return;
timbuf[0] = '\0';
timlen = 0;
if (curlogfd >= 0) {
time_t now = time(NULL);
timlen = strftime(timbuf, sizeof (timbuf), "%Y/%m/%d %T"
"%Z: ", localtime(&now));
}
va_copy(args2, args);
slen = vsnprintf(regbuf, SMALLSTR, fmt, args);
if (slen < SMALLSTR) {
ostr = regbuf;
} else {
ostr = alloca(slen + 2);
slen = vsnprintf(ostr, slen + 1, fmt, args2);
}
if (slen <= 0)
return;
if (ostr[slen - 1] != '\n') {
ostr[slen++] = '\n';
ostr[slen] = '\0';
}
assert(loglev >= 0 && loglev < Dim(xlate_loglev));
sloglev = xlate_loglev[loglev];
for (; *ostr != '\0'; ostr = nstr + 1) {
nstr = strchr(ostr, '\n');
if (nstr == ostr)
continue;
slen = nstr - ostr + 1;
if (curlogfd >= 0) {
if ((retv = dowrite(curlogfd, timbuf, timlen)) > 0)
retv = dowrite(curlogfd, ostr, slen);
if (retv > 0)
continue;
err1 = errno;
if (doclose() == -1)
err2 = errno;
else
err2 = 0;
if (retv == -1)
logerr("write log %s: %s", curfname,
mystrerror(err1));
else
logerr("cannot write %s", curfname);
if (err2 == 0)
logdbg("closed log %s", curfname);
else
logerr("closing log %s: %s", curfname,
mystrerror(err2));
}
syslog(sloglev, "%.*s", slen, ostr);
}
}
void
logdbg(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlogat(LOGLVL_DBG, fmt, args);
va_end(args);
}
void
loginfo(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlogat(LOGLVL_INFO, fmt, args);
va_end(args);
}
void
logwarn(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlogat(LOGLVL_WARN, fmt, args);
va_end(args);
}
void
logerr(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlogat(LOGLVL_ERR, fmt, args);
va_end(args);
}
void
logstrerror(const char *emsg)
{
logerr("%s: %s\n", emsg, mystrerror(errno));
}
void
log_to_stderr(int dbglvl)
{
log_level = dbglvl;
if (curlogfd >= 0)
close_log_files();
curlogfd = STDERR_FILENO;
curfname = stderr_name;
}
void
log_for_service(const char *fname, int dbglvl)
{
int err1, err2;
boolean_t closed;
log_level = dbglvl;
if (fname != NULL &&
(*fname == '\0' || strcasecmp(fname, "syslog") == 0))
fname = NULL;
if (fname == NULL && curfname == NULL)
return;
err1 = err2 = 0;
closed = B_FALSE;
if (curlogfd >= 0) {
if (fname == curfname ||
(fname != NULL && strcmp(fname, curfname) == 0)) {
curfname = fname;
return;
}
if (doclose() == -1)
err1 = errno;
closed = B_TRUE;
}
if (fname != NULL) {
curlogfd = open(fname, O_WRONLY|O_APPEND|O_CREAT, 0600);
if (curlogfd == -1)
err2 = errno;
}
if (closed) {
if (err1 == 0)
logdbg("closed log %s", curfname);
else
logerr("closing log %s: %s", curfname,
mystrerror(err1));
}
if (fname != NULL) {
if (err2 == 0)
logdbg("opened log %s", fname);
else
logerr("opening log %s: %s", fname, mystrerror(err2));
}
curfname = fname;
}
void
close_log_files(void)
{
int err = 0;
if (curlogfd >= 0) {
if (doclose() == -1)
err = errno;
if (err == 0)
logdbg("closed log %s", curfname);
else
logerr("closing log %s: %s", curfname,
mystrerror(err));
}
}
void
reopen_log(void)
{
openlog(prog_name, LOG_PID | LOG_NDELAY | LOG_NOWAIT, LOG_DAEMON);
(void) setlogmask(LOG_UPTO(LOG_DEBUG));
}