/* * "$Id: log.c,v 1.1.1.4 2002/06/06 22:13:17 jlovell Exp $" * * Log file routines for the Common UNIX Printing System (CUPS). * * Copyright 1997-2002 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the * property of Easy Software Products 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 missing or damaged please contact Easy Software Products * at: * * Attn: CUPS Licensing Information * Easy Software Products * 44141 Airport View Drive, Suite 204 * Hollywood, Maryland 20636-3111 USA * * Voice: (301) 373-9603 * EMail: cups-info@cups.org * WWW: http://www.cups.org * * Contents: * * GetDateTime() - Returns a pointer to a date/time string. * LogMessage() - Log a message to the error log file. * LogPage() - Log a page to the page log file. * LogRequest() - 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 #ifdef HAVE_VSYSLOG # include #endif /* HAVE_VSYSLOG */ /* * Local functions... */ static int check_log_file(FILE **, const char *); /* * 'GetDateTime()' - Returns a pointer to a date/time string. */ char * /* O - Date/time string */ GetDateTime(time_t t) /* I - Time value */ { struct tm *date; /* Date/time value */ static char s[1024]; /* Date/time string */ static const char *months[12] =/* Months */ { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /* * 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); } /* * 'LogMessage()' - Log a message to the error log file. */ int /* O - 1 on success, 0 on error */ LogMessage(int level, /* I - Log level */ const char *message, /* I - printf-style message string */ ...) /* I - Additional args as needed */ { int len; /* Length of message */ char line[1024]; /* Line for output file */ va_list ap; /* Argument pointer */ static char levels[] = /* Log levels... */ { ' ', 'X', 'A', 'C', 'E', 'W', 'N', 'I', 'D', 'd' }; #ifdef HAVE_VSYSLOG static 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 */ /* * See if we want to log this message... */ if (level > LogLevel) return (1); #ifdef HAVE_VSYSLOG /* * See if we are logging errors via syslog... */ if (strcmp(ErrorLog, "syslog") == 0) { 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... */ fprintf(ErrorFile, "%c %s ", levels[level], GetDateTime(time(NULL))); /* * Then the log message... */ va_start(ap, message); len = vsnprintf(line, sizeof(line), message, ap); va_end(ap); /* * Then a newline... */ fputs(line, ErrorFile); if (len > 0 && line[len - 1] != '\n') putc('\n', ErrorFile); fflush(ErrorFile); return (1); } /* * 'LogPage()' - Log a page to the page log file. */ int /* O - 1 on success, 0 on error */ LogPage(job_t *job, /* I - Job being printed */ const char *page) /* I - Page being printed */ { ipp_attribute_t *billing; /* job-billing attribute */ billing = ippFindAttribute(job->attrs, "job-billing", IPP_TAG_ZERO); #ifdef HAVE_VSYSLOG /* * See if we are logging pages via syslog... */ if (strcmp(PageLog, "syslog") == 0) { syslog(LOG_INFO, "PAGE %s %s %d %s %s", job->printer->name, job->username, job->id, page, billing ? billing->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 */ fprintf(PageFile, "%s %s %d %s %s %s\n", job->printer->name, job->username, job->id, GetDateTime(time(NULL)), page, billing ? billing->values[0].string.text : ""); fflush(PageFile); return (1); } /* * 'LogRequest()' - Log an HTTP request in Common Log Format. */ int /* O - 1 on success, 0 on error */ LogRequest(client_t *con, /* I - Request to log */ http_status_t code) /* I - Response code */ { static const char *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") == 0) { syslog(LOG_INFO, "REQUEST %s - %s \"%s %s HTTP/%d.%d\" %d %d\n", con->http.hostname, con->username[0] != '\0' ? con->username : "-", states[con->operation], con->uri, con->http.version / 100, con->http.version % 100, code, con->bytes); 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"... */ fprintf(AccessFile, "%s - %s %s \"%s %s HTTP/%d.%d\" %d %d\n", con->http.hostname, con->username[0] != '\0' ? con->username : "-", GetDateTime(con->start), states[con->operation], con->uri, con->http.version / 100, con->http.version % 100, code, con->bytes); fflush(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(FILE **log, /* IO - Log file */ const char *logname) /* I - Log filename */ { char backname[1024], /* Backup log filename */ filename[1024], /* Formatted log filename */ *ptr; /* Pointer into filename */ /* * See if we have a log file to check... */ if (log == NULL || logname == NULL || !logname[0]) return (1); /* * Format the filename as needed... */ if (*log == NULL || (ftell(*log) > 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 (ptr = filename + strlen(filename); *logname && ptr < (filename + sizeof(filename) - 1); logname ++) if (*logname == '%') { /* * Format spec... */ logname ++; if (*logname == 's') { /* * Insert the server name... */ strlcpy(ptr, ServerName, sizeof(filename) - (ptr - filename)); ptr += strlen(ptr); } else { /* * Otherwise just insert the character... */ *ptr++ = *logname; } } else *ptr++ = *logname; *ptr = '\0'; } /* * See if the log file is open... */ if (*log == NULL) { /* * Nope, open the log file... */ if ((*log = fopen(filename, "a")) == NULL) return (0); fchown(fileno(*log), User, Group); fchmod(fileno(*log), LogFilePerm); } /* * Do we need to rotate the log? */ if (ftell(*log) > MaxLogSize && MaxLogSize > 0) { /* * Rotate log file... */ fclose(*log); strcpy(backname, filename); strlcat(backname, ".O", sizeof(backname)); unlink(backname); rename(filename, backname); if ((*log = fopen(filename, "a")) == NULL) return (0); fchown(fileno(*log), User, Group); fchmod(fileno(*log), LogFilePerm); } return (1); } /* * End of "$Id: log.c,v 1.1.1.4 2002/06/06 22:13:17 jlovell Exp $". */