Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/scheduler/log.c
1090 views
1
/*
2
* Log file routines for the CUPS scheduler.
3
*
4
* Copyright © 2021-2022 by OpenPrinting.
5
* Copyright © 2007-2018 by Apple Inc.
6
* Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7
*
8
* Licensed under Apache License v2.0. See the file "LICENSE" for more
9
* information.
10
*/
11
12
/*
13
* Include necessary headers...
14
*/
15
16
#include "cupsd.h"
17
#include <stdarg.h>
18
#ifdef HAVE_ASL_H
19
# include <asl.h>
20
#elif defined(HAVE_SYSTEMD_SD_JOURNAL_H)
21
# define SD_JOURNAL_SUPPRESS_LOCATION
22
# include <systemd/sd-journal.h>
23
#endif /* HAVE_ASL_H */
24
#include <syslog.h>
25
26
27
/*
28
* Constants for log keys from PWG 5110.3 (PWG Common Log Format)...
29
*/
30
31
#define PWG_DeviceUUID "DUU"
32
#define PWG_Event "E"
33
#define PWG_LogNaturalLanguage "NL"
34
#define PWG_Status "S"
35
#define PWG_ServiceURI "URI"
36
#define PWG_UserHost "UH"
37
#define PWG_UserName "UN"
38
#define PWG_UserURI "UU"
39
#define PWG_ServiceIsAcceptingJobs "IAJ"
40
#define PWG_ServiceState "ST"
41
#define PWG_ServiceStateReasons "SR"
42
#define PWG_ServiceUUID "SUU"
43
#define PWG_JobID "JID"
44
#define PWG_JobUUID "JUU"
45
#define PWG_JobImagesCompleted "JIM"
46
#define PWG_JobImpressionsCompleted "JIC"
47
#define PWG_JobDestinationURI "JD"
48
#define PWG_JobState "JS"
49
#define PWG_JobStateReasons "JR"
50
#define PWG_JobAccountingID "JA"
51
#define PWG_JobAcountingUserName "JAUN"
52
#define PWG_JobAccountingUserURI "JAUU"
53
54
55
/*
56
* Local globals...
57
*/
58
59
static _cups_mutex_t log_mutex = _CUPS_MUTEX_INITIALIZER;
60
/* Mutex for logging */
61
static size_t log_linesize = 0; /* Size of line for output file */
62
static char *log_line = NULL; /* Line for output file */
63
64
#ifdef HAVE_ASL_H
65
static const int log_levels[] = /* ASL levels... */
66
{
67
ASL_LEVEL_EMERG,
68
ASL_LEVEL_EMERG,
69
ASL_LEVEL_ALERT,
70
ASL_LEVEL_CRIT,
71
ASL_LEVEL_ERR,
72
ASL_LEVEL_WARNING,
73
ASL_LEVEL_NOTICE,
74
ASL_LEVEL_INFO,
75
ASL_LEVEL_DEBUG,
76
ASL_LEVEL_DEBUG
77
};
78
#elif defined(HAVE_VSYSLOG) || defined(HAVE_SYSTEMD_SD_JOURNAL_H)
79
static const int log_levels[] = /* SYSLOG levels... */
80
{
81
0,
82
LOG_EMERG,
83
LOG_ALERT,
84
LOG_CRIT,
85
LOG_ERR,
86
LOG_WARNING,
87
LOG_NOTICE,
88
LOG_INFO,
89
LOG_DEBUG,
90
LOG_DEBUG
91
};
92
#endif /* HAVE_ASL_H */
93
94
95
/*
96
* Local functions...
97
*/
98
99
static int format_log_line(const char *message, va_list ap);
100
101
102
/*
103
* 'cupsdCheckLogFile()' - Open/rotate a log file if it needs it.
104
*/
105
106
int /* O - 1 if log file open */
107
cupsdCheckLogFile(cups_file_t **lf, /* IO - Log file */
108
const char *logname) /* I - Log filename */
109
{
110
char backname[1024], /* Backup log filename */
111
filename[1024], /* Formatted log filename */
112
*ptr; /* Pointer into filename */
113
const char *logptr; /* Pointer into log filename */
114
115
116
/*
117
* See if we have a log file to check...
118
*/
119
120
if (!lf || !logname || !logname[0])
121
return (1);
122
123
/*
124
* Handle logging to stderr...
125
*/
126
127
if (!strcmp(logname, "stderr"))
128
{
129
*lf = LogStderr;
130
return (1);
131
}
132
133
/*
134
* Format the filename as needed...
135
*/
136
137
if (!*lf ||
138
(strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize &&
139
MaxLogSize > 0))
140
{
141
/*
142
* Handle format strings...
143
*/
144
145
filename[sizeof(filename) - 1] = '\0';
146
147
if (logname[0] != '/')
148
{
149
strlcpy(filename, ServerRoot, sizeof(filename));
150
strlcat(filename, "/", sizeof(filename));
151
}
152
else
153
filename[0] = '\0';
154
155
for (logptr = logname, ptr = filename + strlen(filename);
156
*logptr && ptr < (filename + sizeof(filename) - 1);
157
logptr ++)
158
if (*logptr == '%')
159
{
160
/*
161
* Format spec...
162
*/
163
164
logptr ++;
165
if (*logptr == 's')
166
{
167
/*
168
* Insert the server name...
169
*/
170
171
strlcpy(ptr, ServerName, sizeof(filename) - (size_t)(ptr - filename));
172
ptr += strlen(ptr);
173
}
174
else
175
{
176
/*
177
* Otherwise just insert the character...
178
*/
179
180
*ptr++ = *logptr;
181
}
182
}
183
else
184
*ptr++ = *logptr;
185
186
*ptr = '\0';
187
}
188
189
/*
190
* See if the log file is open...
191
*/
192
193
if (!*lf)
194
{
195
/*
196
* Nope, open the log file...
197
*/
198
199
if ((*lf = cupsFileOpen(filename, "a")) == NULL)
200
{
201
/*
202
* If the file is in CUPS_LOGDIR then try to create a missing directory...
203
*/
204
205
if (!strncmp(filename, CUPS_LOGDIR, strlen(CUPS_LOGDIR)))
206
{
207
/*
208
* Try updating the permissions of the containing log directory, using
209
* the log file permissions as a basis...
210
*/
211
212
mode_t log_dir_perm = (mode_t)(0300 | LogFilePerm);
213
/* LogFilePerm + owner write/search */
214
if (log_dir_perm & 0040)
215
log_dir_perm |= 0010; /* Add group search */
216
if (log_dir_perm & 0004)
217
log_dir_perm |= 0001; /* Add other search */
218
219
cupsdCheckPermissions(CUPS_LOGDIR, NULL, log_dir_perm, RunUser, Group, 1, -1);
220
221
*lf = cupsFileOpen(filename, "a");
222
}
223
224
if (*lf == NULL)
225
{
226
#ifdef HAVE_SYSTEMD_SD_JOURNAL_H
227
sd_journal_print(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno));
228
#else
229
syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno));
230
#endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
231
232
if (FatalErrors & CUPSD_FATAL_LOG)
233
cupsdEndProcess(getpid(), 0);
234
235
return (0);
236
}
237
}
238
239
if (strncmp(filename, "/dev/", 5))
240
{
241
/*
242
* Change ownership and permissions of non-device logs...
243
*/
244
245
fchown(cupsFileNumber(*lf), RunUser, LogFileGroup);
246
fchmod(cupsFileNumber(*lf), LogFilePerm);
247
}
248
}
249
250
/*
251
* Do we need to rotate the log?
252
*/
253
254
if (strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize &&
255
MaxLogSize > 0)
256
{
257
/*
258
* Rotate log file...
259
*/
260
261
cupsFileClose(*lf);
262
263
strlcpy(backname, filename, sizeof(backname));
264
strlcat(backname, ".O", sizeof(backname));
265
266
unlink(backname);
267
rename(filename, backname);
268
269
if ((*lf = cupsFileOpen(filename, "a")) == NULL)
270
{
271
#ifdef HAVE_SYSTEMD_SD_JOURNAL_H
272
sd_journal_print(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno));
273
274
#else
275
syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno));
276
#endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
277
278
if (FatalErrors & CUPSD_FATAL_LOG)
279
cupsdEndProcess(getpid(), 0);
280
281
return (0);
282
}
283
284
/*
285
* Change ownership and permissions of non-device logs...
286
*/
287
288
fchown(cupsFileNumber(*lf), RunUser, LogFileGroup);
289
fchmod(cupsFileNumber(*lf), LogFilePerm);
290
}
291
292
return (1);
293
}
294
295
296
/*
297
* 'cupsdGetDateTime()' - Returns a pointer to a date/time string.
298
*/
299
300
char * /* O - Date/time string */
301
cupsdGetDateTime(struct timeval *t, /* I - Time value or NULL for current */
302
cupsd_time_t format) /* I - Format to use */
303
{
304
struct timeval curtime; /* Current time value */
305
struct tm date; /* Date/time value */
306
static struct timeval last_time = { 0, 0 };
307
/* Last time we formatted */
308
static char s[1024]; /* Date/time string */
309
static const char * const months[12] =/* Months */
310
{
311
"Jan",
312
"Feb",
313
"Mar",
314
"Apr",
315
"May",
316
"Jun",
317
"Jul",
318
"Aug",
319
"Sep",
320
"Oct",
321
"Nov",
322
"Dec"
323
};
324
325
326
/*
327
* Make sure we have a valid time...
328
*/
329
330
if (!t)
331
{
332
gettimeofday(&curtime, NULL);
333
t = &curtime;
334
}
335
336
if (t->tv_sec != last_time.tv_sec ||
337
(LogTimeFormat == CUPSD_TIME_USECS && t->tv_usec != last_time.tv_usec))
338
{
339
last_time = *t;
340
341
/*
342
* Get the date and time from the UNIX time value, and then format it
343
* into a string. Note that we *can't* use the strftime() function since
344
* it is localized and will seriously confuse automatic programs if the
345
* month names are in the wrong language!
346
*
347
* Also, we use the "timezone" variable that contains the current timezone
348
* offset from GMT in seconds so that we are reporting local time in the
349
* log files. If you want GMT, set the TZ environment variable accordingly
350
* before starting the scheduler.
351
*
352
* (*BSD and Darwin store the timezone offset in the tm structure)
353
*/
354
355
localtime_r(&(t->tv_sec), &date);
356
357
if (format == CUPSD_TIME_STANDARD)
358
snprintf(s, sizeof(s), "[%02d/%s/%04d:%02d:%02d:%02d %+03ld%02ld]",
359
date.tm_mday, months[date.tm_mon], 1900 + date.tm_year,
360
date.tm_hour, date.tm_min, date.tm_sec,
361
#ifdef HAVE_TM_GMTOFF
362
date.tm_gmtoff / 3600, (date.tm_gmtoff / 60) % 60);
363
#else
364
timezone / 3600, (timezone / 60) % 60);
365
#endif /* HAVE_TM_GMTOFF */
366
else
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
#ifdef HAVE_TM_GMTOFF
371
date.tm_gmtoff / 3600, (date.tm_gmtoff / 60) % 60);
372
#else
373
timezone / 3600, (timezone / 60) % 60);
374
#endif /* HAVE_TM_GMTOFF */
375
}
376
377
return (s);
378
}
379
380
381
/*
382
* 'cupsdLogFCMessage()' - Log a file checking message.
383
*/
384
385
void
386
cupsdLogFCMessage(
387
void *context, /* I - Printer (if any) */
388
_cups_fc_result_t result, /* I - Check result */
389
const char *message) /* I - Message to log */
390
{
391
cupsd_printer_t *p = (cupsd_printer_t *)context;
392
/* Printer */
393
cupsd_loglevel_t level; /* Log level */
394
395
396
if (result == _CUPS_FILE_CHECK_OK)
397
level = CUPSD_LOG_DEBUG2;
398
else
399
level = CUPSD_LOG_ERROR;
400
401
if (p)
402
{
403
cupsdLogMessage(level, "%s: %s", p->name, message);
404
405
if (result == _CUPS_FILE_CHECK_MISSING ||
406
result == _CUPS_FILE_CHECK_WRONG_TYPE)
407
{
408
strlcpy(p->state_message, message, sizeof(p->state_message));
409
410
if (cupsdSetPrinterReasons(p, "+cups-missing-filter-warning"))
411
cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, p, NULL, "%s", message);
412
}
413
else if (result == _CUPS_FILE_CHECK_PERMISSIONS ||
414
result == _CUPS_FILE_CHECK_RELATIVE_PATH)
415
{
416
strlcpy(p->state_message, message, sizeof(p->state_message));
417
418
if (cupsdSetPrinterReasons(p, "+cups-insecure-filter-warning"))
419
cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, p, NULL, "%s", message);
420
}
421
}
422
else
423
cupsdLogMessage(level, "%s", message);
424
}
425
426
427
#ifdef HAVE_GSSAPI
428
/*
429
* 'cupsdLogGSSMessage()' - Log a GSSAPI error...
430
*/
431
432
int /* O - 1 on success, 0 on error */
433
cupsdLogGSSMessage(
434
int level, /* I - Log level */
435
OM_uint32 major_status, /* I - Major GSSAPI status */
436
OM_uint32 minor_status, /* I - Minor GSSAPI status */
437
const char *message, /* I - printf-style message string */
438
...) /* I - Additional args as needed */
439
{
440
OM_uint32 err_major_status, /* Major status code for display */
441
err_minor_status; /* Minor status code for display */
442
OM_uint32 msg_ctx; /* Message context */
443
gss_buffer_desc major_status_string = GSS_C_EMPTY_BUFFER,
444
/* Major status message */
445
minor_status_string = GSS_C_EMPTY_BUFFER;
446
/* Minor status message */
447
int ret; /* Return value */
448
char buffer[8192]; /* Buffer for vsnprintf */
449
450
451
if (strchr(message, '%'))
452
{
453
/*
454
* Format the message string...
455
*/
456
457
va_list ap; /* Pointer to arguments */
458
459
va_start(ap, message);
460
vsnprintf(buffer, sizeof(buffer), message, ap);
461
va_end(ap);
462
463
message = buffer;
464
}
465
466
msg_ctx = 0;
467
err_major_status = gss_display_status(&err_minor_status,
468
major_status,
469
GSS_C_GSS_CODE,
470
GSS_C_NO_OID,
471
&msg_ctx,
472
&major_status_string);
473
474
if (!GSS_ERROR(err_major_status))
475
gss_display_status(&err_minor_status, minor_status, GSS_C_MECH_CODE,
476
GSS_C_NULL_OID, &msg_ctx, &minor_status_string);
477
478
ret = cupsdLogMessage(level, "%s: %s, %s", message,
479
(char *)major_status_string.value,
480
(char *)minor_status_string.value);
481
gss_release_buffer(&err_minor_status, &major_status_string);
482
gss_release_buffer(&err_minor_status, &minor_status_string);
483
484
return (ret);
485
}
486
#endif /* HAVE_GSSAPI */
487
488
489
/*
490
* 'cupsdLogClient()' - Log a client message.
491
*/
492
493
int /* O - 1 on success, 0 on error */
494
cupsdLogClient(cupsd_client_t *con, /* I - Client connection */
495
int level, /* I - Log level */
496
const char *message, /* I - Printf-style message string */
497
...) /* I - Additional arguments as needed */
498
{
499
va_list ap, ap2; /* Argument pointers */
500
char clientmsg[1024];/* Format string for client message */
501
int status; /* Formatting status */
502
503
504
/*
505
* See if we want to log this message...
506
*/
507
508
if (TestConfigFile || !ErrorLog)
509
return (1);
510
511
if (level > LogLevel)
512
return (1);
513
514
/*
515
* Format and write the log message...
516
*/
517
518
if (con)
519
snprintf(clientmsg, sizeof(clientmsg), "[Client %d] %s", con->number,
520
message);
521
else
522
strlcpy(clientmsg, message, sizeof(clientmsg));
523
524
va_start(ap, message);
525
526
do
527
{
528
va_copy(ap2, ap);
529
status = format_log_line(clientmsg, ap2);
530
va_end(ap2);
531
}
532
while (status == 0);
533
534
va_end(ap);
535
536
if (status > 0)
537
return (cupsdWriteErrorLog(level, log_line));
538
else
539
return (cupsdWriteErrorLog(CUPSD_LOG_ERROR,
540
"Unable to allocate memory for log line."));
541
}
542
543
544
/*
545
* 'cupsdLogJob()' - Log a job message.
546
*/
547
548
int /* O - 1 on success, 0 on error */
549
cupsdLogJob(cupsd_job_t *job, /* I - Job */
550
int level, /* I - Log level */
551
const char *message, /* I - Printf-style message string */
552
...) /* I - Additional arguments as needed */
553
{
554
va_list ap, ap2; /* Argument pointers */
555
char jobmsg[1024]; /* Format string for job message */
556
int status; /* Formatting status */
557
558
559
/*
560
* See if we want to log this message...
561
*/
562
563
if (TestConfigFile || !ErrorLog)
564
return (1);
565
566
if (level > LogLevel && LogDebugHistory <= 0)
567
return (1);
568
569
/*
570
* Format and write the log message...
571
*/
572
573
if (job)
574
snprintf(jobmsg, sizeof(jobmsg), "[Job %d] %s", job->id, message);
575
else
576
strlcpy(jobmsg, message, sizeof(jobmsg));
577
578
va_start(ap, message);
579
580
do
581
{
582
va_copy(ap2, ap);
583
status = format_log_line(jobmsg, ap2);
584
va_end(ap2);
585
}
586
while (status == 0);
587
588
va_end(ap);
589
590
if (status > 0)
591
{
592
if (job && level > LogLevel && LogDebugHistory > 0)
593
{
594
/*
595
* Add message to the job history...
596
*/
597
598
cupsd_joblog_t *temp; /* Copy of log message */
599
size_t log_len = strlen(log_line);
600
/* Length of log message */
601
602
if ((temp = malloc(sizeof(cupsd_joblog_t) + log_len)) != NULL)
603
{
604
temp->time = time(NULL);
605
memcpy(temp->message, log_line, log_len + 1);
606
}
607
608
if (!job->history)
609
job->history = cupsArrayNew(NULL, NULL);
610
611
if (job->history && temp)
612
{
613
cupsArrayAdd(job->history, temp);
614
615
if (cupsArrayCount(job->history) > LogDebugHistory)
616
{
617
/*
618
* Remove excess messages...
619
*/
620
621
temp = cupsArrayFirst(job->history);
622
cupsArrayRemove(job->history, temp);
623
free(temp);
624
}
625
}
626
else if (temp)
627
free(temp);
628
629
return (1);
630
}
631
else if (level <= LogLevel)
632
{
633
#ifdef HAVE_SYSTEMD_SD_JOURNAL_H
634
if (!strcmp(ErrorLog, "syslog"))
635
{
636
cupsd_printer_t *printer = job ? (job->printer ? job->printer : (job->dest ? cupsdFindDest(job->dest) : NULL)) : NULL;
637
static const char * const job_states[] =
638
{ /* job-state strings */
639
"Pending",
640
"PendingHeld",
641
"Processing",
642
"ProcessingStopped",
643
"Canceled",
644
"Aborted",
645
"Completed"
646
};
647
648
if (job)
649
sd_journal_send("MESSAGE=%s", log_line,
650
"PRIORITY=%i", log_levels[level],
651
PWG_Event"=JobStateChanged",
652
PWG_ServiceURI"=%s", printer ? printer->uri : "",
653
PWG_JobID"=%d", job->id,
654
PWG_JobState"=%s", job->state_value < IPP_JSTATE_PENDING ? "" : job_states[job->state_value - IPP_JSTATE_PENDING],
655
PWG_JobImpressionsCompleted"=%d", ippGetInteger(job->impressions, 0),
656
NULL);
657
else
658
sd_journal_send("MESSAGE=%s", log_line,
659
"PRIORITY=%i", log_levels[level],
660
NULL);
661
662
return (1);
663
}
664
else
665
#endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
666
667
return (cupsdWriteErrorLog(level, log_line));
668
}
669
else
670
return (1);
671
}
672
else
673
return (cupsdWriteErrorLog(CUPSD_LOG_ERROR,
674
"Unable to allocate memory for log line."));
675
}
676
677
678
/*
679
* 'cupsdLogMessage()' - Log a message to the error log file.
680
*/
681
682
int /* O - 1 on success, 0 on error */
683
cupsdLogMessage(int level, /* I - Log level */
684
const char *message, /* I - printf-style message string */
685
...) /* I - Additional args as needed */
686
{
687
va_list ap, ap2; /* Argument pointers */
688
int status; /* Formatting status */
689
690
691
/*
692
* See if we want to log this message...
693
*/
694
695
if (TestConfigFile && level <= CUPSD_LOG_WARN)
696
{
697
va_start(ap, message);
698
699
vfprintf(stderr, message, ap);
700
putc('\n', stderr);
701
702
va_end(ap);
703
704
return (1);
705
}
706
else if (!ErrorLog && level <= CUPSD_LOG_WARN)
707
{
708
va_start(ap, message);
709
710
#ifdef HAVE_SYSTEMD_SD_JOURNAL_H
711
sd_journal_printv(log_levels[level], message, ap);
712
713
#elif defined(HAVE_VSYSLOG)
714
vsyslog(LOG_LPR | log_levels[level], message, ap);
715
716
#else
717
vfprintf(stderr, message, ap);
718
putc('\n', stderr);
719
#endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
720
721
va_end(ap);
722
723
return (1);
724
}
725
else if (level > LogLevel || !ErrorLog)
726
return (1);
727
728
#ifdef HAVE_SYSTEMD_SD_JOURNAL_H
729
else if (!strcmp(ErrorLog, "syslog"))
730
{
731
va_start(ap, message);
732
sd_journal_printv(log_levels[level], message, ap);
733
va_end(ap);
734
return (1);
735
}
736
#endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
737
738
/*
739
* Format and write the log message...
740
*/
741
742
va_start(ap, message);
743
744
do
745
{
746
va_copy(ap2, ap);
747
status = format_log_line(message, ap2);
748
va_end(ap2);
749
}
750
while (status == 0);
751
752
va_end(ap);
753
754
if (status > 0)
755
return (cupsdWriteErrorLog(level, log_line));
756
else
757
return (cupsdWriteErrorLog(CUPSD_LOG_ERROR,
758
"Unable to allocate memory for log line!"));
759
}
760
761
762
/*
763
* 'cupsdLogPage()' - Log a page to the page log file.
764
*/
765
766
int /* O - 1 on success, 0 on error */
767
cupsdLogPage(cupsd_job_t *job, /* I - Job being printed */
768
const char *page) /* I - Page being printed */
769
{
770
int i; /* Looping var */
771
char buffer[2048], /* Buffer for page log */
772
*bufptr, /* Pointer into buffer */
773
name[256]; /* Attribute name */
774
const char *format, /* Pointer into PageLogFormat */
775
*nameend; /* End of attribute name */
776
ipp_attribute_t *attr; /* Current attribute */
777
char number[256]; /* Page number */
778
int copies; /* Number of copies */
779
780
781
/*
782
* Format the line going into the page log...
783
*/
784
785
if (!PageLogFormat)
786
return (1);
787
788
strlcpy(number, "1", sizeof(number));
789
copies = 1;
790
sscanf(page, "%255s%d", number, &copies);
791
792
for (format = PageLogFormat, bufptr = buffer; *format; format ++)
793
{
794
if (*format == '%')
795
{
796
format ++;
797
798
switch (*format)
799
{
800
case '%' : /* Literal % */
801
if (bufptr < (buffer + sizeof(buffer) - 1))
802
*bufptr++ = '%';
803
break;
804
805
case 'p' : /* Printer name */
806
strlcpy(bufptr, job->dest, sizeof(buffer) - (size_t)(bufptr - buffer));
807
bufptr += strlen(bufptr);
808
break;
809
810
case 'j' : /* Job ID */
811
snprintf(bufptr, sizeof(buffer) - (size_t)(bufptr - buffer), "%d", job->id);
812
bufptr += strlen(bufptr);
813
break;
814
815
case 'u' : /* Username */
816
strlcpy(bufptr, job->username ? job->username : "-", sizeof(buffer) - (size_t)(bufptr - buffer));
817
bufptr += strlen(bufptr);
818
break;
819
820
case 'T' : /* Date and time */
821
strlcpy(bufptr, cupsdGetDateTime(NULL, LogTimeFormat), sizeof(buffer) - (size_t)(bufptr - buffer));
822
bufptr += strlen(bufptr);
823
break;
824
825
case 'P' : /* Page number */
826
strlcpy(bufptr, number, sizeof(buffer) - (size_t)(bufptr - buffer));
827
bufptr += strlen(bufptr);
828
break;
829
830
case 'C' : /* Number of copies */
831
snprintf(bufptr, sizeof(buffer) - (size_t)(bufptr - buffer), "%d", copies);
832
bufptr += strlen(bufptr);
833
break;
834
835
case '{' : /* {attribute} */
836
if ((nameend = strchr(format, '}')) != NULL && (size_t)(nameend - format - 2) < (sizeof(name) - 1))
837
{
838
/*
839
* Pull the name from inside the brackets...
840
*/
841
842
memcpy(name, format + 1, (size_t)(nameend - format - 1));
843
name[nameend - format - 1] = '\0';
844
845
format = nameend;
846
847
attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO);
848
if (!attr && !strcmp(name, "job-billing"))
849
{
850
/*
851
* Handle alias "job-account-id" (which was standardized after
852
* "job-billing" was defined for CUPS...
853
*/
854
855
attr = ippFindAttribute(job->attrs, "job-account-id", IPP_TAG_ZERO);
856
}
857
else if (!attr && !strcmp(name, "media"))
858
{
859
/*
860
* Handle alias "media-col" which uses dimensions instead of
861
* names...
862
*/
863
864
attr = ippFindAttribute(job->attrs, "media-col/media-size", IPP_TAG_BEGIN_COLLECTION);
865
}
866
867
if (attr)
868
{
869
/*
870
* Add the attribute value...
871
*/
872
873
for (i = 0;
874
i < attr->num_values &&
875
bufptr < (buffer + sizeof(buffer) - 1);
876
i ++)
877
{
878
if (i)
879
*bufptr++ = ',';
880
881
switch (attr->value_tag)
882
{
883
case IPP_TAG_INTEGER :
884
case IPP_TAG_ENUM :
885
snprintf(bufptr, sizeof(buffer) - (size_t)(bufptr - buffer), "%d", attr->values[i].integer);
886
bufptr += strlen(bufptr);
887
break;
888
889
case IPP_TAG_BOOLEAN :
890
snprintf(bufptr, sizeof(buffer) - (size_t)(bufptr - buffer), "%d", attr->values[i].boolean);
891
bufptr += strlen(bufptr);
892
break;
893
894
case IPP_TAG_TEXTLANG :
895
case IPP_TAG_NAMELANG :
896
case IPP_TAG_TEXT :
897
case IPP_TAG_NAME :
898
case IPP_TAG_KEYWORD :
899
case IPP_TAG_URI :
900
case IPP_TAG_URISCHEME :
901
case IPP_TAG_CHARSET :
902
case IPP_TAG_LANGUAGE :
903
case IPP_TAG_MIMETYPE :
904
strlcpy(bufptr, attr->values[i].string.text, sizeof(buffer) - (size_t)(bufptr - buffer));
905
bufptr += strlen(bufptr);
906
break;
907
908
case IPP_TAG_BEGIN_COLLECTION :
909
if (!strcmp(attr->name, "media-size"))
910
{
911
ipp_attribute_t *x_dimension = ippFindAttribute(ippGetCollection(attr, 0), "x-dimension", IPP_TAG_INTEGER);
912
ipp_attribute_t *y_dimension = ippFindAttribute(ippGetCollection(attr, 0), "y-dimension", IPP_TAG_INTEGER);
913
/* Media dimensions */
914
915
if (x_dimension && y_dimension)
916
{
917
pwg_media_t *pwg = pwgMediaForSize(ippGetInteger(x_dimension, 0), ippGetInteger(y_dimension, 0));
918
/* PWG media name */
919
if (pwg)
920
{
921
strlcpy(bufptr, pwg->pwg, sizeof(buffer) - (size_t)(bufptr - buffer));
922
break;
923
}
924
}
925
}
926
927
default :
928
strlcpy(bufptr, "???", sizeof(buffer) - (size_t)(bufptr - buffer));
929
bufptr += strlen(bufptr);
930
break;
931
}
932
}
933
}
934
else if (bufptr < (buffer + sizeof(buffer) - 1))
935
*bufptr++ = '-';
936
break;
937
}
938
939
default :
940
if (bufptr < (buffer + sizeof(buffer) - 2))
941
{
942
*bufptr++ = '%';
943
*bufptr++ = *format;
944
}
945
break;
946
}
947
}
948
else if (bufptr < (buffer + sizeof(buffer) - 1))
949
*bufptr++ = *format;
950
}
951
952
*bufptr = '\0';
953
954
#ifdef HAVE_SYSTEMD_SD_JOURNAL_H
955
if (!strcmp(PageLog, "syslog"))
956
{
957
static const char * const job_states[] =
958
{ /* job-state strings */
959
"Pending",
960
"PendingHeld",
961
"Processing",
962
"ProcessingStopped",
963
"Canceled",
964
"Aborted",
965
"Completed"
966
};
967
968
sd_journal_send("MESSAGE=%s", buffer,
969
"PRIORITY=%i", LOG_INFO,
970
PWG_Event"=JobStateChanged",
971
PWG_ServiceURI"=%s", job->printer->uri,
972
PWG_JobID"=%d", job->id,
973
PWG_JobState"=%s", job_states[job->state_value - IPP_JSTATE_PENDING],
974
PWG_JobImpressionsCompleted"=%d", ippGetInteger(job->impressions, 0),
975
NULL);
976
return (1);
977
}
978
979
#elif defined(HAVE_VSYSLOG)
980
/*
981
* See if we are logging pages via syslog...
982
*/
983
984
if (!strcmp(PageLog, "syslog"))
985
{
986
syslog(LOG_INFO, "%s", buffer);
987
988
return (1);
989
}
990
#endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
991
992
/*
993
* Not using syslog; check the log file...
994
*/
995
996
if (!cupsdCheckLogFile(&PageFile, PageLog))
997
return (0);
998
999
/*
1000
* Print a page log entry of the form:
1001
*
1002
* printer user job-id [DD/MON/YYYY:HH:MM:SS +TTTT] page num-copies \
1003
* billing hostname
1004
*/
1005
1006
cupsFilePrintf(PageFile, "%s\n", buffer);
1007
cupsFileFlush(PageFile);
1008
1009
return (1);
1010
}
1011
1012
1013
/*
1014
* 'cupsdLogRequest()' - Log an HTTP request in Common Log Format.
1015
*/
1016
1017
int /* O - 1 on success, 0 on error */
1018
cupsdLogRequest(cupsd_client_t *con, /* I - Request to log */
1019
http_status_t code) /* I - Response code */
1020
{
1021
char temp[2048]; /* Temporary string for URI */
1022
static const char * const states[] = /* HTTP client states... */
1023
{
1024
"WAITING",
1025
"OPTIONS",
1026
"GET",
1027
"GET",
1028
"HEAD",
1029
"POST",
1030
"POST",
1031
"POST",
1032
"PUT",
1033
"PUT",
1034
"DELETE",
1035
"TRACE",
1036
"CLOSE",
1037
"STATUS"
1038
};
1039
1040
1041
/*
1042
* Filter requests as needed...
1043
*/
1044
1045
if (AccessLogLevel == CUPSD_ACCESSLOG_NONE || !AccessLog)
1046
return (1);
1047
else if (AccessLogLevel < CUPSD_ACCESSLOG_ALL)
1048
{
1049
/*
1050
* Eliminate simple GET, POST, and PUT requests...
1051
*/
1052
1053
if ((con->operation == HTTP_GET &&
1054
strncmp(con->uri, "/admin/conf", 11) &&
1055
strncmp(con->uri, "/admin/log", 10)) ||
1056
(con->operation == HTTP_POST && !con->request &&
1057
strncmp(con->uri, "/admin", 6)) ||
1058
(con->operation != HTTP_GET && con->operation != HTTP_POST &&
1059
con->operation != HTTP_PUT))
1060
return (1);
1061
1062
if (con->request && con->response &&
1063
(con->response->request.status.status_code < IPP_REDIRECTION_OTHER_SITE ||
1064
con->response->request.status.status_code == IPP_NOT_FOUND))
1065
{
1066
/*
1067
* Check successful requests...
1068
*/
1069
1070
ipp_op_t op = con->request->request.op.operation_id;
1071
static cupsd_accesslog_t standard_ops[] =
1072
{
1073
CUPSD_ACCESSLOG_ALL, /* reserved */
1074
CUPSD_ACCESSLOG_ALL, /* reserved */
1075
CUPSD_ACCESSLOG_ACTIONS,/* Print-Job */
1076
CUPSD_ACCESSLOG_ACTIONS,/* Print-URI */
1077
CUPSD_ACCESSLOG_ACTIONS,/* Validate-Job */
1078
CUPSD_ACCESSLOG_ACTIONS,/* Create-Job */
1079
CUPSD_ACCESSLOG_ACTIONS,/* Send-Document */
1080
CUPSD_ACCESSLOG_ACTIONS,/* Send-URI */
1081
CUPSD_ACCESSLOG_ACTIONS,/* Cancel-Job */
1082
CUPSD_ACCESSLOG_ALL, /* Get-Job-Attributes */
1083
CUPSD_ACCESSLOG_ALL, /* Get-Jobs */
1084
CUPSD_ACCESSLOG_ALL, /* Get-Printer-Attributes */
1085
CUPSD_ACCESSLOG_ACTIONS,/* Hold-Job */
1086
CUPSD_ACCESSLOG_ACTIONS,/* Release-Job */
1087
CUPSD_ACCESSLOG_ACTIONS,/* Restart-Job */
1088
CUPSD_ACCESSLOG_ALL, /* reserved */
1089
CUPSD_ACCESSLOG_CONFIG, /* Pause-Printer */
1090
CUPSD_ACCESSLOG_CONFIG, /* Resume-Printer */
1091
CUPSD_ACCESSLOG_CONFIG, /* Purge-Jobs */
1092
CUPSD_ACCESSLOG_CONFIG, /* Set-Printer-Attributes */
1093
CUPSD_ACCESSLOG_ACTIONS,/* Set-Job-Attributes */
1094
CUPSD_ACCESSLOG_CONFIG, /* Get-Printer-Supported-Values */
1095
CUPSD_ACCESSLOG_ACTIONS,/* Create-Printer-Subscription */
1096
CUPSD_ACCESSLOG_ACTIONS,/* Create-Job-Subscription */
1097
CUPSD_ACCESSLOG_ALL, /* Get-Subscription-Attributes */
1098
CUPSD_ACCESSLOG_ALL, /* Get-Subscriptions */
1099
CUPSD_ACCESSLOG_ACTIONS,/* Renew-Subscription */
1100
CUPSD_ACCESSLOG_ACTIONS,/* Cancel-Subscription */
1101
CUPSD_ACCESSLOG_ALL, /* Get-Notifications */
1102
CUPSD_ACCESSLOG_ACTIONS,/* Send-Notifications */
1103
CUPSD_ACCESSLOG_ALL, /* reserved */
1104
CUPSD_ACCESSLOG_ALL, /* reserved */
1105
CUPSD_ACCESSLOG_ALL, /* reserved */
1106
CUPSD_ACCESSLOG_ALL, /* Get-Print-Support-Files */
1107
CUPSD_ACCESSLOG_CONFIG, /* Enable-Printer */
1108
CUPSD_ACCESSLOG_CONFIG, /* Disable-Printer */
1109
CUPSD_ACCESSLOG_CONFIG, /* Pause-Printer-After-Current-Job */
1110
CUPSD_ACCESSLOG_ACTIONS,/* Hold-New-Jobs */
1111
CUPSD_ACCESSLOG_ACTIONS,/* Release-Held-New-Jobs */
1112
CUPSD_ACCESSLOG_CONFIG, /* Deactivate-Printer */
1113
CUPSD_ACCESSLOG_CONFIG, /* Activate-Printer */
1114
CUPSD_ACCESSLOG_CONFIG, /* Restart-Printer */
1115
CUPSD_ACCESSLOG_CONFIG, /* Shutdown-Printer */
1116
CUPSD_ACCESSLOG_CONFIG, /* Startup-Printer */
1117
CUPSD_ACCESSLOG_ACTIONS,/* Reprocess-Job */
1118
CUPSD_ACCESSLOG_ACTIONS,/* Cancel-Current-Job */
1119
CUPSD_ACCESSLOG_ACTIONS,/* Suspend-Current-Job */
1120
CUPSD_ACCESSLOG_ACTIONS,/* Resume-Job */
1121
CUPSD_ACCESSLOG_ACTIONS,/* Promote-Job */
1122
CUPSD_ACCESSLOG_ACTIONS /* Schedule-Job-After */
1123
};
1124
static cupsd_accesslog_t cups_ops[] =
1125
{
1126
CUPSD_ACCESSLOG_ALL, /* CUPS-Get-Default */
1127
CUPSD_ACCESSLOG_ALL, /* CUPS-Get-Printers */
1128
CUPSD_ACCESSLOG_CONFIG, /* CUPS-Add-Modify-Printer */
1129
CUPSD_ACCESSLOG_CONFIG, /* CUPS-Delete-Printer */
1130
CUPSD_ACCESSLOG_ALL, /* CUPS-Get-Classes */
1131
CUPSD_ACCESSLOG_CONFIG, /* CUPS-Add-Modify-Class */
1132
CUPSD_ACCESSLOG_CONFIG, /* CUPS-Delete-Class */
1133
CUPSD_ACCESSLOG_CONFIG, /* CUPS-Accept-Jobs */
1134
CUPSD_ACCESSLOG_CONFIG, /* CUPS-Reject-Jobs */
1135
CUPSD_ACCESSLOG_CONFIG, /* CUPS-Set-Default */
1136
CUPSD_ACCESSLOG_CONFIG, /* CUPS-Get-Devices */
1137
CUPSD_ACCESSLOG_CONFIG, /* CUPS-Get-PPDs */
1138
CUPSD_ACCESSLOG_ACTIONS,/* CUPS-Move-Job */
1139
CUPSD_ACCESSLOG_ACTIONS,/* CUPS-Authenticate-Job */
1140
CUPSD_ACCESSLOG_ALL /* CUPS-Get-PPD */
1141
};
1142
1143
1144
if ((op <= IPP_SCHEDULE_JOB_AFTER && standard_ops[op] > AccessLogLevel) ||
1145
(op >= CUPS_GET_DEFAULT && op <= CUPS_GET_PPD &&
1146
cups_ops[op - CUPS_GET_DEFAULT] > AccessLogLevel))
1147
return (1);
1148
}
1149
}
1150
1151
#ifdef HAVE_SYSTEMD_SD_JOURNAL_H
1152
if (!strcmp(AccessLog, "syslog"))
1153
{
1154
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) : "-");
1155
return (1);
1156
}
1157
1158
#elif defined(HAVE_VSYSLOG)
1159
/*
1160
* See if we are logging accesses via syslog...
1161
*/
1162
1163
if (!strcmp(AccessLog, "syslog"))
1164
{
1165
syslog(LOG_INFO,
1166
"REQUEST %s - %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT " %s %s\n",
1167
con->http->hostname, con->username[0] != '\0' ? con->username : "-",
1168
states[con->operation], _httpEncodeURI(temp, con->uri, sizeof(temp)),
1169
con->http->version / 100, con->http->version % 100,
1170
code, CUPS_LLCAST con->bytes,
1171
con->request ?
1172
ippOpString(con->request->request.op.operation_id) : "-",
1173
con->response ?
1174
ippErrorString(con->response->request.status.status_code) : "-");
1175
1176
return (1);
1177
}
1178
#endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
1179
1180
/*
1181
* Not using syslog; check the log file...
1182
*/
1183
1184
if (!cupsdCheckLogFile(&AccessFile, AccessLog))
1185
return (0);
1186
1187
/*
1188
* Write a log of the request in "common log format"...
1189
*/
1190
1191
cupsFilePrintf(AccessFile,
1192
"%s - %s %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT " %s %s\n",
1193
con->http->hostname,
1194
con->username[0] != '\0' ? con->username : "-",
1195
cupsdGetDateTime(&(con->start), LogTimeFormat),
1196
states[con->operation],
1197
_httpEncodeURI(temp, con->uri, sizeof(temp)),
1198
con->http->version / 100, con->http->version % 100,
1199
code, CUPS_LLCAST con->bytes,
1200
con->request ?
1201
ippOpString(con->request->request.op.operation_id) : "-",
1202
con->response ?
1203
ippErrorString(con->response->request.status.status_code) :
1204
"-");
1205
1206
cupsFileFlush(AccessFile);
1207
1208
return (1);
1209
}
1210
1211
1212
/*
1213
* 'cupsdWriteErrorLog()' - Write a line to the ErrorLog.
1214
*/
1215
1216
int /* O - 1 on success, 0 on failure */
1217
cupsdWriteErrorLog(int level, /* I - Log level */
1218
const char *message) /* I - Message string */
1219
{
1220
int ret = 1; /* Return value */
1221
static const char levels[] = /* Log levels... */
1222
{
1223
' ',
1224
'X',
1225
'A',
1226
'C',
1227
'E',
1228
'W',
1229
'N',
1230
'I',
1231
'D',
1232
'd'
1233
};
1234
1235
1236
#ifdef HAVE_SYSTEMD_SD_JOURNAL_H
1237
if (!strcmp(ErrorLog, "syslog"))
1238
{
1239
sd_journal_print(log_levels[level], "%s", message);
1240
return (1);
1241
}
1242
1243
#elif defined(HAVE_VSYSLOG)
1244
/*
1245
* See if we are logging errors via syslog...
1246
*/
1247
1248
if (!strcmp(ErrorLog, "syslog"))
1249
{
1250
syslog(log_levels[level], "%s", message);
1251
return (1);
1252
}
1253
#endif /* HAVE_SYSTEMD_SD_JOURNAL_H */
1254
1255
/*
1256
* Not using syslog; check the log file...
1257
*/
1258
1259
_cupsMutexLock(&log_mutex);
1260
1261
if (!cupsdCheckLogFile(&ErrorFile, ErrorLog))
1262
{
1263
ret = 0;
1264
}
1265
else
1266
{
1267
/*
1268
* Write the log message...
1269
*/
1270
1271
cupsFilePrintf(ErrorFile, "%c %s %s\n", levels[level],
1272
cupsdGetDateTime(NULL, LogTimeFormat), message);
1273
cupsFileFlush(ErrorFile);
1274
}
1275
1276
_cupsMutexUnlock(&log_mutex);
1277
1278
return (ret);
1279
}
1280
1281
1282
/*
1283
* 'format_log_line()' - Format a line for a log file.
1284
*
1285
* This function resizes a global string buffer as needed. Each call returns
1286
* a pointer to this buffer, so the contents are only good until the next call
1287
* to format_log_line()...
1288
*/
1289
1290
static int /* O - -1 for fatal, 0 for retry, 1 for success */
1291
format_log_line(const char *message, /* I - Printf-style format string */
1292
va_list ap) /* I - Argument list */
1293
{
1294
ssize_t len; /* Length of formatted line */
1295
1296
1297
/*
1298
* Allocate the line buffer as needed...
1299
*/
1300
1301
if (!log_linesize)
1302
{
1303
log_linesize = 8192;
1304
log_line = malloc(log_linesize);
1305
1306
if (!log_line)
1307
return (-1);
1308
}
1309
1310
/*
1311
* Format the log message...
1312
*/
1313
1314
len = _cups_safe_vsnprintf(log_line, log_linesize, message, ap);
1315
1316
/*
1317
* Resize the buffer as needed...
1318
*/
1319
1320
if ((size_t)len >= log_linesize && log_linesize < 65536)
1321
{
1322
char *temp; /* Temporary string pointer */
1323
1324
len ++;
1325
1326
if (len < 8192)
1327
len = 8192;
1328
else if (len > 65536)
1329
len = 65536;
1330
1331
temp = realloc(log_line, (size_t)len);
1332
1333
if (temp)
1334
{
1335
log_line = temp;
1336
log_linesize = (size_t)len;
1337
1338
return (0);
1339
}
1340
}
1341
1342
return (1);
1343
}
1344
1345