/*-
* Copyright (c) 2003 Andrey Simonenko
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
#ifndef lint
static const char rcsid[] ATTR_UNUSED =
"@(#)$Id: ipa_log.c,v 1.1.4.9 2007/05/11 16:29:59 simon Exp $";
#endif /* !lint */
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/queue.h>
#include <unistd.h>
#include "ipa_mod.h"
#include "dlapi.h"
#include "confcommon.h"
#include "memfunc.h"
#include "ipa_ac.h"
#include "ipa_db.h"
#include "ipa_cmd.h"
#include "ipa_ctl.h"
#include "ipa_time.h"
#include "ipa_log.h"
#include "ipa_main.h"
#include "ipa_rules.h"
#include "ipa_autorules.h"
/*
* To prevent possible recursion from mem_* functions,
* libc memory allocation function are used in this function.
*/
/*
* open_log(), logmsg() and mod_logmsg() save errno on enter
* and restore errno on exit, to allow external function to check
* value of errno, changed by some function, which caused error.
*/
#define LOG_STREAM_BUF_SIZE 1024
/*
* Don't call read for caught standard output and error stream
* more than LOG_STREAM_READ_CALLS, to get other code a chance
* to be executed.
*/
#define LOG_STREAM_READ_CALLS 10
/* vlogmsgx() or something like this. */
void (*xvlogmsgx)(int, const char *, va_list);
const char *log_ident = IPA_LOG_IDENT; /* -i <log-ident> */
const char *log_file = NULL; /* -o <log-file> */
static int syslog_logopt; /* Syslog log options. */
static long mypid; /* PID of current process. */
static int log_fd = -1; /* File descriptor of log file. */
#ifndef LOG_FILE_PERM
# define LOG_FILE_PERM (S_IRUSR|S_IWUSR|S_IRGRP) /* 0640 */
#endif
static const char *const priomsg[] = { /* IPA_LOG_xxx -> Message */
"", "W: ", "E: "
};
static const int log_priority[] = { /* IPA_LOG_xxx -> LOG_xxx */
LOG_INFO, LOG_WARNING, LOG_ERR
};
#ifdef WITH_PTHREAD
# define STRERRBUF_SIZE 128 /* Size of buffer for strerror_r(). */
#endif
/*
* Init settings for syslog only.
*/
void
init_log(void)
{
if (log_file == NULL)
syslog_logopt = LOG_PID |
#ifdef LOG_ODELAY
LOG_ODELAY |
#endif
#ifdef LOG_NOWAIT
LOG_NOWAIT |
#endif
(debug ?
#ifdef LOG_PERROR
LOG_PERROR
#else
0
#endif
: LOG_CONS);
}
/*
* This function is called by modules to output their
* log messages.
*/
void
mod_logmsg(const char *mod_name, int priority, int code, const char *format,
va_list ap)
{
int rv, allocated = 0;
int errno_save;
char buf[LOG_BUF_SIZE]; /* Try to use buffer in stack. */
char *ptr;
errno_save = errno; /* Save errno. */
if ( (rv = vsnprintf(buf, sizeof buf, format, ap)) < 0)
ptr = "(mod_logmsg: vsnprintf failed)";
else if (rv >= sizeof buf) {
if ( (ptr = malloc(++rv)) == NULL)
ptr = "(mod_logmsg: malloc failed)";
else {
if (vsnprintf(ptr, rv, format, ap) < 0) {
free(ptr);
ptr = "(mod_logmsg: vsnprintf failed again)";
} else
allocated = 1;
}
} else
ptr = buf;
if (code != 0) {
errno = code;
logmsg(priority, "MOD %s: %s", mod_name, ptr);
} else
logmsgx(priority, "MOD %s: %s", mod_name, ptr);
if (allocated)
free(ptr);
errno = errno_save; /* Restore errno. */
}
/*
* Write a message with a timestamp to the log file and to
* standard error if needed. If stat(2) for the opened
* log file failed, then try to open/create it again.
*/
static void
log_message(const char *message)
{
int fd;
time_t t;
struct iovec iov[3];
struct stat statbuf;
#ifdef WITH_PTHREAD
char buf[26];
char strerrbuf[STRERRBUF_SIZE];
#endif
iov[2].iov_base = "\n";
iov[2].iov_len = 1;
for (;;) {
if (log_fd < 0) {
if ( (log_fd = open(log_file, O_WRONLY|O_APPEND|O_CREAT,
LOG_FILE_PERM)) < 0) {
if (debug) {
iov[0].iov_base = "log_message: open log file failed: ";
iov[0].iov_len = 35;
#ifdef WITH_PTHREAD
iov[1].iov_base =
strerror_r(errno, strerrbuf, sizeof strerrbuf) == 0 ?
strerrbuf : "<strerror_r failed>";
#else
iov[1].iov_base = strerror(errno);
#endif
iov[1].iov_len = strlen(iov[1].iov_base);
(void)fflush(stdout);
(void)writev(STDERR_FILENO, iov, 3);
} else
/* Can't open/create log file and can't log to stderr. */
return;
}
break;
} else {
if (stat(log_file, &statbuf) < 0) {
(void)close(log_fd);
log_fd = -1;
} else
break;
}
}
if (debug) {
fd = STDERR_FILENO;
(void)fflush(stdout);
} else
fd = log_fd;
if (time(&t) == (time_t)-1) {
iov[0].iov_base = "log_message: time failed: ";
iov[0].iov_len = 26;
#ifdef WITH_PTHREAD
iov[1].iov_base =
strerror_r(errno, strerrbuf, sizeof strerrbuf) == 0 ?
strerrbuf : "<strerror_r failed>";
#else
iov[1].iov_base = strerror(errno);
#endif
iov[1].iov_len = strlen(iov[1].iov_base);
} else {
/* DDD MMM dd hh:mm:ss YYYY\n\0 */
#ifdef WITH_PTHREAD
iov[0].iov_base = ctime_r(&t, buf) + 4; /* Skip DDD. */
#else
iov[0].iov_base = ctime(&t) + 4; /* Skip DDD. */
#endif
iov[0].iov_len = 20; /* MMM...YYY */
iov[1].iov_base = (char *)message;
iov[1].iov_len = strlen(message);
}
for (;;) {
if (writev(fd, iov, 3) < 0)
if (debug && fd != STDERR_FILENO) {
(void)close(log_fd);
log_fd = -1;
return;
}
if (debug && fd == STDERR_FILENO) {
if ( (fd = log_fd) < 0)
break;
} else
break;
}
}
/*
* Call openlog for syslog (real open of log descriptor is delayed).
*/
void
open_log(void)
{
int errno_save;
if (log_file == NULL) {
errno_save = errno; /* Save errno. */
openlog(log_ident, syslog_logopt, LOG_USER);
errno = errno_save; /* Restore errno. */
} else {
mypid = (long)getpid();
log_fd = -1;
}
}
/*
* Close log descriptor.
*/
void
close_log(void)
{
if (log_file == NULL)
closelog();
else if (log_fd >= 0) {
(void)close(log_fd);
log_fd = -1;
}
}
/*
* Wrapper for log function from memfunc.c.
*/
void
mvlogmsgx_wrapper(const char *format, va_list ap)
{
/*
* Since mxxx functions log messages when errors
* occurred, we simply use fix log priority here.
*/
vlogmsgx(IPA_LOG_ERR, format, ap);
}
/*
* vprintf-like function, do not output message for errno.
*/
void
vlogmsgx(int priority, const char *format, va_list ap)
{
if (log_file == NULL) {
#ifdef HAVE_VSYSLOG
vsyslog(log_priority[priority], format, ap);
#else
char buf[LOG_BUF_SIZE];
if (vsnprintf(buf, sizeof buf, format, ap) >= 0)
syslog(log_priority[priority], buf);
#endif
#ifdef LOG_PERROR
return;
#endif
}
{
int rv, allocated1, allocated2;
char *ptr1, *ptr2;
char buf1[LOG_BUF_SIZE];
char buf2[LOG_BUF_SIZE + 128];
allocated1 = allocated2 = 0;
if ( (rv = vsnprintf(buf1, sizeof buf1, format, ap)) < 0)
ptr1 = "(vlogmsgx: vsnprintf failed for ptr1)";
else if (rv >= sizeof buf1) {
if ( (ptr1 = malloc(++rv)) == NULL)
ptr1 = "(vlogmsgx: malloc failed for ptr1)";
else {
if (vsnprintf(ptr1, rv, format, ap) < 0) {
free(ptr1);
ptr1 = "(vlogmsgx: vsnprintf failed again for ptr1)";
} else
allocated1 = 1;
}
} else
ptr1 = buf1;
if ( (rv = snprintf(buf2, sizeof buf2, " %s[%ld]: %s%s",
log_ident, mypid, priomsg[priority], ptr1)) < 0)
ptr2 = "(vlogmsgx: vsnprintf failed for ptr2)";
else if (rv >= sizeof buf2) {
if ( (ptr2 = malloc(++rv)) == NULL)
ptr2 = "(vlogmsgx: malloc failed for ptr2)";
else {
if (vsnprintf(ptr2, rv, format, ap) < 0) {
free(ptr2);
ptr2 = "(vlogmsgx: vsnprintf failed again for ptr2)";
} else
allocated2 = 1;
}
} else
ptr2 = buf2;
log_message(ptr2);
if (allocated1)
free(ptr1);
if (allocated2)
free(ptr2);
}
}
/*
* The same as vlogmsgx, but output everything on stderr.
*/
void
vlogmsgx_stderr(int priority, const char *format, va_list ap)
{
fflush(stdout);
switch (priority) {
case IPA_LOG_ERR:
fprintf(stderr, "Error: ");
break;
case IPA_LOG_WARNING:
fprintf(stderr, "Warning: ");
break;
default: /* IPA_LOG_INFO */
fprintf(stderr, "Info: ");
}
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
}
/*
* printf-like function, do not output message for errno.
*/
void
logmsgx(int priority, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vlogmsgx(priority, format, ap);
va_end(ap);
}
/*
* printf-like function, also output message for errno.
*/
void
logmsg(int priority, const char *format, ...)
{
va_list ap;
if (errno == 0) {
va_start(ap, format);
vlogmsgx(priority, format, ap);
va_end(ap);
return;
}
{
int rv, allocated = 0;
int errno_save;
char buf[LOG_BUF_SIZE]; /* Try to use buffer in stack. */
char *ptr;
#ifdef WITH_PTHREAD
char strerrbuf[STRERRBUF_SIZE];
#endif
errno_save = errno; /* Save errno. */
va_start(ap, format);
if ( (rv = vsnprintf(buf, sizeof buf, format, ap)) < 0)
ptr = "(logmsg: vsnprintf failed)";
else if (rv >= sizeof buf) {
if ( (ptr = malloc(++rv)) == NULL)
ptr = "(logmsg: malloc failed)";
else {
if (vsnprintf(ptr, rv, format, ap) < 0) {
free(ptr);
ptr = "(logmsg: vsnprintf failed again)";
} else
allocated = 1;
}
} else
ptr = buf;
va_end(ap);
#ifdef WITH_PTHREAD
if (strerror_r(errno_save, strerrbuf, sizeof strerrbuf) == 0)
logmsgx(priority, "%s: %s", ptr, strerrbuf);
else
logmsgx(priority, "%s: error code %d", ptr, errno_save);
#else
logmsgx(priority, "%s: %s", ptr, strerror(errno_save));
#endif
if (allocated)
free(ptr);
errno = errno_save; /* Restore errno. */
}
}
/*
* Call xvlomsgx() which is a pointer to some vlogmsgx-like function.
*/
void
xlogmsgx(int priority, const char *format, ...)
{
va_list ap;
va_start(ap, format);
xvlogmsgx(priority, format, ap);
va_end(ap);
}
/*
* Read data from nonblockable fd and log them prepending with
* stream name string, interpret '\0' and '\n' as new lines, call
* read() syscall for fd no more than LOG_STREAM_READ_CALLS times.
* This function is used only by log_stdout and log_stderr.
*/
void
log_stream(int fd, const char *stream_name)
{
char string[LOG_STREAM_BUF_SIZE], *ptr1, *ptr2;
int i;
u_int ncalls = 0; /* Number of read() invocations. */
ssize_t nread; /* Number of read bytes from last read(). */
size_t ngot; /* Number of valid bytes in string. */
size_t nleft; /* Number of not used bytes in string. */
ptr1 = string;
nleft = sizeof(string) - 1;
ngot = 0;
for (;;) {
if ( (nread = read(fd, ptr1, nleft)) < 0) {
if (errno == EWOULDBLOCK) {
if (ptr1 != string)
logmsgx(IPA_LOG_WARNING, "*%s: %s",
stream_name, string);
break;
} else {
logmsg(IPA_LOG_ERR, "log_stream(%s): read",
stream_name);
break;
}
}
ngot += nread;
for (i = 0, ptr1 = ptr2 = string; i < ngot; ++ptr2, ++i)
switch (*ptr2) {
case '\n':
*ptr2 = '\0';
/* FALLTHROUGH */
case '\0':
logmsgx(IPA_LOG_WARNING, "*%s: %s",
stream_name, ptr1);
ptr1 = ptr2 + 1;
}
if (ptr1 == string) {
ptr1[ngot] = '\0';
ngot = 0;
nleft = sizeof(string) - 1;
logmsgx(IPA_LOG_WARNING, "*%s: %s", stream_name, ptr1);
} else if (ptr1 != ptr2) {
ngot = ptr2 - ptr1;
memcpy(string, ptr1, ngot);
ptr1 = string + ngot;
*ptr1 = '\0';
nleft = sizeof(string) - ngot - 1;
} else {
ptr1 = string;
ngot = 0;
nleft = sizeof(string) - 1;
}
if (++ncalls == LOG_STREAM_READ_CALLS) {
if (ngot != 0) /* and has '\0' in string. */
logmsgx(IPA_LOG_WARNING, "*%s: %s",
stream_name, string);
break;
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1