2 * "$Id: log.c 6875 2007-08-27 23:25:06Z 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 * cupsdLogMessage() - Log a message to the error log file.
20 * cupsdLogPage() - Log a page to the page log file.
21 * cupsdLogRequest() - Log an HTTP request in Common Log Format.
22 * check_log_file() - Open/rotate a log file if it needs it.
26 * Include necessary headers...
38 static int check_log_file(cups_file_t
**, const char *);
42 * 'cupsdGetDateTime()' - Returns a pointer to a date/time string.
45 char * /* O - Date/time string */
46 cupsdGetDateTime(time_t t
) /* I - Time value */
48 struct tm
*date
; /* Date/time value */
49 static time_t last_time
= -1; /* Last time value */
50 static char s
[1024]; /* Date/time string */
51 static const char * const months
[12] =/* Months */
69 * Make sure we have a valid time...
80 * Get the date and time from the UNIX time value, and then format it
81 * into a string. Note that we *can't* use the strftime() function since
82 * it is localized and will seriously confuse automatic programs if the
83 * month names are in the wrong language!
85 * Also, we use the "timezone" variable that contains the current timezone
86 * offset from GMT in seconds so that we are reporting local time in the
87 * log files. If you want GMT, set the TZ environment variable accordingly
88 * before starting the scheduler.
90 * (*BSD and Darwin store the timezone offset in the tm structure)
95 snprintf(s
, sizeof(s
), "[%02d/%s/%04d:%02d:%02d:%02d %+03ld%02ld]",
96 date
->tm_mday
, months
[date
->tm_mon
], 1900 + date
->tm_year
,
97 date
->tm_hour
, date
->tm_min
, date
->tm_sec
,
99 date
->tm_gmtoff
/ 3600, (date
->tm_gmtoff
/ 60) % 60);
101 timezone
/ 3600, (timezone
/ 60) % 60);
102 #endif /* HAVE_TM_GMTOFF */
111 * 'cupsdLogGSSMessage()' - Log a GSSAPI error...
114 int /* O - 1 on success, 0 on error */
116 int level
, /* I - Log level */
117 int major_status
, /* I - Major GSSAPI status */
118 int minor_status
, /* I - Minor GSSAPI status */
119 const char *message
, /* I - printf-style message string */
120 ...) /* I - Additional args as needed */
122 OM_uint32 err_major_status
, /* Major status code for display */
123 err_minor_status
; /* Minor status code for display */
124 OM_uint32 msg_ctx
; /* Message context */
125 gss_buffer_desc major_status_string
= GSS_C_EMPTY_BUFFER
,
126 /* Major status message */
127 minor_status_string
= GSS_C_EMPTY_BUFFER
;
128 /* Minor status message */
129 int ret
; /* Return value */
133 err_major_status
= gss_display_status(&err_minor_status
,
138 &major_status_string
);
140 if (!GSS_ERROR(err_major_status
))
141 err_major_status
= gss_display_status(&err_minor_status
,
146 &minor_status_string
);
148 ret
= cupsdLogMessage(level
, "%s: %s, %s", message
,
149 (char *)major_status_string
.value
,
150 (char *)minor_status_string
.value
);
151 gss_release_buffer(&err_minor_status
, &major_status_string
);
152 gss_release_buffer(&err_minor_status
, &minor_status_string
);
156 #endif /* HAVE_GSSAPI */
160 * 'cupsdLogMessage()' - Log a message to the error log file.
163 int /* O - 1 on success, 0 on error */
164 cupsdLogMessage(int level
, /* I - Log level */
165 const char *message
, /* I - printf-style message string */
166 ...) /* I - Additional args as needed */
168 int len
; /* Length of message */
169 va_list ap
; /* Argument pointer */
170 static const char levels
[] = /* Log levels... */
184 static const int syslevels
[] = /* SYSLOG levels... */
197 #endif /* HAVE_VSYSLOG */
198 static int linesize
= 0; /* Size of line for output file */
199 static char *line
= NULL
; /* Line for output file */
203 * See if we want to log this message...
208 if (level
<= CUPSD_LOG_WARN
)
210 va_start(ap
, message
);
211 vfprintf(stderr
, message
, ap
);
219 if (level
> LogLevel
|| !ErrorLog
)
224 * See if we are logging errors via syslog...
227 if (!strcmp(ErrorLog
, "syslog"))
229 va_start(ap
, message
);
230 vsyslog(syslevels
[level
], message
, ap
);
235 #endif /* HAVE_VSYSLOG */
238 * Not using syslog; check the log file...
241 if (!check_log_file(&ErrorFile
, ErrorLog
))
245 * Print the log level and date/time...
248 cupsFilePrintf(ErrorFile
, "%c %s ", levels
[level
], cupsdGetDateTime(time(NULL
)));
251 * Allocate the line buffer as needed...
257 line
= malloc(linesize
);
261 cupsFilePrintf(ErrorFile
,
262 "ERROR: Unable to allocate memory for line - %s\n",
264 cupsFileFlush(ErrorFile
);
271 * Format the log message...
274 va_start(ap
, message
);
275 len
= vsnprintf(line
, linesize
, message
, ap
);
279 * Resize the buffer as needed...
284 char *temp
; /* Temporary string pointer */
291 else if (len
> 65536)
294 temp
= realloc(line
, len
);
302 va_start(ap
, message
);
303 len
= vsnprintf(line
, linesize
, message
, ap
);
311 * Then the log message...
314 cupsFilePuts(ErrorFile
, line
);
320 if (len
> 0 && line
[len
- 1] != '\n')
321 cupsFilePutChar(ErrorFile
, '\n');
324 * Flush the line to the file and return...
327 cupsFileFlush(ErrorFile
);
334 * 'cupsdLogPage()' - Log a page to the page log file.
337 int /* O - 1 on success, 0 on error */
338 cupsdLogPage(cupsd_job_t
*job
, /* I - Job being printed */
339 const char *page
) /* I - Page being printed */
341 int i
; /* Looping var */
342 char buffer
[2048], /* Buffer for page log */
343 *bufptr
, /* Pointer into buffer */
344 name
[256]; /* Attribute name */
345 const char *format
, /* Pointer into PageLogFormat */
346 *nameend
; /* End of attribute name */
347 ipp_attribute_t
*attr
; /* Current attribute */
348 int number
; /* Page number */
349 char copies
[256]; /* Number of copies */
353 * Format the line going into the page log...
361 sscanf(page
, "%d%255s", &number
, copies
);
363 for (format
= PageLogFormat
, bufptr
= buffer
; *format
; format
++)
371 case '%' : /* Literal % */
372 if (bufptr
< (buffer
+ sizeof(buffer
) - 1))
376 case 'p' : /* Printer name */
377 strlcpy(bufptr
, job
->printer
->name
,
378 sizeof(buffer
) - (bufptr
- buffer
));
379 bufptr
+= strlen(bufptr
);
382 case 'j' : /* Job ID */
383 snprintf(bufptr
, sizeof(buffer
) - (bufptr
- buffer
), "%d", job
->id
);
384 bufptr
+= strlen(bufptr
);
387 case 'u' : /* Username */
388 strlcpy(bufptr
, job
->username
? job
->username
: "-",
389 sizeof(buffer
) - (bufptr
- buffer
));
390 bufptr
+= strlen(bufptr
);
393 case 'T' : /* Date and time */
394 strlcpy(bufptr
, cupsdGetDateTime(time(NULL
)),
395 sizeof(buffer
) - (bufptr
- buffer
));
396 bufptr
+= strlen(bufptr
);
399 case 'P' : /* Page number */
400 snprintf(bufptr
, sizeof(buffer
) - (bufptr
- buffer
), "%d", number
);
401 bufptr
+= strlen(bufptr
);
404 case 'C' : /* Number of copies */
405 strlcpy(bufptr
, copies
, sizeof(buffer
) - (bufptr
- buffer
));
406 bufptr
+= strlen(bufptr
);
409 case '{' : /* {attribute} */
410 if ((nameend
= strchr(format
, '}')) != NULL
&&
411 (nameend
- format
- 2) < (sizeof(name
) - 1))
414 * Pull the name from inside the brackets...
417 memcpy(name
, format
+ 1, nameend
- format
- 2);
418 name
[nameend
- format
- 2] = '\0';
420 if ((attr
= ippFindAttribute(job
->attrs
, name
,
421 IPP_TAG_ZERO
)) != NULL
)
424 * Add the attribute value...
430 i
< attr
->num_values
&&
431 bufptr
< (buffer
+ sizeof(buffer
) - 1);
437 switch (attr
->value_tag
)
439 case IPP_TAG_INTEGER
:
441 snprintf(bufptr
, sizeof(buffer
) - (bufptr
- buffer
),
442 "%d", attr
->values
[i
].integer
);
443 bufptr
+= strlen(bufptr
);
446 case IPP_TAG_BOOLEAN
:
447 snprintf(bufptr
, sizeof(buffer
) - (bufptr
- buffer
),
448 "%d", attr
->values
[i
].boolean
);
449 bufptr
+= strlen(bufptr
);
452 case IPP_TAG_TEXTLANG
:
453 case IPP_TAG_NAMELANG
:
456 case IPP_TAG_KEYWORD
:
458 case IPP_TAG_URISCHEME
:
459 case IPP_TAG_CHARSET
:
460 case IPP_TAG_LANGUAGE
:
461 case IPP_TAG_MIMETYPE
:
462 strlcpy(bufptr
, attr
->values
[i
].string
.text
,
463 sizeof(buffer
) - (bufptr
- buffer
));
464 bufptr
+= strlen(bufptr
);
468 strlcpy(bufptr
, "???",
469 sizeof(buffer
) - (bufptr
- buffer
));
470 bufptr
+= strlen(bufptr
);
475 else if (bufptr
< (buffer
+ sizeof(buffer
) - 1))
481 if (bufptr
< (buffer
+ sizeof(buffer
) - 2))
489 else if (bufptr
< (buffer
+ sizeof(buffer
) - 1))
497 * See if we are logging pages via syslog...
500 if (!strcmp(PageLog
, "syslog"))
502 syslog(LOG_INFO
, "%s", buffer
);
506 #endif /* HAVE_VSYSLOG */
509 * Not using syslog; check the log file...
512 if (!check_log_file(&PageFile
, PageLog
))
516 * Print a page log entry of the form:
518 * printer user job-id [DD/MON/YYYY:HH:MM:SS +TTTT] page num-copies \
522 cupsFilePrintf(PageFile
, "%s\n", buffer
);
523 cupsFileFlush(PageFile
);
530 * 'cupsdLogRequest()' - Log an HTTP request in Common Log Format.
533 int /* O - 1 on success, 0 on error */
534 cupsdLogRequest(cupsd_client_t
*con
, /* I - Request to log */
535 http_status_t code
) /* I - Response code */
537 char temp
[2048]; /* Temporary string for URI */
538 static const char * const states
[] = /* HTTP client states... */
559 * See if we are logging accesses via syslog...
562 if (!strcmp(AccessLog
, "syslog"))
565 "REQUEST %s - %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT
" %s %s\n",
566 con
->http
.hostname
, con
->username
[0] != '\0' ? con
->username
: "-",
567 states
[con
->operation
], _httpEncodeURI(temp
, con
->uri
, sizeof(temp
)),
568 con
->http
.version
/ 100, con
->http
.version
% 100,
569 code
, CUPS_LLCAST con
->bytes
,
571 ippOpString(con
->request
->request
.op
.operation_id
) : "-",
573 ippErrorString(con
->response
->request
.status
.status_code
) : "-");
577 #endif /* HAVE_VSYSLOG */
580 * Not using syslog; check the log file...
583 if (!check_log_file(&AccessFile
, AccessLog
))
587 * Write a log of the request in "common log format"...
590 cupsFilePrintf(AccessFile
,
591 "%s - %s %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT
" %s %s\n",
592 con
->http
.hostname
, con
->username
[0] != '\0' ? con
->username
: "-",
593 cupsdGetDateTime(con
->start
), states
[con
->operation
],
594 _httpEncodeURI(temp
, con
->uri
, sizeof(temp
)),
595 con
->http
.version
/ 100, con
->http
.version
% 100,
596 code
, CUPS_LLCAST con
->bytes
,
598 ippOpString(con
->request
->request
.op
.operation_id
) : "-",
600 ippErrorString(con
->response
->request
.status
.status_code
) :
603 cupsFileFlush(AccessFile
);
610 * 'check_log_file()' - Open/rotate a log file if it needs it.
613 static int /* O - 1 if log file open */
614 check_log_file(cups_file_t
**lf
, /* IO - Log file */
615 const char *logname
) /* I - Log filename */
617 char backname
[1024], /* Backup log filename */
618 filename
[1024], /* Formatted log filename */
619 *ptr
; /* Pointer into filename */
620 const char *logptr
; /* Pointer into log filename */
624 * See if we have a log file to check...
627 if (!lf
|| !logname
|| !logname
[0])
631 * Format the filename as needed...
635 (strncmp(logname
, "/dev/", 5) && cupsFileTell(*lf
) > MaxLogSize
&&
639 * Handle format strings...
642 filename
[sizeof(filename
) - 1] = '\0';
644 if (logname
[0] != '/')
646 strlcpy(filename
, ServerRoot
, sizeof(filename
));
647 strlcat(filename
, "/", sizeof(filename
));
652 for (logptr
= logname
, ptr
= filename
+ strlen(filename
);
653 *logptr
&& ptr
< (filename
+ sizeof(filename
) - 1);
665 * Insert the server name...
668 strlcpy(ptr
, ServerName
, sizeof(filename
) - (ptr
- filename
));
674 * Otherwise just insert the character...
687 * See if the log file is open...
693 * Nope, open the log file...
696 if ((*lf
= cupsFileOpen(filename
, "a")) == NULL
)
699 * If the file is in CUPS_LOGDIR then try to create a missing directory...
702 if (!strncmp(filename
, CUPS_LOGDIR
, strlen(CUPS_LOGDIR
)))
704 cupsdCheckPermissions(CUPS_LOGDIR
, NULL
, 0755, RunUser
, Group
, 1, -1);
706 *lf
= cupsFileOpen(filename
, "a");
711 syslog(LOG_ERR
, "Unable to open log file \"%s\" - %s", filename
,
717 if (strncmp(filename
, "/dev/", 5))
720 * Change ownership and permissions of non-device logs...
723 fchown(cupsFileNumber(*lf
), RunUser
, Group
);
724 fchmod(cupsFileNumber(*lf
), LogFilePerm
);
729 * Do we need to rotate the log?
732 if (strncmp(logname
, "/dev/", 5) && cupsFileTell(*lf
) > MaxLogSize
&&
741 strcpy(backname
, filename
);
742 strlcat(backname
, ".O", sizeof(backname
));
745 rename(filename
, backname
);
747 if ((*lf
= cupsFileOpen(filename
, "a")) == NULL
)
749 syslog(LOG_ERR
, "Unable to open log file \"%s\" - %s", filename
,
756 * Change ownership and permissions of non-device logs...
759 fchown(cupsFileNumber(*lf
), RunUser
, Group
);
760 fchmod(cupsFileNumber(*lf
), LogFilePerm
);
768 * End of "$Id: log.c 6875 2007-08-27 23:25:06Z mike $".