]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/log.c
Merge changes from CUPS 1.4svn-r7582.
[thirdparty/cups.git] / scheduler / log.c
CommitLineData
ef416fc2 1/*
2e4ff8af 2 * "$Id: log.c 6875 2007-08-27 23:25:06Z mike $"
ef416fc2 3 *
4 * Log file routines for the Common UNIX Printing System (CUPS).
5 *
91c84a35 6 * Copyright 2007-2008 by Apple Inc.
b94498cf 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
ef416fc2 14 *
15 * Contents:
16 *
f7deaa1a 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.
ef416fc2 23 */
24
25/*
26 * Include necessary headers...
27 */
28
29#include "cupsd.h"
30#include <stdarg.h>
b423cd4c 31#include <syslog.h>
ef416fc2 32
33
34/*
35 * Local functions...
36 */
37
38static int check_log_file(cups_file_t **, const char *);
39
40
41/*
42 * 'cupsdGetDateTime()' - Returns a pointer to a date/time string.
43 */
44
45char * /* O - Date/time string */
46cupsdGetDateTime(time_t t) /* I - Time value */
47{
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 */
52 {
53 "Jan",
54 "Feb",
55 "Mar",
56 "Apr",
57 "May",
58 "Jun",
59 "Jul",
60 "Aug",
61 "Sep",
62 "Oct",
63 "Nov",
64 "Dec"
65 };
66
67
839a51c8
MS
68 /*
69 * Make sure we have a valid time...
70 */
71
72 if (!t)
73 t = time(NULL);
74
ef416fc2 75 if (t != last_time)
76 {
77 last_time = t;
78
79 /*
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!
84 *
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.
89 *
90 * (*BSD and Darwin store the timezone offset in the tm structure)
91 */
92
93 date = localtime(&t);
94
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,
98#ifdef HAVE_TM_GMTOFF
99 date->tm_gmtoff / 3600, (date->tm_gmtoff / 60) % 60);
100#else
101 timezone / 3600, (timezone / 60) % 60);
102#endif /* HAVE_TM_GMTOFF */
103 }
104
105 return (s);
106}
107
108
f7deaa1a 109#ifdef HAVE_GSSAPI
110/*
111 * 'cupsdLogGSSMessage()' - Log a GSSAPI error...
112 */
113
114int /* O - 1 on success, 0 on error */
115cupsdLogGSSMessage(
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 */
121{
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 */
130
131
132 msg_ctx = 0;
133 err_major_status = gss_display_status(&err_minor_status,
134 major_status,
135 GSS_C_GSS_CODE,
136 GSS_C_NO_OID,
137 &msg_ctx,
138 &major_status_string);
139
140 if (!GSS_ERROR(err_major_status))
141 err_major_status = gss_display_status(&err_minor_status,
142 minor_status,
143 GSS_C_MECH_CODE,
144 GSS_C_NULL_OID,
145 &msg_ctx,
146 &minor_status_string);
147
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);
153
154 return (ret);
155}
156#endif /* HAVE_GSSAPI */
157
158
ef416fc2 159/*
160 * 'cupsdLogMessage()' - Log a message to the error log file.
161 */
162
163int /* O - 1 on success, 0 on error */
164cupsdLogMessage(int level, /* I - Log level */
165 const char *message, /* I - printf-style message string */
166 ...) /* I - Additional args as needed */
167{
168 int len; /* Length of message */
169 va_list ap; /* Argument pointer */
170 static const char levels[] = /* Log levels... */
171 {
172 ' ',
173 'X',
174 'A',
175 'C',
176 'E',
177 'W',
178 'N',
179 'I',
180 'D',
181 'd'
182 };
183#ifdef HAVE_VSYSLOG
839a51c8 184 static const int syslevels[] = /* SYSLOG levels... */
ef416fc2 185 {
186 0,
187 LOG_EMERG,
188 LOG_ALERT,
189 LOG_CRIT,
190 LOG_ERR,
191 LOG_WARNING,
192 LOG_NOTICE,
193 LOG_INFO,
194 LOG_DEBUG,
195 LOG_DEBUG
196 };
197#endif /* HAVE_VSYSLOG */
839a51c8
MS
198 static int linesize = 0; /* Size of line for output file */
199 static char *line = NULL; /* Line for output file */
ef416fc2 200
201
202 /*
203 * See if we want to log this message...
204 */
205
2e4ff8af
MS
206 if (TestConfigFile)
207 {
208 if (level <= CUPSD_LOG_WARN)
209 {
210 va_start(ap, message);
211 vfprintf(stderr, message, ap);
212 putc('\n', stderr);
213 va_end(ap);
214 }
215
216 return (1);
217 }
218
ef416fc2 219 if (level > LogLevel || !ErrorLog)
220 return (1);
221
222#ifdef HAVE_VSYSLOG
223 /*
224 * See if we are logging errors via syslog...
225 */
226
227 if (!strcmp(ErrorLog, "syslog"))
228 {
229 va_start(ap, message);
230 vsyslog(syslevels[level], message, ap);
231 va_end(ap);
232
233 return (1);
234 }
235#endif /* HAVE_VSYSLOG */
236
237 /*
238 * Not using syslog; check the log file...
239 */
240
241 if (!check_log_file(&ErrorFile, ErrorLog))
242 return (0);
243
244 /*
245 * Print the log level and date/time...
246 */
247
248 cupsFilePrintf(ErrorFile, "%c %s ", levels[level], cupsdGetDateTime(time(NULL)));
249
250 /*
251 * Allocate the line buffer as needed...
252 */
253
254 if (!linesize)
255 {
256 linesize = 8192;
257 line = malloc(linesize);
258
259 if (!line)
260 {
261 cupsFilePrintf(ErrorFile,
262 "ERROR: Unable to allocate memory for line - %s\n",
263 strerror(errno));
264 cupsFileFlush(ErrorFile);
265
266 return (0);
267 }
268 }
269
270 /*
271 * Format the log message...
272 */
273
274 va_start(ap, message);
275 len = vsnprintf(line, linesize, message, ap);
276 va_end(ap);
277
278 /*
279 * Resize the buffer as needed...
280 */
281
282 if (len >= linesize)
283 {
91c84a35
MS
284 char *temp; /* Temporary string pointer */
285
286
ef416fc2 287 len ++;
288
289 if (len < 8192)
290 len = 8192;
291 else if (len > 65536)
292 len = 65536;
293
91c84a35 294 temp = realloc(line, len);
ef416fc2 295
91c84a35 296 if (temp)
ef416fc2 297 {
91c84a35
MS
298 line = temp;
299 linesize = len;
ef416fc2 300 }
301
302 va_start(ap, message);
303 len = vsnprintf(line, linesize, message, ap);
304 va_end(ap);
305 }
306
307 if (len >= linesize)
308 len = linesize - 1;
309
310 /*
311 * Then the log message...
312 */
313
314 cupsFilePuts(ErrorFile, line);
315
316 /*
317 * Then a newline...
318 */
319
320 if (len > 0 && line[len - 1] != '\n')
321 cupsFilePutChar(ErrorFile, '\n');
322
323 /*
324 * Flush the line to the file and return...
325 */
326
327 cupsFileFlush(ErrorFile);
328
329 return (1);
330}
331
332
333/*
334 * 'cupsdLogPage()' - Log a page to the page log file.
335 */
336
337int /* O - 1 on success, 0 on error */
338cupsdLogPage(cupsd_job_t *job, /* I - Job being printed */
339 const char *page) /* I - Page being printed */
340{
01ce6322
MS
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 */
ef416fc2 350
351
01ce6322
MS
352 /*
353 * Format the line going into the page log...
354 */
355
356 if (!PageLogFormat)
357 return (1);
358
359 number = 1;
360 strcpy(copies, "1");
361 sscanf(page, "%d%255s", &number, copies);
362
363 for (format = PageLogFormat, bufptr = buffer; *format; format ++)
364 {
365 if (*format == '%')
366 {
367 format ++;
368
369 switch (*format)
370 {
371 case '%' : /* Literal % */
372 if (bufptr < (buffer + sizeof(buffer) - 1))
373 *bufptr++ = '%';
374 break;
375
376 case 'p' : /* Printer name */
377 strlcpy(bufptr, job->printer->name,
378 sizeof(buffer) - (bufptr - buffer));
379 bufptr += strlen(bufptr);
380 break;
381
382 case 'j' : /* Job ID */
383 snprintf(bufptr, sizeof(buffer) - (bufptr - buffer), "%d", job->id);
384 bufptr += strlen(bufptr);
385 break;
386
387 case 'u' : /* Username */
388 strlcpy(bufptr, job->username ? job->username : "-",
389 sizeof(buffer) - (bufptr - buffer));
390 bufptr += strlen(bufptr);
391 break;
392
393 case 'T' : /* Date and time */
394 strlcpy(bufptr, cupsdGetDateTime(time(NULL)),
395 sizeof(buffer) - (bufptr - buffer));
396 bufptr += strlen(bufptr);
397 break;
398
399 case 'P' : /* Page number */
400 snprintf(bufptr, sizeof(buffer) - (bufptr - buffer), "%d", number);
401 bufptr += strlen(bufptr);
402 break;
403
404 case 'C' : /* Number of copies */
405 strlcpy(bufptr, copies, sizeof(buffer) - (bufptr - buffer));
406 bufptr += strlen(bufptr);
407 break;
408
409 case '{' : /* {attribute} */
410 if ((nameend = strchr(format, '}')) != NULL &&
411 (nameend - format - 2) < (sizeof(name) - 1))
412 {
413 /*
414 * Pull the name from inside the brackets...
415 */
416
417 memcpy(name, format + 1, nameend - format - 2);
418 name[nameend - format - 2] = '\0';
419
420 if ((attr = ippFindAttribute(job->attrs, name,
421 IPP_TAG_ZERO)) != NULL)
422 {
423 /*
424 * Add the attribute value...
425 */
426
427 format = nameend;
428
429 for (i = 0;
430 i < attr->num_values &&
431 bufptr < (buffer + sizeof(buffer) - 1);
432 i ++)
433 {
434 if (i)
435 *bufptr++ = ',';
436
437 switch (attr->value_tag)
438 {
439 case IPP_TAG_INTEGER :
440 case IPP_TAG_ENUM :
441 snprintf(bufptr, sizeof(buffer) - (bufptr - buffer),
442 "%d", attr->values[i].integer);
443 bufptr += strlen(bufptr);
444 break;
445
446 case IPP_TAG_BOOLEAN :
447 snprintf(bufptr, sizeof(buffer) - (bufptr - buffer),
448 "%d", attr->values[i].boolean);
449 bufptr += strlen(bufptr);
450 break;
451
452 case IPP_TAG_TEXTLANG :
453 case IPP_TAG_NAMELANG :
454 case IPP_TAG_TEXT :
455 case IPP_TAG_NAME :
456 case IPP_TAG_KEYWORD :
457 case IPP_TAG_URI :
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);
465 break;
466
467 default :
468 strlcpy(bufptr, "???",
469 sizeof(buffer) - (bufptr - buffer));
470 bufptr += strlen(bufptr);
471 break;
472 }
473 }
474 }
475 else if (bufptr < (buffer + sizeof(buffer) - 1))
476 *bufptr++ = '-';
477 break;
478 }
479
480 default :
481 if (bufptr < (buffer + sizeof(buffer) - 2))
482 {
483 *bufptr++ = '%';
484 *bufptr++ = *format;
485 }
486 break;
487 }
488 }
489 else if (bufptr < (buffer + sizeof(buffer) - 1))
490 *bufptr++ = *format;
491 }
ef416fc2 492
01ce6322
MS
493 *bufptr = '\0';
494
ef416fc2 495#ifdef HAVE_VSYSLOG
496 /*
497 * See if we are logging pages via syslog...
498 */
499
500 if (!strcmp(PageLog, "syslog"))
501 {
01ce6322 502 syslog(LOG_INFO, "%s", buffer);
ef416fc2 503
504 return (1);
505 }
506#endif /* HAVE_VSYSLOG */
507
508 /*
509 * Not using syslog; check the log file...
510 */
511
512 if (!check_log_file(&PageFile, PageLog))
513 return (0);
514
515 /*
516 * Print a page log entry of the form:
517 *
ac884b6a 518 * printer user job-id [DD/MON/YYYY:HH:MM:SS +TTTT] page num-copies \
ef416fc2 519 * billing hostname
520 */
521
01ce6322 522 cupsFilePrintf(PageFile, "%s\n", buffer);
ef416fc2 523 cupsFileFlush(PageFile);
524
525 return (1);
526}
527
528
529/*
530 * 'cupsdLogRequest()' - Log an HTTP request in Common Log Format.
531 */
532
533int /* O - 1 on success, 0 on error */
534cupsdLogRequest(cupsd_client_t *con, /* I - Request to log */
535 http_status_t code) /* I - Response code */
536{
839a51c8 537 char temp[2048]; /* Temporary string for URI */
ef416fc2 538 static const char * const states[] = /* HTTP client states... */
539 {
540 "WAITING",
541 "OPTIONS",
542 "GET",
543 "GET",
544 "HEAD",
545 "POST",
546 "POST",
547 "POST",
548 "PUT",
549 "PUT",
550 "DELETE",
551 "TRACE",
552 "CLOSE",
553 "STATUS"
554 };
555
556
557#ifdef HAVE_VSYSLOG
558 /*
559 * See if we are logging accesses via syslog...
560 */
561
562 if (!strcmp(AccessLog, "syslog"))
563 {
2abf387c 564 syslog(LOG_INFO,
565 "REQUEST %s - %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT " %s %s\n",
ef416fc2 566 con->http.hostname, con->username[0] != '\0' ? con->username : "-",
839a51c8 567 states[con->operation], _httpEncodeURI(temp, con->uri, sizeof(temp)),
ef416fc2 568 con->http.version / 100, con->http.version % 100,
2abf387c 569 code, CUPS_LLCAST con->bytes,
570 con->request ?
571 ippOpString(con->request->request.op.operation_id) : "-",
572 con->response ?
573 ippErrorString(con->response->request.status.status_code) : "-");
ef416fc2 574
575 return (1);
576 }
577#endif /* HAVE_VSYSLOG */
578
579 /*
580 * Not using syslog; check the log file...
581 */
582
583 if (!check_log_file(&AccessFile, AccessLog))
584 return (0);
585
586 /*
587 * Write a log of the request in "common log format"...
588 */
589
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 : "-",
839a51c8
MS
593 cupsdGetDateTime(con->start), states[con->operation],
594 _httpEncodeURI(temp, con->uri, sizeof(temp)),
ef416fc2 595 con->http.version / 100, con->http.version % 100,
596 code, CUPS_LLCAST con->bytes,
597 con->request ?
598 ippOpString(con->request->request.op.operation_id) : "-",
599 con->response ?
600 ippErrorString(con->response->request.status.status_code) :
601 "-");
602
603 cupsFileFlush(AccessFile);
604
605 return (1);
606}
607
608
609/*
610 * 'check_log_file()' - Open/rotate a log file if it needs it.
611 */
612
613static int /* O - 1 if log file open */
614check_log_file(cups_file_t **lf, /* IO - Log file */
615 const char *logname) /* I - Log filename */
616{
f7deaa1a 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 */
ef416fc2 621
622
623 /*
624 * See if we have a log file to check...
625 */
626
627 if (!lf || !logname || !logname[0])
628 return (1);
629
630 /*
631 * Format the filename as needed...
632 */
633
a74454a7 634 if (!*lf ||
635 (strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize &&
636 MaxLogSize > 0))
ef416fc2 637 {
638 /*
639 * Handle format strings...
640 */
641
642 filename[sizeof(filename) - 1] = '\0';
643
644 if (logname[0] != '/')
645 {
646 strlcpy(filename, ServerRoot, sizeof(filename));
647 strlcat(filename, "/", sizeof(filename));
648 }
649 else
650 filename[0] = '\0';
651
f7deaa1a 652 for (logptr = logname, ptr = filename + strlen(filename);
653 *logptr && ptr < (filename + sizeof(filename) - 1);
654 logptr ++)
655 if (*logptr == '%')
ef416fc2 656 {
657 /*
658 * Format spec...
659 */
660
f7deaa1a 661 logptr ++;
662 if (*logptr == 's')
ef416fc2 663 {
664 /*
665 * Insert the server name...
666 */
667
668 strlcpy(ptr, ServerName, sizeof(filename) - (ptr - filename));
669 ptr += strlen(ptr);
670 }
671 else
672 {
673 /*
674 * Otherwise just insert the character...
675 */
676
f7deaa1a 677 *ptr++ = *logptr;
ef416fc2 678 }
679 }
680 else
f7deaa1a 681 *ptr++ = *logptr;
ef416fc2 682
683 *ptr = '\0';
684 }
685
686 /*
687 * See if the log file is open...
688 */
689
690 if (!*lf)
691 {
692 /*
693 * Nope, open the log file...
694 */
695
696 if ((*lf = cupsFileOpen(filename, "a")) == NULL)
697 {
b94498cf 698 /*
699 * If the file is in CUPS_LOGDIR then try to create a missing directory...
700 */
ef416fc2 701
b94498cf 702 if (!strncmp(filename, CUPS_LOGDIR, strlen(CUPS_LOGDIR)))
703 {
704 cupsdCheckPermissions(CUPS_LOGDIR, NULL, 0755, RunUser, Group, 1, -1);
705
706 *lf = cupsFileOpen(filename, "a");
707 }
708
709 if (*lf == NULL)
710 {
711 syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename,
712 strerror(errno));
713 return (0);
714 }
ef416fc2 715 }
716
717 if (strncmp(filename, "/dev/", 5))
718 {
719 /*
720 * Change ownership and permissions of non-device logs...
721 */
722
723 fchown(cupsFileNumber(*lf), RunUser, Group);
724 fchmod(cupsFileNumber(*lf), LogFilePerm);
725 }
726 }
727
728 /*
729 * Do we need to rotate the log?
730 */
731
a74454a7 732 if (strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize &&
733 MaxLogSize > 0)
ef416fc2 734 {
735 /*
736 * Rotate log file...
737 */
738
739 cupsFileClose(*lf);
740
741 strcpy(backname, filename);
742 strlcat(backname, ".O", sizeof(backname));
743
744 unlink(backname);
745 rename(filename, backname);
746
747 if ((*lf = cupsFileOpen(filename, "a")) == NULL)
748 {
749 syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename,
750 strerror(errno));
751
752 return (0);
753 }
754
a74454a7 755 /*
756 * Change ownership and permissions of non-device logs...
757 */
ef416fc2 758
a74454a7 759 fchown(cupsFileNumber(*lf), RunUser, Group);
760 fchmod(cupsFileNumber(*lf), LogFilePerm);
ef416fc2 761 }
762
763 return (1);
764}
765
766
767/*
2e4ff8af 768 * End of "$Id: log.c 6875 2007-08-27 23:25:06Z mike $".
ef416fc2 769 */