2 * Log file routines for the CUPS scheduler.
4 * Copyright © 2020-2025 by OpenPrinting.
5 * Copyright © 2007-2018 by Apple Inc.
6 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
16 #elif defined(HAVE_SYSTEMD_SD_JOURNAL_H)
17 # define SD_JOURNAL_SUPPRESS_LOCATION
18 # include <systemd/sd-journal.h>
19 #endif /* HAVE_ASL_H */
22 # define va_copy(__list1, __list2) ((void)(__list1 = __list2))
27 * Constants for log keys from PWG 5110.3 (PWG Common Log Format)...
30 #define PWG_DeviceUUID "DUU"
32 #define PWG_LogNaturalLanguage "NL"
33 #define PWG_Status "S"
34 #define PWG_ServiceURI "URI"
35 #define PWG_UserHost "UH"
36 #define PWG_UserName "UN"
37 #define PWG_UserURI "UU"
38 #define PWG_ServiceIsAcceptingJobs "IAJ"
39 #define PWG_ServiceState "ST"
40 #define PWG_ServiceStateReasons "SR"
41 #define PWG_ServiceUUID "SUU"
42 #define PWG_JobID "JID"
43 #define PWG_JobUUID "JUU"
44 #define PWG_JobImagesCompleted "JIM"
45 #define PWG_JobImpressionsCompleted "JIC"
46 #define PWG_JobDestinationURI "JD"
47 #define PWG_JobState "JS"
48 #define PWG_JobStateReasons "JR"
49 #define PWG_JobAccountingID "JA"
50 #define PWG_JobAcountingUserName "JAUN"
51 #define PWG_JobAccountingUserURI "JAUU"
58 static cups_mutex_t log_mutex
= CUPS_MUTEX_INITIALIZER
;
59 /* Mutex for logging */
60 static size_t log_linesize
= 0; /* Size of line for output file */
61 static char *log_line
= NULL
; /* Line for output file */
64 static const int log_levels
[] = /* ASL levels... */
77 #elif defined(HAVE_VSYSLOG) || defined(HAVE_SYSTEMD_SD_JOURNAL_H)
78 static const int log_levels
[] = /* SYSLOG levels... */
91 #endif /* HAVE_ASL_H */
98 static int format_log_line(const char *message
, va_list ap
);
102 * 'cupsdCheckLogFile()' - Open/rotate a log file if it needs it.
105 int /* O - 1 if log file open */
106 cupsdCheckLogFile(cups_file_t
**lf
, /* IO - Log file */
107 const char *logname
) /* I - Log filename */
109 char backname
[1024], /* Backup log filename */
110 filename
[1024], /* Formatted log filename */
111 *ptr
; /* Pointer into filename */
112 const char *logptr
; /* Pointer into log filename */
116 * See if we have a log file to check...
119 if (!lf
|| !logname
|| !logname
[0])
123 * Handle logging to stderr...
126 if (!strcmp(logname
, "stderr"))
133 * Format the filename as needed...
137 (strncmp(logname
, "/dev/", 5) && cupsFileTell(*lf
) > MaxLogSize
&&
141 * Handle format strings...
144 filename
[sizeof(filename
) - 1] = '\0';
146 if (logname
[0] != '/')
148 cupsCopyString(filename
, ServerRoot
, sizeof(filename
));
149 cupsConcatString(filename
, "/", sizeof(filename
));
156 for (logptr
= logname
, ptr
= filename
+ strlen(filename
); *logptr
&& ptr
< (filename
+ sizeof(filename
) - 1); logptr
++)
168 * Insert the server name...
171 cupsCopyString(ptr
, ServerName
? ServerName
: "localhost", sizeof(filename
) - (size_t)(ptr
- filename
));
177 * Otherwise just insert the character...
193 * See if the log file is open...
199 * Nope, open the log file...
202 if ((*lf
= cupsFileOpen(filename
, "a")) == NULL
)
205 * If the file is in CUPS_LOGDIR then try to create a missing directory...
208 if (!strncmp(filename
, CUPS_LOGDIR
, strlen(CUPS_LOGDIR
)))
211 * Try updating the permissions of the containing log directory, using
212 * the log file permissions as a basis...
215 mode_t log_dir_perm
= (mode_t
)(0300 | LogFilePerm
);
216 /* LogFilePerm + owner write/search */
217 if (log_dir_perm
& 0040)
218 log_dir_perm
|= 0010; /* Add group search */
219 if (log_dir_perm
& 0004)
220 log_dir_perm
|= 0001; /* Add other search */
222 cupsdCheckPermissions(CUPS_LOGDIR
, NULL
, log_dir_perm
, RunUser
, Group
, 1, -1);
224 *lf
= cupsFileOpen(filename
, "a");
229 #ifdef HAVE_SYSTEMD_SD_JOURNAL_H
230 sd_journal_print(LOG_ERR
, "Unable to open log file \"%s\" - %s", filename
, strerror(errno
));
232 syslog(LOG_ERR
, "Unable to open log file \"%s\" - %s", filename
, strerror(errno
));
233 #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
235 if (FatalErrors
& CUPSD_FATAL_LOG
)
236 cupsdEndProcess(getpid(), 0);
242 if (strncmp(filename
, "/dev/", 5))
245 * Change ownership and permissions of non-device logs...
248 fchown(cupsFileNumber(*lf
), RunUser
, LogFileGroup
);
249 fchmod(cupsFileNumber(*lf
), LogFilePerm
);
254 * Do we need to rotate the log?
257 if (strncmp(logname
, "/dev/", 5) && cupsFileTell(*lf
) > MaxLogSize
&&
266 cupsCopyString(backname
, filename
, sizeof(backname
));
267 cupsConcatString(backname
, ".O", sizeof(backname
));
270 rename(filename
, backname
);
272 if ((*lf
= cupsFileOpen(filename
, "a")) == NULL
)
274 #ifdef HAVE_SYSTEMD_SD_JOURNAL_H
275 sd_journal_print(LOG_ERR
, "Unable to open log file \"%s\" - %s", filename
, strerror(errno
));
278 syslog(LOG_ERR
, "Unable to open log file \"%s\" - %s", filename
, strerror(errno
));
279 #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
281 if (FatalErrors
& CUPSD_FATAL_LOG
)
282 cupsdEndProcess(getpid(), 0);
288 * Change ownership and permissions of non-device logs...
291 fchown(cupsFileNumber(*lf
), RunUser
, LogFileGroup
);
292 fchmod(cupsFileNumber(*lf
), LogFilePerm
);
300 * 'cupsdGetDateTime()' - Returns a pointer to a date/time string.
303 char * /* O - Date/time string */
304 cupsdGetDateTime(struct timeval
*t
, /* I - Time value or NULL for current */
305 cupsd_time_t format
) /* I - Format to use */
307 struct timeval curtime
; /* Current time value */
308 struct tm date
; /* Date/time value */
309 static struct timeval last_time
= { 0, 0 };
310 /* Last time we formatted */
311 static char s
[1024]; /* Date/time string */
312 static const char * const months
[12] =/* Months */
330 * Make sure we have a valid time...
335 gettimeofday(&curtime
, NULL
);
339 if (t
->tv_sec
!= last_time
.tv_sec
||
340 (LogTimeFormat
== CUPSD_TIME_USECS
&& t
->tv_usec
!= last_time
.tv_usec
))
345 * Get the date and time from the UNIX time value, and then format it
346 * into a string. Note that we *can't* use the strftime() function since
347 * it is localized and will seriously confuse automatic programs if the
348 * month names are in the wrong language!
350 * Also, we use the "timezone" variable that contains the current timezone
351 * offset from GMT in seconds so that we are reporting local time in the
352 * log files. If you want GMT, set the TZ environment variable accordingly
353 * before starting the scheduler.
355 * (*BSD and Darwin store the timezone offset in the tm structure)
358 localtime_r(&(t
->tv_sec
), &date
);
360 #ifdef HAVE_TM_GMTOFF
361 if (format
== CUPSD_TIME_STANDARD
)
362 snprintf(s
, sizeof(s
), "[%02d/%s/%04d:%02d:%02d:%02d %+03ld%02ld]",
363 date
.tm_mday
, months
[date
.tm_mon
], 1900 + date
.tm_year
,
364 date
.tm_hour
, date
.tm_min
, date
.tm_sec
,
365 date
.tm_gmtoff
/ 3600, (date
.tm_gmtoff
/ 60) % 60);
367 snprintf(s
, sizeof(s
), "[%02d/%s/%04d:%02d:%02d:%02d.%06d %+03ld%02ld]",
368 date
.tm_mday
, months
[date
.tm_mon
], 1900 + date
.tm_year
,
369 date
.tm_hour
, date
.tm_min
, date
.tm_sec
, (int)t
->tv_usec
,
370 date
.tm_gmtoff
/ 3600, (date
.tm_gmtoff
/ 60) % 60);
373 if (format
== CUPSD_TIME_STANDARD
)
374 snprintf(s
, sizeof(s
), "[%02d/%s/%04d:%02d:%02d:%02d %+03ld%02ld]",
375 date
.tm_mday
, months
[date
.tm_mon
], 1900 + date
.tm_year
,
376 date
.tm_hour
, date
.tm_min
, date
.tm_sec
,
377 timezone
/ 3600, (timezone
/ 60) % 60);
379 snprintf(s
, sizeof(s
), "[%02d/%s/%04d:%02d:%02d:%02d.%06d %+03ld%02ld]",
380 date
.tm_mday
, months
[date
.tm_mon
], 1900 + date
.tm_year
,
381 date
.tm_hour
, date
.tm_min
, date
.tm_sec
, (int)t
->tv_usec
,
382 timezone
/ 3600, (timezone
/ 60) % 60);
383 #endif /* HAVE_TM_GMTOFF */
391 * 'cupsdLogFCMessage()' - Log a file checking message.
396 void *context
, /* I - Printer (if any) */
397 _cups_fc_result_t result
, /* I - Check result */
398 const char *message
) /* I - Message to log */
400 cupsd_printer_t
*p
= (cupsd_printer_t
*)context
;
402 cupsd_loglevel_t level
; /* Log level */
405 if (result
== _CUPS_FILE_CHECK_OK
)
406 level
= CUPSD_LOG_DEBUG2
;
408 level
= CUPSD_LOG_ERROR
;
412 cupsdLogMessage(level
, "%s: %s", p
->name
, message
);
414 if (result
== _CUPS_FILE_CHECK_MISSING
||
415 result
== _CUPS_FILE_CHECK_WRONG_TYPE
)
417 cupsCopyString(p
->state_message
, message
, sizeof(p
->state_message
));
419 if (cupsdSetPrinterReasons(p
, "+cups-missing-filter-warning"))
420 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE
, p
, NULL
, "%s", message
);
422 else if (result
== _CUPS_FILE_CHECK_PERMISSIONS
||
423 result
== _CUPS_FILE_CHECK_RELATIVE_PATH
)
425 cupsCopyString(p
->state_message
, message
, sizeof(p
->state_message
));
427 if (cupsdSetPrinterReasons(p
, "+cups-insecure-filter-warning"))
428 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE
, p
, NULL
, "%s", message
);
432 cupsdLogMessage(level
, "%s", message
);
438 * 'cupsdLogGSSMessage()' - Log a GSSAPI error...
441 int /* O - 1 on success, 0 on error */
443 int level
, /* I - Log level */
444 OM_uint32 major_status
, /* I - Major GSSAPI status */
445 OM_uint32 minor_status
, /* I - Minor GSSAPI status */
446 const char *message
, /* I - printf-style message string */
447 ...) /* I - Additional args as needed */
449 OM_uint32 err_major_status
, /* Major status code for display */
450 err_minor_status
; /* Minor status code for display */
451 OM_uint32 msg_ctx
; /* Message context */
452 gss_buffer_desc major_status_string
= GSS_C_EMPTY_BUFFER
,
453 /* Major status message */
454 minor_status_string
= GSS_C_EMPTY_BUFFER
;
455 /* Minor status message */
456 int ret
; /* Return value */
457 char buffer
[8192]; /* Buffer for vsnprintf */
460 if (strchr(message
, '%'))
463 * Format the message string...
466 va_list ap
; /* Pointer to arguments */
468 va_start(ap
, message
);
469 vsnprintf(buffer
, sizeof(buffer
), message
, ap
);
476 err_major_status
= gss_display_status(&err_minor_status
,
481 &major_status_string
);
483 if (!GSS_ERROR(err_major_status
))
484 gss_display_status(&err_minor_status
, minor_status
, GSS_C_MECH_CODE
,
485 GSS_C_NULL_OID
, &msg_ctx
, &minor_status_string
);
487 ret
= cupsdLogMessage(level
, "%s: %s, %s", message
,
488 (char *)major_status_string
.value
,
489 (char *)minor_status_string
.value
);
490 gss_release_buffer(&err_minor_status
, &major_status_string
);
491 gss_release_buffer(&err_minor_status
, &minor_status_string
);
495 #endif /* HAVE_GSSAPI */
499 * 'cupsdLogClient()' - Log a client message.
502 int /* O - 1 on success, 0 on error */
503 cupsdLogClient(cupsd_client_t
*con
, /* I - Client connection */
504 int level
, /* I - Log level */
505 const char *message
, /* I - Printf-style message string */
506 ...) /* I - Additional arguments as needed */
508 va_list ap
, ap2
; /* Argument pointers */
509 char clientmsg
[1024];/* Format string for client message */
510 int status
; /* Formatting status */
514 * See if we want to log this message...
517 if (TestConfigFile
|| !ErrorLog
)
520 if (level
> LogLevel
)
524 * Format and write the log message...
528 snprintf(clientmsg
, sizeof(clientmsg
), "[Client %d] %s", con
->number
,
531 cupsCopyString(clientmsg
, message
, sizeof(clientmsg
));
533 va_start(ap
, message
);
538 status
= format_log_line(clientmsg
, ap2
);
546 return (cupsdWriteErrorLog(level
, log_line
));
548 return (cupsdWriteErrorLog(CUPSD_LOG_ERROR
,
549 "Unable to allocate memory for log line."));
554 * 'cupsdLogJob()' - Log a job message.
557 int /* O - 1 on success, 0 on error */
558 cupsdLogJob(cupsd_job_t
*job
, /* I - Job */
559 int level
, /* I - Log level */
560 const char *message
, /* I - Printf-style message string */
561 ...) /* I - Additional arguments as needed */
563 va_list ap
, ap2
; /* Argument pointers */
564 char jobmsg
[1024]; /* Format string for job message */
565 int status
; /* Formatting status */
569 * See if we want to log this message...
572 if (TestConfigFile
|| !ErrorLog
)
575 if (level
> LogLevel
&& LogDebugHistory
<= 0)
579 * Format and write the log message...
583 snprintf(jobmsg
, sizeof(jobmsg
), "[Job %d] %s", job
->id
, message
);
585 cupsCopyString(jobmsg
, message
, sizeof(jobmsg
));
587 va_start(ap
, message
);
592 status
= format_log_line(jobmsg
, ap2
);
601 if (job
&& level
> LogLevel
&& LogDebugHistory
> 0)
604 * Add message to the job history...
607 cupsd_joblog_t
*temp
; /* Copy of log message */
608 size_t log_len
= strlen(log_line
);
609 /* Length of log message */
611 if ((temp
= malloc(sizeof(cupsd_joblog_t
) + log_len
)) != NULL
)
613 temp
->time
= time(NULL
);
614 memcpy(temp
->message
, log_line
, log_len
+ 1);
618 job
->history
= cupsArrayNew(NULL
, NULL
);
620 if (job
->history
&& temp
)
622 cupsArrayAdd(job
->history
, temp
);
624 if (cupsArrayCount(job
->history
) > LogDebugHistory
)
627 * Remove excess messages...
630 temp
= cupsArrayFirst(job
->history
);
631 cupsArrayRemove(job
->history
, temp
);
640 else if (level
<= LogLevel
)
642 #ifdef HAVE_SYSTEMD_SD_JOURNAL_H
643 if (!strcmp(ErrorLog
, "syslog"))
645 cupsd_printer_t
*printer
= job
? (job
->printer
? job
->printer
: (job
->dest
? cupsdFindDest(job
->dest
) : NULL
)) : NULL
;
646 static const char * const job_states
[] =
647 { /* job-state strings */
658 sd_journal_send("MESSAGE=%s", log_line
,
659 "PRIORITY=%i", log_levels
[level
],
660 PWG_Event
"=JobStateChanged",
661 PWG_ServiceURI
"=%s", printer
? printer
->uri
: "",
662 PWG_JobID
"=%d", job
->id
,
663 PWG_JobState
"=%s", job
->state_value
< IPP_JSTATE_PENDING
? "" : job_states
[job
->state_value
- IPP_JSTATE_PENDING
],
664 PWG_JobImpressionsCompleted
"=%d", ippGetInteger(job
->impressions
, 0),
667 sd_journal_send("MESSAGE=%s", log_line
,
668 "PRIORITY=%i", log_levels
[level
],
674 #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
676 return (cupsdWriteErrorLog(level
, log_line
));
682 return (cupsdWriteErrorLog(CUPSD_LOG_ERROR
,
683 "Unable to allocate memory for log line."));
688 * 'cupsdLogMessage()' - Log a message to the error log file.
691 int /* O - 1 on success, 0 on error */
692 cupsdLogMessage(int level
, /* I - Log level */
693 const char *message
, /* I - printf-style message string */
694 ...) /* I - Additional args as needed */
696 va_list ap
, ap2
; /* Argument pointers */
697 int status
; /* Formatting status */
701 * See if we want to log this message...
704 if (TestConfigFile
&& level
<= CUPSD_LOG_WARN
)
706 va_start(ap
, message
);
708 vfprintf(stderr
, message
, ap
);
715 else if (!ErrorLog
&& level
<= CUPSD_LOG_WARN
)
717 va_start(ap
, message
);
719 #ifdef HAVE_SYSTEMD_SD_JOURNAL_H
720 sd_journal_printv(log_levels
[level
], message
, ap
);
722 #elif defined(HAVE_VSYSLOG)
723 vsyslog(LOG_LPR
| log_levels
[level
], message
, ap
);
726 vfprintf(stderr
, message
, ap
);
728 #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
734 else if (level
> LogLevel
|| !ErrorLog
)
737 #ifdef HAVE_SYSTEMD_SD_JOURNAL_H
738 else if (!strcmp(ErrorLog
, "syslog"))
740 va_start(ap
, message
);
741 sd_journal_printv(log_levels
[level
], message
, ap
);
745 #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
748 * Format and write the log message...
751 va_start(ap
, message
);
756 status
= format_log_line(message
, ap2
);
764 return (cupsdWriteErrorLog(level
, log_line
));
766 return (cupsdWriteErrorLog(CUPSD_LOG_ERROR
,
767 "Unable to allocate memory for log line!"));
772 * 'cupsdLogPage()' - Log a page to the page log file.
775 int /* O - 1 on success, 0 on error */
776 cupsdLogPage(cupsd_job_t
*job
, /* I - Job being printed */
777 const char *page
) /* I - Page being printed */
779 int i
; /* Looping var */
780 char buffer
[2048], /* Buffer for page log */
781 *bufptr
, /* Pointer into buffer */
782 name
[256]; /* Attribute name */
783 const char *format
, /* Pointer into PageLogFormat */
784 *nameend
; /* End of attribute name */
785 ipp_attribute_t
*attr
; /* Current attribute */
786 char number
[256]; /* Page number */
787 int copies
; /* Number of copies */
791 * Format the line going into the page log...
797 cupsCopyString(number
, "1", sizeof(number
));
799 sscanf(page
, "%255s%d", number
, &copies
);
801 for (format
= PageLogFormat
, bufptr
= buffer
; *format
; format
++)
809 case '%' : /* Literal % */
810 if (bufptr
< (buffer
+ sizeof(buffer
) - 1))
814 case 'p' : /* Printer name */
815 cupsCopyString(bufptr
, job
->dest
, sizeof(buffer
) - (size_t)(bufptr
- buffer
));
816 bufptr
+= strlen(bufptr
);
819 case 'j' : /* Job ID */
820 snprintf(bufptr
, sizeof(buffer
) - (size_t)(bufptr
- buffer
), "%d", job
->id
);
821 bufptr
+= strlen(bufptr
);
824 case 'u' : /* Username */
825 cupsCopyString(bufptr
, job
->username
? job
->username
: "-", sizeof(buffer
) - (size_t)(bufptr
- buffer
));
826 bufptr
+= strlen(bufptr
);
829 case 'T' : /* Date and time */
830 cupsCopyString(bufptr
, cupsdGetDateTime(NULL
, LogTimeFormat
), sizeof(buffer
) - (size_t)(bufptr
- buffer
));
831 bufptr
+= strlen(bufptr
);
834 case 'P' : /* Page number */
835 cupsCopyString(bufptr
, number
, sizeof(buffer
) - (size_t)(bufptr
- buffer
));
836 bufptr
+= strlen(bufptr
);
839 case 'C' : /* Number of copies */
840 snprintf(bufptr
, sizeof(buffer
) - (size_t)(bufptr
- buffer
), "%d", copies
);
841 bufptr
+= strlen(bufptr
);
844 case '{' : /* {attribute} */
845 if ((nameend
= strchr(format
, '}')) != NULL
&& (size_t)(nameend
- format
- 2) < (sizeof(name
) - 1))
848 * Pull the name from inside the brackets...
851 memcpy(name
, format
+ 1, (size_t)(nameend
- format
- 1));
852 name
[nameend
- format
- 1] = '\0';
856 attr
= ippFindAttribute(job
->attrs
, name
, IPP_TAG_ZERO
);
857 if (!attr
&& !strcmp(name
, "job-billing"))
860 * Handle alias "job-account-id" (which was standardized after
861 * "job-billing" was defined for CUPS...
864 attr
= ippFindAttribute(job
->attrs
, "job-account-id", IPP_TAG_ZERO
);
866 else if (!attr
&& !strcmp(name
, "media"))
869 * Handle alias "media-col" which uses dimensions instead of
873 attr
= ippFindAttribute(job
->attrs
, "media-col/media-size", IPP_TAG_BEGIN_COLLECTION
);
879 * Add the attribute value...
883 i
< attr
->num_values
&&
884 bufptr
< (buffer
+ sizeof(buffer
) - 1);
890 switch (attr
->value_tag
)
892 case IPP_TAG_INTEGER
:
894 snprintf(bufptr
, sizeof(buffer
) - (size_t)(bufptr
- buffer
), "%d", attr
->values
[i
].integer
);
895 bufptr
+= strlen(bufptr
);
898 case IPP_TAG_BOOLEAN
:
899 snprintf(bufptr
, sizeof(buffer
) - (size_t)(bufptr
- buffer
), "%d", attr
->values
[i
].boolean
);
900 bufptr
+= strlen(bufptr
);
903 case IPP_TAG_TEXTLANG
:
904 case IPP_TAG_NAMELANG
:
907 case IPP_TAG_KEYWORD
:
909 case IPP_TAG_URISCHEME
:
910 case IPP_TAG_CHARSET
:
911 case IPP_TAG_LANGUAGE
:
912 case IPP_TAG_MIMETYPE
:
913 cupsCopyString(bufptr
, attr
->values
[i
].string
.text
, sizeof(buffer
) - (size_t)(bufptr
- buffer
));
914 bufptr
+= strlen(bufptr
);
917 case IPP_TAG_BEGIN_COLLECTION
:
918 if (!strcmp(attr
->name
, "media-size"))
920 ipp_attribute_t
*x_dimension
= ippFindAttribute(ippGetCollection(attr
, 0), "x-dimension", IPP_TAG_INTEGER
);
921 ipp_attribute_t
*y_dimension
= ippFindAttribute(ippGetCollection(attr
, 0), "y-dimension", IPP_TAG_INTEGER
);
922 /* Media dimensions */
924 if (x_dimension
&& y_dimension
)
926 pwg_media_t
*pwg
= pwgMediaForSize(ippGetInteger(x_dimension
, 0), ippGetInteger(y_dimension
, 0));
930 cupsCopyString(bufptr
, pwg
->pwg
, sizeof(buffer
) - (size_t)(bufptr
- buffer
));
937 cupsCopyString(bufptr
, "???", sizeof(buffer
) - (size_t)(bufptr
- buffer
));
938 bufptr
+= strlen(bufptr
);
943 else if (bufptr
< (buffer
+ sizeof(buffer
) - 1))
949 if (bufptr
< (buffer
+ sizeof(buffer
) - 2))
957 else if (bufptr
< (buffer
+ sizeof(buffer
) - 1))
963 #ifdef HAVE_SYSTEMD_SD_JOURNAL_H
964 if (!strcmp(PageLog
, "syslog"))
966 static const char * const job_states
[] =
967 { /* job-state strings */
977 sd_journal_send("MESSAGE=%s", buffer
,
978 "PRIORITY=%i", LOG_INFO
,
979 PWG_Event
"=JobStateChanged",
980 PWG_ServiceURI
"=%s", job
->printer
->uri
,
981 PWG_JobID
"=%d", job
->id
,
982 PWG_JobState
"=%s", job_states
[job
->state_value
- IPP_JSTATE_PENDING
],
983 PWG_JobImpressionsCompleted
"=%d", ippGetInteger(job
->impressions
, 0),
988 #elif defined(HAVE_VSYSLOG)
990 * See if we are logging pages via syslog...
993 if (!strcmp(PageLog
, "syslog"))
995 syslog(LOG_INFO
, "%s", buffer
);
999 #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
1002 * Not using syslog; check the log file...
1005 if (!cupsdCheckLogFile(&PageFile
, PageLog
))
1009 * Print a page log entry of the form:
1011 * printer user job-id [DD/MON/YYYY:HH:MM:SS +TTTT] page num-copies \
1015 cupsFilePrintf(PageFile
, "%s\n", buffer
);
1016 cupsFileFlush(PageFile
);
1023 * 'cupsdLogPrinter()' - Log a printer message.
1026 int /* O - 1 on success, 0 on error */
1028 cupsd_printer_t
*p
, /* I - Printer */
1029 int level
, /* I - Log level */
1030 const char *message
, /* I - Printf-style message string */
1031 ...) /* I - Additional arguments as needed */
1033 va_list ap
, ap2
; /* Argument pointers */
1034 char pmsg
[1024]; /* Format string for printer message */
1035 int status
; /* Formatting status */
1039 * See if we want to log this message...
1042 if (TestConfigFile
|| !ErrorLog
)
1045 if (level
> LogLevel
)
1049 * Format and write the log message...
1053 snprintf(pmsg
, sizeof(pmsg
), "[Printer %s] %s", p
->name
, message
);
1055 cupsCopyString(pmsg
, message
, sizeof(pmsg
));
1057 va_start(ap
, message
);
1062 status
= format_log_line(pmsg
, ap2
);
1065 while (status
== 0);
1071 #ifdef HAVE_SYSTEMD_SD_JOURNAL_H
1072 if (!strcmp(ErrorLog
, "syslog"))
1074 static const char * const printer_states
[] =
1075 { /* printer-state strings */
1082 sd_journal_send("MESSAGE=%s", log_line
,
1083 "PRIORITY=%i", log_levels
[level
],
1084 PWG_Event
"=PrinterStateChanged",
1085 PWG_ServiceURI
"=%s", p
? p
->uri
: "",
1086 PWG_ServiceState
"=%s", printer_states
[p
->state
- IPP_PSTATE_IDLE
],
1089 sd_journal_send("MESSAGE=%s", log_line
,
1090 "PRIORITY=%i", log_levels
[level
],
1096 #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
1098 return (cupsdWriteErrorLog(level
, log_line
));
1101 return (cupsdWriteErrorLog(CUPSD_LOG_ERROR
, "Unable to allocate memory for log line."));
1106 * 'cupsdLogRequest()' - Log an HTTP request in Common Log Format.
1109 int /* O - 1 on success, 0 on error */
1110 cupsdLogRequest(cupsd_client_t
*con
, /* I - Request to log */
1111 http_status_t code
) /* I - Response code */
1113 char temp
[2048]; /* Temporary string for URI */
1114 static const char * const states
[] = /* HTTP client states... */
1134 * Filter requests as needed...
1137 if (AccessLogLevel
== CUPSD_ACCESSLOG_NONE
|| !AccessLog
)
1139 else if (AccessLogLevel
< CUPSD_ACCESSLOG_ALL
)
1142 * Eliminate simple GET, POST, and PUT requests...
1145 if ((con
->operation
== HTTP_STATE_GET
&&
1146 strncmp(con
->uri
, "/admin/conf", 11) &&
1147 strncmp(con
->uri
, "/admin/log", 10)) ||
1148 (con
->operation
== HTTP_STATE_POST
&& !con
->request
&&
1149 strncmp(con
->uri
, "/admin", 6)) ||
1150 (con
->operation
!= HTTP_STATE_GET
&& con
->operation
!= HTTP_STATE_POST
&&
1151 con
->operation
!= HTTP_STATE_PUT
))
1154 if (con
->request
&& con
->response
&&
1155 (con
->response
->request
.status
.status_code
< IPP_STATUS_REDIRECTION_OTHER_SITE
||
1156 con
->response
->request
.status
.status_code
== IPP_STATUS_ERROR_NOT_FOUND
))
1159 * Check successful requests...
1162 ipp_op_t op
= con
->request
->request
.op
.operation_id
;
1163 static cupsd_accesslog_t standard_ops
[] =
1165 CUPSD_ACCESSLOG_ALL
, /* reserved */
1166 CUPSD_ACCESSLOG_ALL
, /* reserved */
1167 CUPSD_ACCESSLOG_ACTIONS
,/* Print-Job */
1168 CUPSD_ACCESSLOG_ACTIONS
,/* Print-URI */
1169 CUPSD_ACCESSLOG_ACTIONS
,/* Validate-Job */
1170 CUPSD_ACCESSLOG_ACTIONS
,/* Create-Job */
1171 CUPSD_ACCESSLOG_ACTIONS
,/* Send-Document */
1172 CUPSD_ACCESSLOG_ACTIONS
,/* Send-URI */
1173 CUPSD_ACCESSLOG_ACTIONS
,/* Cancel-Job */
1174 CUPSD_ACCESSLOG_ALL
, /* Get-Job-Attributes */
1175 CUPSD_ACCESSLOG_ALL
, /* Get-Jobs */
1176 CUPSD_ACCESSLOG_ALL
, /* Get-Printer-Attributes */
1177 CUPSD_ACCESSLOG_ACTIONS
,/* Hold-Job */
1178 CUPSD_ACCESSLOG_ACTIONS
,/* Release-Job */
1179 CUPSD_ACCESSLOG_ACTIONS
,/* Restart-Job */
1180 CUPSD_ACCESSLOG_ALL
, /* reserved */
1181 CUPSD_ACCESSLOG_CONFIG
, /* Pause-Printer */
1182 CUPSD_ACCESSLOG_CONFIG
, /* Resume-Printer */
1183 CUPSD_ACCESSLOG_CONFIG
, /* Purge-Jobs */
1184 CUPSD_ACCESSLOG_CONFIG
, /* Set-Printer-Attributes */
1185 CUPSD_ACCESSLOG_ACTIONS
,/* Set-Job-Attributes */
1186 CUPSD_ACCESSLOG_CONFIG
, /* Get-Printer-Supported-Values */
1187 CUPSD_ACCESSLOG_ACTIONS
,/* Create-Printer-Subscription */
1188 CUPSD_ACCESSLOG_ACTIONS
,/* Create-Job-Subscription */
1189 CUPSD_ACCESSLOG_ALL
, /* Get-Subscription-Attributes */
1190 CUPSD_ACCESSLOG_ALL
, /* Get-Subscriptions */
1191 CUPSD_ACCESSLOG_ACTIONS
,/* Renew-Subscription */
1192 CUPSD_ACCESSLOG_ACTIONS
,/* Cancel-Subscription */
1193 CUPSD_ACCESSLOG_ALL
, /* Get-Notifications */
1194 CUPSD_ACCESSLOG_ACTIONS
,/* Send-Notifications */
1195 CUPSD_ACCESSLOG_ALL
, /* reserved */
1196 CUPSD_ACCESSLOG_ALL
, /* reserved */
1197 CUPSD_ACCESSLOG_ALL
, /* reserved */
1198 CUPSD_ACCESSLOG_ALL
, /* Get-Print-Support-Files */
1199 CUPSD_ACCESSLOG_CONFIG
, /* Enable-Printer */
1200 CUPSD_ACCESSLOG_CONFIG
, /* Disable-Printer */
1201 CUPSD_ACCESSLOG_CONFIG
, /* Pause-Printer-After-Current-Job */
1202 CUPSD_ACCESSLOG_ACTIONS
,/* Hold-New-Jobs */
1203 CUPSD_ACCESSLOG_ACTIONS
,/* Release-Held-New-Jobs */
1204 CUPSD_ACCESSLOG_CONFIG
, /* Deactivate-Printer */
1205 CUPSD_ACCESSLOG_CONFIG
, /* Activate-Printer */
1206 CUPSD_ACCESSLOG_CONFIG
, /* Restart-Printer */
1207 CUPSD_ACCESSLOG_CONFIG
, /* Shutdown-Printer */
1208 CUPSD_ACCESSLOG_CONFIG
, /* Startup-Printer */
1209 CUPSD_ACCESSLOG_ACTIONS
,/* Reprocess-Job */
1210 CUPSD_ACCESSLOG_ACTIONS
,/* Cancel-Current-Job */
1211 CUPSD_ACCESSLOG_ACTIONS
,/* Suspend-Current-Job */
1212 CUPSD_ACCESSLOG_ACTIONS
,/* Resume-Job */
1213 CUPSD_ACCESSLOG_ACTIONS
,/* Promote-Job */
1214 CUPSD_ACCESSLOG_ACTIONS
/* Schedule-Job-After */
1216 static cupsd_accesslog_t cups_ops
[] =
1218 CUPSD_ACCESSLOG_ALL
, /* CUPS-Get-Default */
1219 CUPSD_ACCESSLOG_ALL
, /* CUPS-Get-Printers */
1220 CUPSD_ACCESSLOG_CONFIG
, /* CUPS-Add-Modify-Printer */
1221 CUPSD_ACCESSLOG_CONFIG
, /* CUPS-Delete-Printer */
1222 CUPSD_ACCESSLOG_ALL
, /* CUPS-Get-Classes */
1223 CUPSD_ACCESSLOG_CONFIG
, /* CUPS-Add-Modify-Class */
1224 CUPSD_ACCESSLOG_CONFIG
, /* CUPS-Delete-Class */
1225 CUPSD_ACCESSLOG_CONFIG
, /* CUPS-Accept-Jobs */
1226 CUPSD_ACCESSLOG_CONFIG
, /* CUPS-Reject-Jobs */
1227 CUPSD_ACCESSLOG_CONFIG
, /* CUPS-Set-Default */
1228 CUPSD_ACCESSLOG_CONFIG
, /* CUPS-Get-Devices */
1229 CUPSD_ACCESSLOG_CONFIG
, /* CUPS-Get-PPDs */
1230 CUPSD_ACCESSLOG_ACTIONS
,/* CUPS-Move-Job */
1231 CUPSD_ACCESSLOG_ACTIONS
,/* CUPS-Authenticate-Job */
1232 CUPSD_ACCESSLOG_ALL
/* CUPS-Get-PPD */
1236 if ((op
<= IPP_OP_SCHEDULE_JOB_AFTER
&& standard_ops
[op
] > AccessLogLevel
) ||
1237 (op
>= IPP_OP_CUPS_GET_DEFAULT
&& op
<= IPP_OP_CUPS_GET_PPD
&&
1238 cups_ops
[op
- IPP_OP_CUPS_GET_DEFAULT
] > AccessLogLevel
))
1243 #ifdef HAVE_SYSTEMD_SD_JOURNAL_H
1244 if (!strcmp(AccessLog
, "syslog"))
1246 sd_journal_print(LOG_INFO
, "REQUEST %s - %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT
" %s %s", con
->http
->hostname
, con
->username
[0] != '\0' ? con
->username
: "-", states
[con
->operation
], _httpEncodeURI(temp
, con
->uri
, sizeof(temp
)), 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
) : "-");
1250 #elif defined(HAVE_VSYSLOG)
1252 * See if we are logging accesses via syslog...
1255 if (!strcmp(AccessLog
, "syslog"))
1258 "REQUEST %s - %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT
" %s %s\n",
1259 con
->http
->hostname
, con
->username
[0] != '\0' ? con
->username
: "-",
1260 states
[con
->operation
], _httpEncodeURI(temp
, con
->uri
, sizeof(temp
)),
1261 con
->http
->version
/ 100, con
->http
->version
% 100,
1262 code
, CUPS_LLCAST con
->bytes
,
1264 ippOpString(con
->request
->request
.op
.operation_id
) : "-",
1266 ippErrorString(con
->response
->request
.status
.status_code
) : "-");
1270 #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
1273 * Not using syslog; check the log file...
1276 if (!cupsdCheckLogFile(&AccessFile
, AccessLog
))
1280 * Write a log of the request in "common log format"...
1283 cupsFilePrintf(AccessFile
,
1284 "%s - %s %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT
" %s %s\n",
1285 con
->http
->hostname
,
1286 con
->username
[0] != '\0' ? con
->username
: "-",
1287 cupsdGetDateTime(&(con
->start
), LogTimeFormat
),
1288 states
[con
->operation
],
1289 _httpEncodeURI(temp
, con
->uri
, sizeof(temp
)),
1290 con
->http
->version
/ 100, con
->http
->version
% 100,
1291 code
, CUPS_LLCAST con
->bytes
,
1293 ippOpString(con
->request
->request
.op
.operation_id
) : "-",
1295 ippErrorString(con
->response
->request
.status
.status_code
) :
1298 cupsFileFlush(AccessFile
);
1305 * 'cupsdWriteErrorLog()' - Write a line to the ErrorLog.
1308 int /* O - 1 on success, 0 on failure */
1309 cupsdWriteErrorLog(int level
, /* I - Log level */
1310 const char *message
) /* I - Message string */
1312 int ret
= 1; /* Return value */
1313 static const char levels
[] = /* Log levels... */
1328 #ifdef HAVE_SYSTEMD_SD_JOURNAL_H
1329 if (!strcmp(ErrorLog
, "syslog"))
1331 sd_journal_print(log_levels
[level
], "%s", message
);
1335 #elif defined(HAVE_VSYSLOG)
1337 * See if we are logging errors via syslog...
1340 if (!strcmp(ErrorLog
, "syslog"))
1342 syslog(log_levels
[level
], "%s", message
);
1345 #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
1348 * Not using syslog; check the log file...
1351 cupsMutexLock(&log_mutex
);
1353 if (!cupsdCheckLogFile(&ErrorFile
, ErrorLog
))
1360 * Write the log message...
1363 cupsFilePrintf(ErrorFile
, "%c %s %s\n", levels
[level
],
1364 cupsdGetDateTime(NULL
, LogTimeFormat
), message
);
1365 cupsFileFlush(ErrorFile
);
1368 cupsMutexUnlock(&log_mutex
);
1375 * 'format_log_line()' - Format a line for a log file.
1377 * This function resizes a global string buffer as needed. Each call returns
1378 * a pointer to this buffer, so the contents are only good until the next call
1379 * to format_log_line()...
1382 static int /* O - -1 for fatal, 0 for retry, 1 for success */
1383 format_log_line(const char *message
, /* I - Printf-style format string */
1384 va_list ap
) /* I - Argument list */
1386 ssize_t len
; /* Length of formatted line */
1390 * Allocate the line buffer as needed...
1395 log_linesize
= 8192;
1396 log_line
= malloc(log_linesize
);
1403 * Format the log message...
1406 len
= cupsFormatStringv(log_line
, log_linesize
, message
, ap
);
1409 * Resize the buffer as needed...
1412 if ((size_t)len
>= log_linesize
&& log_linesize
< 65536)
1414 char *temp
; /* Temporary string pointer */
1420 else if (len
> 65536)
1423 temp
= realloc(log_line
, (size_t)len
);
1428 log_linesize
= (size_t)len
;