547 lines
12 KiB
C
547 lines
12 KiB
C
/*
|
|
* FCRON - periodic command scheduler
|
|
*
|
|
* Copyright 2000-2014 Thibault Godouet <fcron@free.fr>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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 General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* The GNU General Public License can also be found in the file
|
|
* `LICENSE' that comes with the fcron source distribution.
|
|
*/
|
|
|
|
|
|
/* This code was originally inspired by Anacron's sources of
|
|
Itai Tzur <itzur@actcom.co.il> */
|
|
|
|
|
|
#include "fcron.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
#ifdef DEBUG
|
|
char debug_opt = 1; /* set to 1 if we are in debug mode */
|
|
#else
|
|
char debug_opt = 0; /* set to 1 if we are in debug mode */
|
|
#endif
|
|
int dosyslog = 1;
|
|
char *logfile_path = NULL;
|
|
|
|
|
|
char *make_msg(const char *append, char *fmt, va_list args);
|
|
void log_syslog_str(int priority, char *msg);
|
|
void log_file_str(int priority, char *msg);
|
|
void log_console_str(int priority, char *msg);
|
|
void log_fd_str(int fd, char *msg);
|
|
static void print_line_prefix(FILE * logfile, int priority);
|
|
static void xlog(int priority, int fd, char *fmt, va_list args);
|
|
static void xlog_e(int priority, int fd, char *fmt, va_list args);
|
|
#ifdef HAVE_LIBPAM
|
|
static void log_pame(int priority, pam_handle_t * pamh, int pamerrno,
|
|
char *fmt, va_list args);
|
|
#endif
|
|
|
|
static char truncated[] = " (truncated)";
|
|
static int log_open = 0;
|
|
static FILE *logfile = NULL;
|
|
|
|
|
|
/* Initialise logging to either syslog or a file specified in fcron.conf,
|
|
* or take no action if logging is suppressed.
|
|
* This function will be called automatically if you attempt to log something,
|
|
* however you may have to call it explicitely as it needs to run before the
|
|
* program becomes a daemon so as it can print an error on the console
|
|
* if it can't open the logs correctly. */
|
|
void
|
|
xopenlog(void)
|
|
{
|
|
if (log_open)
|
|
return;
|
|
|
|
/* we MUST set log_open to 1 before doing anything else. That way,
|
|
* if we call a function that logs something, which calls xopenlog,
|
|
* then we won't end up in a nasty loop */
|
|
log_open = 1;
|
|
|
|
// are we logging to a file or using syslog or not logging at all?
|
|
if (dosyslog) {
|
|
openlog(prog_name, LOG_PID, SYSLOG_FACILITY);
|
|
}
|
|
|
|
if (logfile_path != NULL) {
|
|
logfile = fopen(logfile_path, "a");
|
|
if (logfile == NULL) {
|
|
int saved_errno = errno;
|
|
|
|
if (dosyslog) {
|
|
/* we have already called openlog() which cannot fail */
|
|
syslog(COMPLAIN_LEVEL, "Could not fopen log file '%s': %s",
|
|
logfile_path, strerror(saved_errno));
|
|
}
|
|
|
|
print_line_prefix(stderr, COMPLAIN_LEVEL);
|
|
fprintf(stderr, "Could not fopen log file '%s': %s\n", logfile_path,
|
|
strerror(saved_errno));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void
|
|
xcloselog()
|
|
{
|
|
if (!log_open)
|
|
return;
|
|
|
|
// check whether we need to close syslog, or a file.
|
|
if (logfile != NULL) {
|
|
/* we must NOT use xfclose_check() in log.c to avoid infinite loops */
|
|
if (xfclose(&logfile) != 0) {
|
|
int saved_errno = errno;
|
|
|
|
syslog(COMPLAIN_LEVEL, "Error while closing log file '%s': %s",
|
|
logfile_path, strerror(saved_errno));
|
|
|
|
if (foreground == 1) {
|
|
print_line_prefix(stderr, COMPLAIN_LEVEL);
|
|
fprintf(stderr, "Error while closing log file '%s': %s\n",
|
|
logfile_path, strerror(saved_errno));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dosyslog) {
|
|
closelog();
|
|
}
|
|
|
|
log_open = 0;
|
|
}
|
|
|
|
|
|
/* Construct the message string from its parts, and append a string to it */
|
|
char *
|
|
make_msg(const char *append, char *fmt, va_list args)
|
|
{
|
|
int len;
|
|
char *msg = NULL;
|
|
|
|
if ((msg = calloc(1, MAX_MSG + 1)) == NULL)
|
|
return NULL;
|
|
/* There's some confusion in the documentation about what vsnprintf
|
|
* returns when the buffer overflows. Hmmm... */
|
|
len = vsnprintf(msg, MAX_MSG + 1, fmt, args);
|
|
if (append != NULL) {
|
|
size_t size_to_cat = ((MAX_MSG - len) > 0) ? (MAX_MSG - len) : 0;
|
|
strncat(msg, ": ", size_to_cat);
|
|
strncat(msg, append, size_to_cat);
|
|
len += 2 + strlen(append);
|
|
}
|
|
if (len >= MAX_MSG)
|
|
strcpy(msg + (MAX_MSG - 1) - sizeof(truncated), truncated);
|
|
|
|
return msg;
|
|
}
|
|
|
|
|
|
/* log a simple string to syslog if needed */
|
|
void
|
|
log_syslog_str(int priority, char *msg)
|
|
{
|
|
if (dosyslog) {
|
|
xopenlog();
|
|
syslog(priority, "%s", msg);
|
|
}
|
|
}
|
|
|
|
/* log a simple string to a log file if needed */
|
|
void
|
|
log_file_str(int priority, char *msg)
|
|
{
|
|
xopenlog();
|
|
|
|
/* we may have failed to open the logfile - check if
|
|
* it does exist *after* xopenlog() */
|
|
if (logfile != NULL) {
|
|
print_line_prefix(logfile, priority);
|
|
fprintf(logfile, "%s\n", msg);
|
|
fflush(logfile);
|
|
}
|
|
|
|
}
|
|
|
|
/* log a simple string to console if needed */
|
|
void
|
|
log_console_str(int priority, char *msg)
|
|
{
|
|
if (foreground == 1) {
|
|
print_line_prefix(stderr, priority);
|
|
fprintf(stderr, "%s\n", msg);
|
|
}
|
|
}
|
|
|
|
/* log a simple string to fd if needed */
|
|
void
|
|
log_fd_str(int fd, char *msg)
|
|
{
|
|
if (fd >= 0) {
|
|
send(fd, msg, strlen(msg), 0);
|
|
send(fd, "\n", strlen("\n"), 0);
|
|
}
|
|
}
|
|
|
|
/* Log a message, described by "fmt" and "args", with the specified
|
|
* "priority". */
|
|
/* write it also to fd if positive, and to stderr if foreground==1 */
|
|
static void
|
|
xlog(int priority, int fd, char *fmt, va_list args)
|
|
{
|
|
char *msg;
|
|
|
|
if ((msg = make_msg(NULL, fmt, args)) == NULL)
|
|
return;
|
|
|
|
log_syslog_str(priority, msg);
|
|
log_file_str(priority, msg);
|
|
log_console_str(priority, msg);
|
|
log_fd_str(fd, msg);
|
|
|
|
Free_safe(msg);
|
|
}
|
|
|
|
/* Same as xlog(), but also appends an error description corresponding
|
|
* to "errno". */
|
|
static void
|
|
xlog_e(int priority, int fd, char *fmt, va_list args)
|
|
{
|
|
int saved_errno;
|
|
char *msg;
|
|
|
|
saved_errno = errno;
|
|
|
|
if ((msg = make_msg(strerror(saved_errno), fmt, args)) == NULL)
|
|
return;
|
|
|
|
log_syslog_str(priority, msg);
|
|
log_file_str(priority, msg);
|
|
log_console_str(priority, msg);
|
|
log_fd_str(fd, msg);
|
|
|
|
Free_safe(msg);
|
|
}
|
|
|
|
/* write a message to the file specified by logfile. */
|
|
static void
|
|
print_line_prefix(FILE * logfile, int priority)
|
|
{
|
|
time_t t = time(NULL);
|
|
struct tm *ft;
|
|
char date[30];
|
|
char *type;
|
|
|
|
// print the current time as a string.
|
|
ft = localtime(&t);
|
|
date[0] = '\0';
|
|
strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", ft);
|
|
|
|
// is it an info/warning/error/debug message?
|
|
switch (priority) {
|
|
case EXPLAIN_LEVEL:
|
|
type = " INFO";
|
|
break;
|
|
case WARNING_LEVEL:
|
|
type = " WARN";
|
|
break;
|
|
case COMPLAIN_LEVEL:
|
|
type = "ERROR";
|
|
break;
|
|
case DEBUG_LEVEL:
|
|
type = "DEBUG";
|
|
break;
|
|
default:
|
|
type = "UNKNOWN_SEVERITY";
|
|
}
|
|
|
|
// print the log message.
|
|
fprintf(logfile, "%s %s ", date, type);
|
|
}
|
|
|
|
|
|
#ifdef HAVE_LIBPAM
|
|
/* Same as log_syslog(), but also appends an error description corresponding
|
|
* to the pam_error. */
|
|
static void
|
|
log_pame(int priority, pam_handle_t * pamh, int pamerrno, char *fmt,
|
|
va_list args)
|
|
{
|
|
char *msg;
|
|
|
|
if ((msg = make_msg(pam_strerror(pamh, pamerrno), fmt, args)) == NULL)
|
|
return;
|
|
|
|
log_syslog_str(priority, msg);
|
|
log_console_str(priority, msg);
|
|
|
|
xcloselog();
|
|
|
|
Free_safe(msg);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Log an "explain" level message */
|
|
void
|
|
explain(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
xlog(EXPLAIN_LEVEL, -1, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/* as explain(), but also write message to fd if positive */
|
|
void
|
|
explain_fd(int fd, char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
xlog(EXPLAIN_LEVEL, fd, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
/* Log an "explain" level message, with an error description */
|
|
void
|
|
explain_e(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
xlog_e(EXPLAIN_LEVEL, -1, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
/* Log a "warning" level message */
|
|
void
|
|
warn(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
xlog(WARNING_LEVEL, -1, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/* as warn(), but also write message to fd if positive */
|
|
void
|
|
warn_fd(int fd, char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
xlog(WARNING_LEVEL, fd, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
/* Log a "warning" level message, with an error description */
|
|
void
|
|
warn_e(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
xlog_e(WARNING_LEVEL, -1, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
/* Log a "complain" level message */
|
|
void
|
|
error(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
xlog(COMPLAIN_LEVEL, -1, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/* as error(), but also write message to fd if positive */
|
|
void
|
|
error_fd(int fd, char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
xlog(COMPLAIN_LEVEL, fd, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
/* Log a "complain" level message, with an error description */
|
|
void
|
|
error_e(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
xlog_e(COMPLAIN_LEVEL, -1, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
#ifdef HAVE_LIBPAM
|
|
/* Log a "complain" level message, with a PAM error description */
|
|
void
|
|
error_pame(pam_handle_t * pamh, int pamerrno, char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
xcloselog(); /* PAM is likely to have used openlog() */
|
|
|
|
va_start(args, fmt);
|
|
log_pame(COMPLAIN_LEVEL, pamh, pamerrno, fmt, args);
|
|
va_end(args);
|
|
}
|
|
#endif
|
|
|
|
/* Log a "complain" level message, and exit */
|
|
void
|
|
die(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
xlog(COMPLAIN_LEVEL, -1, fmt, args);
|
|
va_end(args);
|
|
if (getpid() == daemon_pid) {
|
|
error("Aborted");
|
|
}
|
|
else {
|
|
error("fcron child aborted: this does not affect the main fcron daemon,"
|
|
" but this may prevent a job from being run or an email from being sent.");
|
|
}
|
|
|
|
exit(EXIT_ERR);
|
|
|
|
}
|
|
|
|
|
|
/* Log a "complain" level message, with an error description, and exit */
|
|
void
|
|
die_e(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
int err_no = 0;
|
|
|
|
err_no = errno;
|
|
|
|
va_start(args, fmt);
|
|
xlog_e(COMPLAIN_LEVEL, -1, fmt, args);
|
|
va_end(args);
|
|
if (getpid() == daemon_pid) {
|
|
error("Aborted");
|
|
}
|
|
else {
|
|
error("fcron child aborted: this does not affect the main fcron daemon,"
|
|
" but this may prevent a job from being run or an email from being sent.");
|
|
}
|
|
|
|
|
|
exit(err_no);
|
|
|
|
}
|
|
|
|
#ifdef HAVE_LIBPAM
|
|
/* Log a "complain" level message, with a PAM error description, and exit */
|
|
void
|
|
die_pame(pam_handle_t * pamh, int pamerrno, char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
xcloselog(); /* PAM is likely to have used openlog() */
|
|
|
|
va_start(args, fmt);
|
|
log_pame(COMPLAIN_LEVEL, pamh, pamerrno, fmt, args);
|
|
va_end(args);
|
|
pam_end(pamh, pamerrno);
|
|
if (getpid() == daemon_pid)
|
|
error("Aborted");
|
|
|
|
exit(EXIT_ERR);
|
|
|
|
}
|
|
#endif
|
|
|
|
/* Log a "debug" level message */
|
|
void
|
|
Debug(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
xlog(DEBUG_LEVEL, -1, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/* write message to fd, and to syslog in "debug" level message if debug_opt */
|
|
void
|
|
send_msg_fd_debug(int fd, char *fmt, ...)
|
|
{
|
|
char *msg;
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
if ((msg = make_msg(NULL, fmt, args)) == NULL)
|
|
return;
|
|
|
|
if (debug_opt)
|
|
log_syslog_str(DEBUG_LEVEL, msg);
|
|
|
|
log_fd_str(fd, msg);
|
|
|
|
Free_safe(msg);
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
/* write message to fd */
|
|
void
|
|
send_msg_fd(int fd, char *fmt, ...)
|
|
{
|
|
char *msg;
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
if ((msg = make_msg(NULL, fmt, args)) == NULL)
|
|
return;
|
|
|
|
log_fd_str(fd, msg);
|
|
|
|
Free_safe(msg);
|
|
|
|
va_end(args);
|
|
}
|