2 * "$Id: log.c 7697 2008-06-27 15:56:00Z mike $"
4 * Log file routines for the Common UNIX Printing System (CUPS).
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
17 * cupsdGetDateTime() - Returns a pointer to a date/time string.
18 * cupsdLogGSSMessage() - Log a GSSAPI error...
19 * cupsdLogJob() - Log a job message.
20 * cupsdLogMessage() - Log a message to the error log file.
21 * cupsdLogPage() - Log a page to the page log file.
22 * cupsdLogRequest() - Log an HTTP request in Common Log Format.
23 * cupsdWriteErrorLog() - Write a line to the ErrorLog.
24 * check_log_file() - Open/rotate a log file if it needs it.
25 * format_log_line() - Format a line for a log file.
29 * Include necessary headers...
41 static int log_linesize
= 0; /* Size of line for output file */
42 static char *log_line
= NULL
; /* Line for output file */
49 static int check_log_file(cups_file_t
**lf
, const char *logname
);
50 static char *format_log_line(const char *message
, va_list ap
);
54 * 'cupsdGetDateTime()' - Returns a pointer to a date/time string.
57 char * /* O - Date/time string */
58 cupsdGetDateTime(time_t t
) /* I - Time value */
60 struct tm
*date
; /* Date/time value */
61 static time_t last_time
= -1; /* Last time value */
62 static char s
[1024]; /* Date/time string */
63 static const char * const months
[12] =/* Months */
81 * Make sure we have a valid time...
92 * Get the date and time from the UNIX time value, and then format it
93 * into a string. Note that we *can't* use the strftime() function since
94 * it is localized and will seriously confuse automatic programs if the
95 * month names are in the wrong language!
97 * Also, we use the "timezone" variable that contains the current timezone
98 * offset from GMT in seconds so that we are reporting local time in the
99 * log files. If you want GMT, set the TZ environment variable accordingly
100 * before starting the scheduler.
102 * (*BSD and Darwin store the timezone offset in the tm structure)
105 date
= localtime(&t
);
107 snprintf(s
, sizeof(s
), "[%02d/%s/%04d:%02d:%02d:%02d %+03ld%02ld]",
108 date
->tm_mday
, months
[date
->tm_mon
], 1900 + date
->tm_year
,
109 date
->tm_hour
, date
->tm_min
, date
->tm_sec
,
110 #ifdef HAVE_TM_GMTOFF
111 date
->tm_gmtoff
/ 3600, (date
->tm_gmtoff
/ 60) % 60);
113 timezone
/ 3600, (timezone
/ 60) % 60);
114 #endif /* HAVE_TM_GMTOFF */
123 * 'cupsdLogGSSMessage()' - Log a GSSAPI error...
126 int /* O - 1 on success, 0 on error */
128 int level
, /* I - Log level */
129 int major_status
, /* I - Major GSSAPI status */
130 int minor_status
, /* I - Minor GSSAPI status */
131 const char *message
, /* I - printf-style message string */
132 ...) /* I - Additional args as needed */
134 OM_uint32 err_major_status
, /* Major status code for display */
135 err_minor_status
; /* Minor status code for display */
136 OM_uint32 msg_ctx
; /* Message context */
137 gss_buffer_desc major_status_string
= GSS_C_EMPTY_BUFFER
,
138 /* Major status message */
139 minor_status_string
= GSS_C_EMPTY_BUFFER
;
140 /* Minor status message */
141 int ret
; /* Return value */
145 err_major_status
= gss_display_status(&err_minor_status
,
150 &major_status_string
);
152 if (!GSS_ERROR(err_major_status
))
153 err_major_status
= gss_display_status(&err_minor_status
,
158 &minor_status_string
);
160 ret
= cupsdLogMessage(level
, "%s: %s, %s", message
,
161 (char *)major_status_string
.value
,
162 (char *)minor_status_string
.value
);
163 gss_release_buffer(&err_minor_status
, &major_status_string
);
164 gss_release_buffer(&err_minor_status
, &minor_status_string
);
168 #endif /* HAVE_GSSAPI */
172 * 'cupsdLogJob()' - Log a job message.
175 int /* O - 1 on success, 0 on error */
176 cupsdLogJob(cupsd_job_t
*job
, /* I - Job */
177 int level
, /* I - Log level */
178 const char *message
, /* I - Printf-style message string */
179 ...) /* I - Additional arguments as needed */
181 va_list ap
; /* Argument pointer */
182 char jobmsg
[1024], /* Format string for job message */
183 *line
; /* Message line */
187 * See if we want to log this message...
190 if (TestConfigFile
|| level
> LogLevel
|| !ErrorLog
)
193 if (level
> LogLevel
|| !ErrorLog
)
197 * Format and write the log message...
200 snprintf(jobmsg
, sizeof(jobmsg
), "[Job %d] %s", job
->id
, message
);
202 va_start(ap
, message
);
203 line
= format_log_line(jobmsg
, ap
);
207 return (cupsdWriteErrorLog(level
, line
));
209 return (cupsdWriteErrorLog(CUPSD_LOG_ERROR
,
210 "Unable to allocate memory for log line!"));
215 * 'cupsdLogMessage()' - Log a message to the error log file.
218 int /* O - 1 on success, 0 on error */
219 cupsdLogMessage(int level
, /* I - Log level */
220 const char *message
, /* I - printf-style message string */
221 ...) /* I - Additional args as needed */
223 va_list ap
; /* Argument pointer */
224 char *line
; /* Message line */
228 * See if we want to log this message...
233 if (level
<= CUPSD_LOG_WARN
)
235 va_start(ap
, message
);
236 vfprintf(stderr
, message
, ap
);
244 if (level
> LogLevel
|| !ErrorLog
)
248 * Format and write the log message...
251 va_start(ap
, message
);
252 line
= format_log_line(message
, ap
);
256 return (cupsdWriteErrorLog(level
, line
));
258 return (cupsdWriteErrorLog(CUPSD_LOG_ERROR
,
259 "Unable to allocate memory for log line!"));
264 * 'cupsdLogPage()' - Log a page to the page log file.
267 int /* O - 1 on success, 0 on error */
268 cupsdLogPage(cupsd_job_t
*job
, /* I - Job being printed */
269 const char *page
) /* I - Page being printed */
271 int i
; /* Looping var */
272 char buffer
[2048], /* Buffer for page log */
273 *bufptr
, /* Pointer into buffer */
274 name
[256]; /* Attribute name */
275 const char *format
, /* Pointer into PageLogFormat */
276 *nameend
; /* End of attribute name */
277 ipp_attribute_t
*attr
; /* Current attribute */
278 int number
; /* Page number */
279 char copies
[256]; /* Number of copies */
283 * Format the line going into the page log...
291 sscanf(page
, "%d%255s", &number
, copies
);
293 for (format
= PageLogFormat
, bufptr
= buffer
; *format
; format
++)
301 case '%' : /* Literal % */
302 if (bufptr
< (buffer
+ sizeof(buffer
) - 1))
306 case 'p' : /* Printer name */
307 strlcpy(bufptr
, job
->printer
->name
,
308 sizeof(buffer
) - (bufptr
- buffer
));
309 bufptr
+= strlen(bufptr
);
312 case 'j' : /* Job ID */
313 snprintf(bufptr
, sizeof(buffer
) - (bufptr
- buffer
), "%d", job
->id
);
314 bufptr
+= strlen(bufptr
);
317 case 'u' : /* Username */
318 strlcpy(bufptr
, job
->username
? job
->username
: "-",
319 sizeof(buffer
) - (bufptr
- buffer
));
320 bufptr
+= strlen(bufptr
);
323 case 'T' : /* Date and time */
324 strlcpy(bufptr
, cupsdGetDateTime(time(NULL
)),
325 sizeof(buffer
) - (bufptr
- buffer
));
326 bufptr
+= strlen(bufptr
);
329 case 'P' : /* Page number */
330 snprintf(bufptr
, sizeof(buffer
) - (bufptr
- buffer
), "%d", number
);
331 bufptr
+= strlen(bufptr
);
334 case 'C' : /* Number of copies */
335 strlcpy(bufptr
, copies
, sizeof(buffer
) - (bufptr
- buffer
));
336 bufptr
+= strlen(bufptr
);
339 case '{' : /* {attribute} */
340 if ((nameend
= strchr(format
, '}')) != NULL
&&
341 (nameend
- format
- 2) < (sizeof(name
) - 1))
344 * Pull the name from inside the brackets...
347 memcpy(name
, format
+ 1, nameend
- format
- 1);
348 name
[nameend
- format
- 1] = '\0';
352 if ((attr
= ippFindAttribute(job
->attrs
, name
,
353 IPP_TAG_ZERO
)) != NULL
)
356 * Add the attribute value...
360 i
< attr
->num_values
&&
361 bufptr
< (buffer
+ sizeof(buffer
) - 1);
367 switch (attr
->value_tag
)
369 case IPP_TAG_INTEGER
:
371 snprintf(bufptr
, sizeof(buffer
) - (bufptr
- buffer
),
372 "%d", attr
->values
[i
].integer
);
373 bufptr
+= strlen(bufptr
);
376 case IPP_TAG_BOOLEAN
:
377 snprintf(bufptr
, sizeof(buffer
) - (bufptr
- buffer
),
378 "%d", attr
->values
[i
].boolean
);
379 bufptr
+= strlen(bufptr
);
382 case IPP_TAG_TEXTLANG
:
383 case IPP_TAG_NAMELANG
:
386 case IPP_TAG_KEYWORD
:
388 case IPP_TAG_URISCHEME
:
389 case IPP_TAG_CHARSET
:
390 case IPP_TAG_LANGUAGE
:
391 case IPP_TAG_MIMETYPE
:
392 strlcpy(bufptr
, attr
->values
[i
].string
.text
,
393 sizeof(buffer
) - (bufptr
- buffer
));
394 bufptr
+= strlen(bufptr
);
398 strlcpy(bufptr
, "???",
399 sizeof(buffer
) - (bufptr
- buffer
));
400 bufptr
+= strlen(bufptr
);
405 else if (bufptr
< (buffer
+ sizeof(buffer
) - 1))
411 if (bufptr
< (buffer
+ sizeof(buffer
) - 2))
419 else if (bufptr
< (buffer
+ sizeof(buffer
) - 1))
427 * See if we are logging pages via syslog...
430 if (!strcmp(PageLog
, "syslog"))
432 syslog(LOG_INFO
, "%s", buffer
);
436 #endif /* HAVE_VSYSLOG */
439 * Not using syslog; check the log file...
442 if (!check_log_file(&PageFile
, PageLog
))
446 * Print a page log entry of the form:
448 * printer user job-id [DD/MON/YYYY:HH:MM:SS +TTTT] page num-copies \
452 cupsFilePrintf(PageFile
, "%s\n", buffer
);
453 cupsFileFlush(PageFile
);
460 * 'cupsdLogRequest()' - Log an HTTP request in Common Log Format.
463 int /* O - 1 on success, 0 on error */
464 cupsdLogRequest(cupsd_client_t
*con
, /* I - Request to log */
465 http_status_t code
) /* I - Response code */
467 char temp
[2048]; /* Temporary string for URI */
468 static const char * const states
[] = /* HTTP client states... */
489 * See if we are logging accesses via syslog...
492 if (!strcmp(AccessLog
, "syslog"))
495 "REQUEST %s - %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT
" %s %s\n",
496 con
->http
.hostname
, con
->username
[0] != '\0' ? con
->username
: "-",
497 states
[con
->operation
], _httpEncodeURI(temp
, con
->uri
, sizeof(temp
)),
498 con
->http
.version
/ 100, con
->http
.version
% 100,
499 code
, CUPS_LLCAST con
->bytes
,
501 ippOpString(con
->request
->request
.op
.operation_id
) : "-",
503 ippErrorString(con
->response
->request
.status
.status_code
) : "-");
507 #endif /* HAVE_VSYSLOG */
510 * Not using syslog; check the log file...
513 if (!check_log_file(&AccessFile
, AccessLog
))
517 * Write a log of the request in "common log format"...
520 cupsFilePrintf(AccessFile
,
521 "%s - %s %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT
" %s %s\n",
522 con
->http
.hostname
, con
->username
[0] != '\0' ? con
->username
: "-",
523 cupsdGetDateTime(con
->start
), states
[con
->operation
],
524 _httpEncodeURI(temp
, con
->uri
, sizeof(temp
)),
525 con
->http
.version
/ 100, con
->http
.version
% 100,
526 code
, CUPS_LLCAST con
->bytes
,
528 ippOpString(con
->request
->request
.op
.operation_id
) : "-",
530 ippErrorString(con
->response
->request
.status
.status_code
) :
533 cupsFileFlush(AccessFile
);
540 * 'cupsdWriteErrorLog()' - Write a line to the ErrorLog.
543 int /* O - 1 on success, 0 on failure */
544 cupsdWriteErrorLog(int level
, /* I - Log level */
545 const char *message
) /* I - Message string */
547 static const char levels
[] = /* Log levels... */
561 static const int syslevels
[] = /* SYSLOG levels... */
574 #endif /* HAVE_VSYSLOG */
579 * See if we are logging errors via syslog...
582 if (!strcmp(ErrorLog
, "syslog"))
584 syslog(syslevels
[level
], "%s", message
);
587 #endif /* HAVE_VSYSLOG */
590 * Not using syslog; check the log file...
593 if (!check_log_file(&ErrorFile
, ErrorLog
))
597 * Write the log message...
600 cupsFilePrintf(ErrorFile
, "%c %s %s\n", levels
[level
],
601 cupsdGetDateTime(time(NULL
)), message
);
602 cupsFileFlush(ErrorFile
);
609 * 'check_log_file()' - Open/rotate a log file if it needs it.
612 static int /* O - 1 if log file open */
613 check_log_file(cups_file_t
**lf
, /* IO - Log file */
614 const char *logname
) /* I - Log filename */
616 char backname
[1024], /* Backup log filename */
617 filename
[1024], /* Formatted log filename */
618 *ptr
; /* Pointer into filename */
619 const char *logptr
; /* Pointer into log filename */
623 * See if we have a log file to check...
626 if (!lf
|| !logname
|| !logname
[0])
630 * Format the filename as needed...
634 (strncmp(logname
, "/dev/", 5) && cupsFileTell(*lf
) > MaxLogSize
&&
638 * Handle format strings...
641 filename
[sizeof(filename
) - 1] = '\0';
643 if (logname
[0] != '/')
645 strlcpy(filename
, ServerRoot
, sizeof(filename
));
646 strlcat(filename
, "/", sizeof(filename
));
651 for (logptr
= logname
, ptr
= filename
+ strlen(filename
);
652 *logptr
&& ptr
< (filename
+ sizeof(filename
) - 1);
664 * Insert the server name...
667 strlcpy(ptr
, ServerName
, sizeof(filename
) - (ptr
- filename
));
673 * Otherwise just insert the character...
686 * See if the log file is open...
692 * Nope, open the log file...
695 if ((*lf
= cupsFileOpen(filename
, "a")) == NULL
)
698 * If the file is in CUPS_LOGDIR then try to create a missing directory...
701 if (!strncmp(filename
, CUPS_LOGDIR
, strlen(CUPS_LOGDIR
)))
703 cupsdCheckPermissions(CUPS_LOGDIR
, NULL
, 0755, RunUser
, Group
, 1, -1);
705 *lf
= cupsFileOpen(filename
, "a");
710 syslog(LOG_ERR
, "Unable to open log file \"%s\" - %s", filename
,
716 if (strncmp(filename
, "/dev/", 5))
719 * Change ownership and permissions of non-device logs...
722 fchown(cupsFileNumber(*lf
), RunUser
, Group
);
723 fchmod(cupsFileNumber(*lf
), LogFilePerm
);
728 * Do we need to rotate the log?
731 if (strncmp(logname
, "/dev/", 5) && cupsFileTell(*lf
) > MaxLogSize
&&
740 strcpy(backname
, filename
);
741 strlcat(backname
, ".O", sizeof(backname
));
744 rename(filename
, backname
);
746 if ((*lf
= cupsFileOpen(filename
, "a")) == NULL
)
748 syslog(LOG_ERR
, "Unable to open log file \"%s\" - %s", filename
,
755 * Change ownership and permissions of non-device logs...
758 fchown(cupsFileNumber(*lf
), RunUser
, Group
);
759 fchmod(cupsFileNumber(*lf
), LogFilePerm
);
767 * 'format_log_line()' - Format a line for a log file.
769 * This function resizes a global string buffer as needed. Each call returns
770 * a pointer to this buffer, so the contents are only good until the next call
771 * to format_log_line()...
774 static char * /* O - Text or NULL on error */
775 format_log_line(const char *message
, /* I - Printf-style format string */
776 va_list ap
) /* I - Argument list */
778 int len
; /* Length of formatted line */
782 * Allocate the line buffer as needed...
788 log_line
= malloc(log_linesize
);
795 * Format the log message...
798 len
= vsnprintf(log_line
, log_linesize
, message
, ap
);
801 * Resize the buffer as needed...
804 if (len
>= log_linesize
)
806 char *temp
; /* Temporary string pointer */
813 else if (len
> 65536)
816 temp
= realloc(log_line
, len
);
824 len
= vsnprintf(log_line
, log_linesize
, message
, ap
);
832 * End of "$Id: log.c 7697 2008-06-27 15:56:00Z mike $".