blob: 35752573a9bfdea17c4475968dc3b4320a20d731 [file] [log] [blame] [edit]
/*
* Copyright (C) Tildeslash Ltd. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
*
* You must obey the GNU Affero General Public License in all respects
* for all of the code used other than OpenSSL.
*/
#include "config.h"
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include "monit.h"
/**
* Implementation of a logger that appends log messages to a file
* with a preceding timestamp. Methods support both syslog or own
* logfile.
*
* @file
*/
/* ------------------------------------------------------------- Definitions */
static FILE *LOG= NULL;
static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct mylogpriority {
int priority;
char *description;
} logPriority[]= {
{LOG_EMERG, "emergency"},
{LOG_ALERT, "alert"},
{LOG_CRIT, "critical"},
{LOG_ERR, "error"},
{LOG_WARNING, "warning"},
{LOG_NOTICE, "notice"},
{LOG_INFO, "info"},
{LOG_DEBUG, "debug"},
{-1, NULL}
};
/* -------------------------------------------------------------- Prototypes */
static int open_log();
static char *timefmt(char *t, int size);
static const char *logPriorityDescription(int p);
static void log_log(int priority, const char *s, va_list ap);
static void log_backtrace();
/* ------------------------------------------------------------------ Public */
/**
* Initialize the log system and 'log' function
* @return TRUE if the log system was successfully initialized
*/
int log_init() {
if (!Run.dolog) {
return TRUE;
}
if (!open_log()) {
return FALSE;
}
/* Register log_close to be
called at program termination */
atexit(log_close);
return TRUE;
}
/**
* Logging interface with priority support
* @param s A formated (printf-style) string to log
*/
void LogEmergency(const char *s, ...) {
va_list ap;
ASSERT(s);
va_start(ap, s);
log_log(LOG_EMERG, s, ap);
va_end(ap);
log_backtrace();
}
/**
* Logging interface with priority support
* @param s A formated (printf-style) string to log
*/
void LogAlert(const char *s, ...) {
va_list ap;
ASSERT(s);
va_start(ap, s);
log_log(LOG_ALERT, s, ap);
va_end(ap);
log_backtrace();
}
/**
* Logging interface with priority support
* @param s A formated (printf-style) string to log
*/
void LogCritical(const char *s, ...) {
va_list ap;
ASSERT(s);
va_start(ap, s);
log_log(LOG_CRIT, s, ap);
va_end(ap);
log_backtrace();
}
/*
* Called by libmonit on Exception. Log
* error and abort the application
*/
void vLogAbortHandler(const char *s, va_list ap) {
va_list ap_copy;
ASSERT(s);
va_copy(ap_copy, ap);
log_log(LOG_CRIT, s, ap);
va_end(ap_copy);
abort();
}
/**
* Logging interface with priority support
* @param s A formated (printf-style) string to log
*/
void LogError(const char *s, ...) {
va_list ap;
ASSERT(s);
va_start(ap, s);
log_log(LOG_ERR, s, ap);
va_end(ap);
log_backtrace();
}
/**
* Logging interface with priority support
* @param s A formated (printf-style) string to log
*/
void vLogError(const char *s, va_list ap) {
va_list ap_copy;
ASSERT(s);
va_copy(ap_copy, ap);
log_log(LOG_ERR, s, ap);
va_end(ap_copy);
log_backtrace();
}
/**
* Logging interface with priority support
* @param s A formated (printf-style) string to log
*/
void LogWarning(const char *s, ...) {
va_list ap;
ASSERT(s);
va_start(ap, s);
log_log(LOG_WARNING, s, ap);
va_end(ap);
}
/**
* Logging interface with priority support
* @param s A formated (printf-style) string to log
*/
void LogNotice(const char *s, ...) {
va_list ap;
ASSERT(s);
va_start(ap, s);
log_log(LOG_NOTICE, s, ap);
va_end(ap);
}
/**
* Logging interface with priority support
* @param s A formated (printf-style) string to log
*/
void LogInfo(const char *s, ...) {
va_list ap;
ASSERT(s);
va_start(ap, s);
log_log(LOG_INFO, s, ap);
va_end(ap);
}
/**
* Logging interface with priority support
* @param s A formated (printf-style) string to log
*/
void LogDebug(const char *s, ...) {
va_list ap;
ASSERT(s);
va_start(ap, s);
log_log(LOG_DEBUG, s, ap);
va_end(ap);
}
/**
* Close the log file or syslog
*/
void log_close() {
if (Run.use_syslog) {
closelog();
}
if (LOG && (0 != fclose(LOG))) {
LogError("%s: Error closing the log file -- %s\n", prog, STRERROR);
}
LOG= NULL;
}
#ifndef HAVE_VSYSLOG
#ifdef HAVE_SYSLOG
void vsyslog (int facility_priority, const char *format, va_list arglist) {
char msg[STRLEN+1];
vsnprintf(msg, STRLEN, format, arglist);
syslog(facility_priority, "%s", msg);
}
#endif /* HAVE_SYSLOG */
#endif /* HAVE_VSYSLOG */
/* ----------------------------------------------------------------- Private */
/**
* Open a log file or syslog
*/
static int open_log() {
if (Run.use_syslog) {
openlog(prog, LOG_PID, Run.facility);
} else {
umask(LOGMASK);
if ((LOG= fopen(Run.logfile,"a+")) == (FILE *)NULL) {
LogError("%s: Error opening the log file '%s' for writing -- %s\n", prog, Run.logfile, STRERROR);
return(FALSE);
}
/* Set logger in unbuffered mode */
setvbuf(LOG, NULL, _IONBF, 0);
}
return TRUE;
}
/**
* Returns the current time as a formated string, see the TIMEFORMAT
* macro in monit.h
*/
static char *timefmt(char *t, int size) {
time_t now;
struct tm tm;
time(&now);
localtime_r(&now, &tm);
if ( !strftime(t, size, TIMEFORMAT, &tm))
*t = 0;
return t;
}
/**
* Get a textual description of the actual log priority.
* @param p The log priority
* @return A string describing the log priority in clear text. If the
* priority is not found NULL is returned.
*/
static const char *logPriorityDescription(int p) {
struct mylogpriority *lp= logPriority;
while ((*lp).description)
{
if (p == (*lp).priority)
{
return (*lp).description;
}
lp++;
}
return "unknown";
}
/**
* Log a message to monits logfile or syslog.
* @param priority A message priority
* @param s A formated (printf-style) string to log
*/
static void log_log(int priority, const char *s, va_list ap) {
#ifdef HAVE_VA_COPY
va_list ap_copy;
#endif
ASSERT(s);
LOCK(log_mutex)
#ifdef HAVE_VA_COPY
va_copy(ap_copy, ap);
vfprintf(stderr, s, ap_copy);
va_end(ap_copy);
#else
vfprintf(stderr, s, ap);
#endif
fflush(stderr);
if (Run.dolog) {
if (Run.use_syslog) {
#ifdef HAVE_VA_COPY
va_copy(ap_copy, ap);
vsyslog(priority, s, ap_copy);
va_end(ap_copy);
#else
vsyslog(priority, s, ap);
#endif
} else if (LOG) {
char datetime[STRLEN];
fprintf(LOG, "[%s] %-8s : ", timefmt(datetime, STRLEN), logPriorityDescription(priority));
#ifdef HAVE_VA_COPY
va_copy(ap_copy, ap);
vfprintf(LOG, s, ap_copy);
va_end(ap_copy);
#else
vfprintf(LOG, s, ap);
#endif
}
}
END_LOCK;
}
static void log_backtrace() {
#ifdef HAVE_BACKTRACE
int i, frames;
void *callstack[128];
char **strs;
if (Run.debug >= 2) {
frames = backtrace(callstack, 128);
strs = backtrace_symbols(callstack, frames);
LogDebug("-------------------------------------------------------------------------------\n");
for (i = 0; i < frames; ++i)
LogDebug(" %s\n", strs[i]);
LogDebug("-------------------------------------------------------------------------------\n");
FREE(strs);
}
#endif
}