blob: dbe08e7509047d9aa4f5ce852af84b6c8d87b6e5 [file] [log] [blame]
/*
* "$Id: log.c 6875 2007-08-27 23:25:06Z mike $"
*
* Log file routines for the Common UNIX Printing System (CUPS).
*
* Copyright 2007 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
* property of Apple Inc. and are protected by Federal copyright
* law. Distribution and use rights are outlined in the file "LICENSE.txt"
* which should have been included with this file. If this file is
* file is missing or damaged, see the license at "http://www.cups.org/".
*
* Contents:
*
* cupsdGetDateTime() - Returns a pointer to a date/time string.
* cupsdLogGSSMessage() - Log a GSSAPI error...
* cupsdLogMessage() - Log a message to the error log file.
* cupsdLogPage() - Log a page to the page log file.
* cupsdLogRequest() - Log an HTTP request in Common Log Format.
* check_log_file() - Open/rotate a log file if it needs it.
*/
/*
* Include necessary headers...
*/
#include "cupsd.h"
#include <stdarg.h>
#include <syslog.h>
/*
* Local functions...
*/
static int check_log_file(cups_file_t **, const char *);
/*
* 'cupsdGetDateTime()' - Returns a pointer to a date/time string.
*/
char * /* O - Date/time string */
cupsdGetDateTime(time_t t) /* I - Time value */
{
struct tm *date; /* Date/time value */
static time_t last_time = -1; /* Last time value */
static char s[1024]; /* Date/time string */
static const char * const months[12] =/* Months */
{
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
if (t != last_time)
{
last_time = t;
/*
* Get the date and time from the UNIX time value, and then format it
* into a string. Note that we *can't* use the strftime() function since
* it is localized and will seriously confuse automatic programs if the
* month names are in the wrong language!
*
* Also, we use the "timezone" variable that contains the current timezone
* offset from GMT in seconds so that we are reporting local time in the
* log files. If you want GMT, set the TZ environment variable accordingly
* before starting the scheduler.
*
* (*BSD and Darwin store the timezone offset in the tm structure)
*/
date = localtime(&t);
snprintf(s, sizeof(s), "[%02d/%s/%04d:%02d:%02d:%02d %+03ld%02ld]",
date->tm_mday, months[date->tm_mon], 1900 + date->tm_year,
date->tm_hour, date->tm_min, date->tm_sec,
#ifdef HAVE_TM_GMTOFF
date->tm_gmtoff / 3600, (date->tm_gmtoff / 60) % 60);
#else
timezone / 3600, (timezone / 60) % 60);
#endif /* HAVE_TM_GMTOFF */
}
return (s);
}
#ifdef HAVE_GSSAPI
/*
* 'cupsdLogGSSMessage()' - Log a GSSAPI error...
*/
int /* O - 1 on success, 0 on error */
cupsdLogGSSMessage(
int level, /* I - Log level */
int major_status, /* I - Major GSSAPI status */
int minor_status, /* I - Minor GSSAPI status */
const char *message, /* I - printf-style message string */
...) /* I - Additional args as needed */
{
OM_uint32 err_major_status, /* Major status code for display */
err_minor_status; /* Minor status code for display */
OM_uint32 msg_ctx; /* Message context */
gss_buffer_desc major_status_string = GSS_C_EMPTY_BUFFER,
/* Major status message */
minor_status_string = GSS_C_EMPTY_BUFFER;
/* Minor status message */
int ret; /* Return value */
msg_ctx = 0;
err_major_status = gss_display_status(&err_minor_status,
major_status,
GSS_C_GSS_CODE,
GSS_C_NO_OID,
&msg_ctx,
&major_status_string);
if (!GSS_ERROR(err_major_status))
err_major_status = gss_display_status(&err_minor_status,
minor_status,
GSS_C_MECH_CODE,
GSS_C_NULL_OID,
&msg_ctx,
&minor_status_string);
ret = cupsdLogMessage(level, "%s: %s, %s", message,
(char *)major_status_string.value,
(char *)minor_status_string.value);
gss_release_buffer(&err_minor_status, &major_status_string);
gss_release_buffer(&err_minor_status, &minor_status_string);
return (ret);
}
#endif /* HAVE_GSSAPI */
/*
* 'cupsdLogMessage()' - Log a message to the error log file.
*/
int /* O - 1 on success, 0 on error */
cupsdLogMessage(int level, /* I - Log level */
const char *message, /* I - printf-style message string */
...) /* I - Additional args as needed */
{
int len; /* Length of message */
va_list ap; /* Argument pointer */
static const char levels[] = /* Log levels... */
{
' ',
'X',
'A',
'C',
'E',
'W',
'N',
'I',
'D',
'd'
};
#ifdef HAVE_VSYSLOG
static const int syslevels[] = /* SYSLOG levels... */
{
0,
LOG_EMERG,
LOG_ALERT,
LOG_CRIT,
LOG_ERR,
LOG_WARNING,
LOG_NOTICE,
LOG_INFO,
LOG_DEBUG,
LOG_DEBUG
};
#endif /* HAVE_VSYSLOG */
static int linesize = 0; /* Size of line for output file */
static char *line = NULL; /* Line for output file */
/*
* See if we want to log this message...
*/
if (TestConfigFile)
{
if (level <= CUPSD_LOG_WARN)
{
va_start(ap, message);
vfprintf(stderr, message, ap);
putc('\n', stderr);
va_end(ap);
}
return (1);
}
if (level > LogLevel || !ErrorLog)
return (1);
#ifdef HAVE_VSYSLOG
/*
* See if we are logging errors via syslog...
*/
if (!strcmp(ErrorLog, "syslog"))
{
va_start(ap, message);
vsyslog(syslevels[level], message, ap);
va_end(ap);
return (1);
}
#endif /* HAVE_VSYSLOG */
/*
* Not using syslog; check the log file...
*/
if (!check_log_file(&ErrorFile, ErrorLog))
return (0);
/*
* Print the log level and date/time...
*/
cupsFilePrintf(ErrorFile, "%c %s ", levels[level], cupsdGetDateTime(time(NULL)));
/*
* Allocate the line buffer as needed...
*/
if (!linesize)
{
linesize = 8192;
line = malloc(linesize);
if (!line)
{
cupsFilePrintf(ErrorFile,
"ERROR: Unable to allocate memory for line - %s\n",
strerror(errno));
cupsFileFlush(ErrorFile);
return (0);
}
}
/*
* Format the log message...
*/
va_start(ap, message);
len = vsnprintf(line, linesize, message, ap);
va_end(ap);
/*
* Resize the buffer as needed...
*/
if (len >= linesize)
{
len ++;
if (len < 8192)
len = 8192;
else if (len > 65536)
len = 65536;
line = realloc(line, len);
if (line)
linesize = len;
else
{
cupsFilePrintf(ErrorFile,
"ERROR: Unable to allocate memory for line - %s\n",
strerror(errno));
cupsFileFlush(ErrorFile);
return (0);
}
va_start(ap, message);
len = vsnprintf(line, linesize, message, ap);
va_end(ap);
}
if (len >= linesize)
len = linesize - 1;
/*
* Then the log message...
*/
cupsFilePuts(ErrorFile, line);
/*
* Then a newline...
*/
if (len > 0 && line[len - 1] != '\n')
cupsFilePutChar(ErrorFile, '\n');
/*
* Flush the line to the file and return...
*/
cupsFileFlush(ErrorFile);
return (1);
}
/*
* 'cupsdLogPage()' - Log a page to the page log file.
*/
int /* O - 1 on success, 0 on error */
cupsdLogPage(cupsd_job_t *job, /* I - Job being printed */
const char *page) /* I - Page being printed */
{
ipp_attribute_t *billing, /* job-billing attribute */
*hostname; /* job-originating-host-name attribute */
billing = ippFindAttribute(job->attrs, "job-billing", IPP_TAG_ZERO);
hostname = ippFindAttribute(job->attrs, "job-originating-host-name",
IPP_TAG_ZERO);
#ifdef HAVE_VSYSLOG
/*
* See if we are logging pages via syslog...
*/
if (!strcmp(PageLog, "syslog"))
{
syslog(LOG_INFO, "PAGE %s %s %d %s %s %s", job->printer->name,
job->username ? job->username : "-",
job->id, page, billing ? billing->values[0].string.text : "-",
hostname->values[0].string.text);
return (1);
}
#endif /* HAVE_VSYSLOG */
/*
* Not using syslog; check the log file...
*/
if (!check_log_file(&PageFile, PageLog))
return (0);
/*
* Print a page log entry of the form:
*
* printer job-id user [DD/MON/YYYY:HH:MM:SS +TTTT] page num-copies \
* billing hostname
*/
cupsFilePrintf(PageFile, "%s %s %d %s %s %s %s\n", job->printer->name,
job->username ? job->username : "-",
job->id, cupsdGetDateTime(time(NULL)), page,
billing ? billing->values[0].string.text : "-",
hostname->values[0].string.text);
cupsFileFlush(PageFile);
return (1);
}
/*
* 'cupsdLogRequest()' - Log an HTTP request in Common Log Format.
*/
int /* O - 1 on success, 0 on error */
cupsdLogRequest(cupsd_client_t *con, /* I - Request to log */
http_status_t code) /* I - Response code */
{
static const char * const states[] = /* HTTP client states... */
{
"WAITING",
"OPTIONS",
"GET",
"GET",
"HEAD",
"POST",
"POST",
"POST",
"PUT",
"PUT",
"DELETE",
"TRACE",
"CLOSE",
"STATUS"
};
#ifdef HAVE_VSYSLOG
/*
* See if we are logging accesses via syslog...
*/
if (!strcmp(AccessLog, "syslog"))
{
syslog(LOG_INFO,
"REQUEST %s - %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT " %s %s\n",
con->http.hostname, con->username[0] != '\0' ? con->username : "-",
states[con->operation], con->uri,
con->http.version / 100, con->http.version % 100,
code, CUPS_LLCAST con->bytes,
con->request ?
ippOpString(con->request->request.op.operation_id) : "-",
con->response ?
ippErrorString(con->response->request.status.status_code) : "-");
return (1);
}
#endif /* HAVE_VSYSLOG */
/*
* Not using syslog; check the log file...
*/
if (!check_log_file(&AccessFile, AccessLog))
return (0);
/*
* Write a log of the request in "common log format"...
*/
cupsFilePrintf(AccessFile,
"%s - %s %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT " %s %s\n",
con->http.hostname, con->username[0] != '\0' ? con->username : "-",
cupsdGetDateTime(con->start), states[con->operation], con->uri,
con->http.version / 100, con->http.version % 100,
code, CUPS_LLCAST con->bytes,
con->request ?
ippOpString(con->request->request.op.operation_id) : "-",
con->response ?
ippErrorString(con->response->request.status.status_code) :
"-");
cupsFileFlush(AccessFile);
return (1);
}
/*
* 'check_log_file()' - Open/rotate a log file if it needs it.
*/
static int /* O - 1 if log file open */
check_log_file(cups_file_t **lf, /* IO - Log file */
const char *logname) /* I - Log filename */
{
char backname[1024], /* Backup log filename */
filename[1024], /* Formatted log filename */
*ptr; /* Pointer into filename */
const char *logptr; /* Pointer into log filename */
/*
* See if we have a log file to check...
*/
if (!lf || !logname || !logname[0])
return (1);
/*
* Format the filename as needed...
*/
if (!*lf ||
(strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize &&
MaxLogSize > 0))
{
/*
* Handle format strings...
*/
filename[sizeof(filename) - 1] = '\0';
if (logname[0] != '/')
{
strlcpy(filename, ServerRoot, sizeof(filename));
strlcat(filename, "/", sizeof(filename));
}
else
filename[0] = '\0';
for (logptr = logname, ptr = filename + strlen(filename);
*logptr && ptr < (filename + sizeof(filename) - 1);
logptr ++)
if (*logptr == '%')
{
/*
* Format spec...
*/
logptr ++;
if (*logptr == 's')
{
/*
* Insert the server name...
*/
strlcpy(ptr, ServerName, sizeof(filename) - (ptr - filename));
ptr += strlen(ptr);
}
else
{
/*
* Otherwise just insert the character...
*/
*ptr++ = *logptr;
}
}
else
*ptr++ = *logptr;
*ptr = '\0';
}
/*
* See if the log file is open...
*/
if (!*lf)
{
/*
* Nope, open the log file...
*/
if ((*lf = cupsFileOpen(filename, "a")) == NULL)
{
/*
* If the file is in CUPS_LOGDIR then try to create a missing directory...
*/
if (!strncmp(filename, CUPS_LOGDIR, strlen(CUPS_LOGDIR)))
{
cupsdCheckPermissions(CUPS_LOGDIR, NULL, 0755, RunUser, Group, 1, -1);
*lf = cupsFileOpen(filename, "a");
}
if (*lf == NULL)
{
syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename,
strerror(errno));
return (0);
}
}
if (strncmp(filename, "/dev/", 5))
{
/*
* Change ownership and permissions of non-device logs...
*/
fchown(cupsFileNumber(*lf), RunUser, Group);
fchmod(cupsFileNumber(*lf), LogFilePerm);
}
}
/*
* Do we need to rotate the log?
*/
if (strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize &&
MaxLogSize > 0)
{
/*
* Rotate log file...
*/
cupsFileClose(*lf);
strcpy(backname, filename);
strlcat(backname, ".O", sizeof(backname));
unlink(backname);
rename(filename, backname);
if ((*lf = cupsFileOpen(filename, "a")) == NULL)
{
syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename,
strerror(errno));
return (0);
}
/*
* Change ownership and permissions of non-device logs...
*/
fchown(cupsFileNumber(*lf), RunUser, Group);
fchmod(cupsFileNumber(*lf), LogFilePerm);
}
return (1);
}
/*
* End of "$Id: log.c 6875 2007-08-27 23:25:06Z mike $".
*/