Coverage Report

Created: 2026-10-28 08:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cups/cups/debug.c
Line
Count
Source
1
/*
2
 * Debugging functions for CUPS.
3
 *
4
 * Copyright 2008-2015 by Apple Inc.
5
 *
6
 * These coded instructions, statements, and computer programs are the
7
 * property of Apple Inc. and are protected by Federal copyright
8
 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
9
 * which should have been included with this file.  If this file is
10
 * missing or damaged, see the license at "http://www.cups.org/".
11
 *
12
 * This file is subject to the Apple OS-Developed Software exception.
13
 */
14
15
/*
16
 * Include necessary headers...
17
 */
18
19
#include "cups-private.h"
20
#include "thread-private.h"
21
#ifdef _WIN32
22
#  include <sys/timeb.h>
23
#  include <time.h>
24
#  include <io.h>
25
#  define getpid (int)GetCurrentProcessId
26
int         /* O  - 0 on success, -1 on failure */
27
_cups_gettimeofday(struct timeval *tv,  /* I  - Timeval struct */
28
                   void     *tz)  /* I  - Timezone */
29
{
30
  struct _timeb timebuffer;   /* Time buffer struct */
31
  _ftime(&timebuffer);
32
  tv->tv_sec  = (long)timebuffer.time;
33
  tv->tv_usec = timebuffer.millitm * 1000;
34
  return 0;
35
}
36
#else
37
#  include <sys/time.h>
38
#  include <unistd.h>
39
#endif /* _WIN32 */
40
#include <regex.h>
41
#include <fcntl.h>
42
43
44
/*
45
 * Globals...
46
 */
47
48
int     _cups_debug_fd = -1;
49
          /* Debug log file descriptor */
50
int     _cups_debug_level = 1;
51
          /* Log level (0 to 9) */
52
53
54
#ifdef DEBUG
55
/*
56
 * Local globals...
57
 */
58
59
static regex_t    *debug_filter = NULL;
60
          /* Filter expression for messages */
61
static int    debug_init = 0; /* Did we initialize debugging? */
62
static _cups_mutex_t  debug_init_mutex = _CUPS_MUTEX_INITIALIZER,
63
          /* Mutex to control initialization */
64
      debug_log_mutex = _CUPS_MUTEX_INITIALIZER;
65
          /* Mutex to serialize log entries */
66
67
68
/*
69
 * 'debug_thread_id()' - Return an integer representing the current thread.
70
 */
71
72
static int        /* O - Local thread ID */
73
debug_thread_id(void)
74
{
75
  _cups_globals_t *cg = _cupsGlobals(); /* Global data */
76
77
78
  return (cg->thread_id);
79
}
80
81
82
/*
83
 * '_cups_debug_printf()' - Write a formatted line to the log.
84
 */
85
86
void
87
_cups_debug_printf(const char *format,  /* I - Printf-style format string */
88
                   ...)     /* I - Additional arguments as needed */
89
{
90
  va_list   ap;   /* Pointer to arguments */
91
  struct timeval  curtime;  /* Current time */
92
  char      buffer[2048]; /* Output buffer */
93
  ssize_t   bytes;    /* Number of bytes in buffer */
94
  int     level;    /* Log level in message */
95
96
97
 /*
98
  * See if we need to do any logging...
99
  */
100
101
  if (!debug_init)
102
    _cups_debug_set(getenv("CUPS_DEBUG_LOG"), getenv("CUPS_DEBUG_LEVEL"),
103
                    getenv("CUPS_DEBUG_FILTER"), 0);
104
105
  if (_cups_debug_fd < 0)
106
    return;
107
108
 /*
109
  * Filter as needed...
110
  */
111
112
  if (isdigit(format[0]))
113
    level = *format++ - '0';
114
  else
115
    level = 0;
116
117
  if (level > _cups_debug_level)
118
    return;
119
120
  if (debug_filter)
121
  {
122
    int result;       /* Filter result */
123
124
    _cupsMutexLock(&debug_init_mutex);
125
    result = regexec(debug_filter, format, 0, NULL, 0);
126
    _cupsMutexUnlock(&debug_init_mutex);
127
128
    if (result)
129
      return;
130
  }
131
132
 /*
133
  * Format the message...
134
  */
135
136
  gettimeofday(&curtime, NULL);
137
  snprintf(buffer, sizeof(buffer), "T%03d %02d:%02d:%02d.%03d  ",
138
           debug_thread_id(), (int)((curtime.tv_sec / 3600) % 24),
139
     (int)((curtime.tv_sec / 60) % 60),
140
     (int)(curtime.tv_sec % 60), (int)(curtime.tv_usec / 1000));
141
142
  va_start(ap, format);
143
  bytes = _cups_safe_vsnprintf(buffer + 19, sizeof(buffer) - 20, format, ap) + 19;
144
  va_end(ap);
145
146
  if ((size_t)bytes >= (sizeof(buffer) - 1))
147
  {
148
    buffer[sizeof(buffer) - 2] = '\n';
149
    bytes = sizeof(buffer) - 1;
150
  }
151
  else if (buffer[bytes - 1] != '\n')
152
  {
153
    buffer[bytes++] = '\n';
154
    buffer[bytes]   = '\0';
155
  }
156
157
 /*
158
  * Write it out...
159
  */
160
161
  _cupsMutexLock(&debug_log_mutex);
162
  write(_cups_debug_fd, buffer, (size_t)bytes);
163
  _cupsMutexUnlock(&debug_log_mutex);
164
}
165
166
167
/*
168
 * '_cups_debug_puts()' - Write a single line to the log.
169
 */
170
171
void
172
_cups_debug_puts(const char *s)   /* I - String to output */
173
{
174
  struct timeval  curtime;  /* Current time */
175
  char      buffer[2048]; /* Output buffer */
176
  ssize_t   bytes;    /* Number of bytes in buffer */
177
  int     level;    /* Log level in message */
178
179
180
 /*
181
  * See if we need to do any logging...
182
  */
183
184
  if (!debug_init)
185
    _cups_debug_set(getenv("CUPS_DEBUG_LOG"), getenv("CUPS_DEBUG_LEVEL"),
186
                    getenv("CUPS_DEBUG_FILTER"), 0);
187
188
  if (_cups_debug_fd < 0)
189
    return;
190
191
 /*
192
  * Filter as needed...
193
  */
194
195
  if (isdigit(s[0]))
196
    level = *s++ - '0';
197
  else
198
    level = 0;
199
200
  if (level > _cups_debug_level)
201
    return;
202
203
  if (debug_filter)
204
  {
205
    int result;       /* Filter result */
206
207
    _cupsMutexLock(&debug_init_mutex);
208
    result = regexec(debug_filter, s, 0, NULL, 0);
209
    _cupsMutexUnlock(&debug_init_mutex);
210
211
    if (result)
212
      return;
213
  }
214
215
 /*
216
  * Format the message...
217
  */
218
219
  gettimeofday(&curtime, NULL);
220
  bytes = snprintf(buffer, sizeof(buffer), "T%03d %02d:%02d:%02d.%03d  %s",
221
                   debug_thread_id(), (int)((curtime.tv_sec / 3600) % 24),
222
       (int)((curtime.tv_sec / 60) % 60),
223
       (int)(curtime.tv_sec % 60), (int)(curtime.tv_usec / 1000),
224
       s);
225
226
  if ((size_t)bytes >= (sizeof(buffer) - 1))
227
  {
228
    buffer[sizeof(buffer) - 2] = '\n';
229
    bytes = sizeof(buffer) - 1;
230
  }
231
  else if (buffer[bytes - 1] != '\n')
232
  {
233
    buffer[bytes++] = '\n';
234
    buffer[bytes]   = '\0';
235
  }
236
237
 /*
238
  * Write it out...
239
  */
240
241
  _cupsMutexLock(&debug_log_mutex);
242
  write(_cups_debug_fd, buffer, (size_t)bytes);
243
  _cupsMutexUnlock(&debug_log_mutex);
244
}
245
246
247
/*
248
 * '_cups_debug_set()' - Enable or disable debug logging.
249
 */
250
251
void
252
_cups_debug_set(const char *logfile,  /* I - Log file or NULL */
253
                const char *level,  /* I - Log level or NULL */
254
    const char *filter, /* I - Filter string or NULL */
255
    int        force) /* I - Force initialization */
256
{
257
  _cupsMutexLock(&debug_init_mutex);
258
259
  if (!debug_init || force)
260
  {
261
   /*
262
    * Restore debug settings to defaults...
263
    */
264
265
    if (_cups_debug_fd != -1)
266
    {
267
      close(_cups_debug_fd);
268
      _cups_debug_fd = -1;
269
    }
270
271
    if (debug_filter)
272
    {
273
      regfree((regex_t *)debug_filter);
274
      debug_filter = NULL;
275
    }
276
277
    _cups_debug_level = 1;
278
279
   /*
280
    * Open logs, set log levels, etc.
281
    */
282
283
    if (!logfile)
284
      _cups_debug_fd = -1;
285
    else if (!strcmp(logfile, "-"))
286
      _cups_debug_fd = 2;
287
    else
288
    {
289
      char  buffer[1024];   /* Filename buffer */
290
291
      snprintf(buffer, sizeof(buffer), logfile, getpid());
292
293
      if (buffer[0] == '+')
294
  _cups_debug_fd = open(buffer + 1, O_WRONLY | O_APPEND | O_CREAT, 0644);
295
      else
296
  _cups_debug_fd = open(buffer, O_WRONLY | O_TRUNC | O_CREAT, 0644);
297
    }
298
299
    if (level)
300
      _cups_debug_level = atoi(level);
301
302
    if (filter)
303
    {
304
      if ((debug_filter = (regex_t *)calloc(1, sizeof(regex_t))) == NULL)
305
  fputs("Unable to allocate memory for CUPS_DEBUG_FILTER - results not "
306
        "filtered!\n", stderr);
307
      else if (regcomp(debug_filter, filter, REG_EXTENDED))
308
      {
309
  fputs("Bad regular expression in CUPS_DEBUG_FILTER - results not "
310
        "filtered!\n", stderr);
311
  free(debug_filter);
312
  debug_filter = NULL;
313
      }
314
    }
315
316
    debug_init = 1;
317
  }
318
319
  _cupsMutexUnlock(&debug_init_mutex);
320
}
321
#endif /* DEBUG */
322
323
324
/*
325
 * '_cups_safe_vsnprintf()' - Format a string into a fixed size buffer,
326
 *                            quoting special characters.
327
 */
328
329
ssize_t         /* O - Number of bytes formatted */
330
_cups_safe_vsnprintf(
331
    char       *buffer,     /* O - Output buffer */
332
    size_t     bufsize,     /* O - Size of output buffer */
333
    const char *format,     /* I - printf-style format string */
334
    va_list    ap)      /* I - Pointer to additional arguments */
335
0
{
336
0
  char    *bufptr,    /* Pointer to position in buffer */
337
0
    *bufend,    /* Pointer to end of buffer */
338
0
    size,     /* Size character (h, l, L) */
339
0
    type;     /* Format type character */
340
0
  int   width,      /* Width of field */
341
0
    prec;     /* Number of characters of precision */
342
0
  char    tformat[100],   /* Temporary format string for snprintf() */
343
0
    *tptr,      /* Pointer into temporary format */
344
0
    temp[1024];   /* Buffer for formatted numbers */
345
0
  char    *s;     /* Pointer to string */
346
0
  ssize_t bytes;      /* Total number of bytes needed */
347
348
349
0
  if (!buffer || bufsize < 2 || !format)
350
0
    return (-1);
351
352
 /*
353
  * Loop through the format string, formatting as needed...
354
  */
355
356
0
  bufptr = buffer;
357
0
  bufend = buffer + bufsize - 1;
358
0
  bytes  = 0;
359
360
0
  while (*format)
361
0
  {
362
0
    if (*format == '%')
363
0
    {
364
0
      tptr = tformat;
365
0
      *tptr++ = *format++;
366
367
0
      if (*format == '%')
368
0
      {
369
0
        if (bufptr < bufend)
370
0
    *bufptr++ = *format;
371
0
        bytes ++;
372
0
        format ++;
373
0
  continue;
374
0
      }
375
0
      else if (strchr(" -+#\'", *format))
376
0
        *tptr++ = *format++;
377
378
0
      if (*format == '*')
379
0
      {
380
       /*
381
        * Get width from argument...
382
  */
383
384
0
  format ++;
385
0
  width = va_arg(ap, int);
386
387
0
  snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
388
0
  tptr += strlen(tptr);
389
0
      }
390
0
      else
391
0
      {
392
0
  width = 0;
393
394
0
  while (isdigit(*format & 255))
395
0
  {
396
0
    if (tptr < (tformat + sizeof(tformat) - 1))
397
0
      *tptr++ = *format;
398
399
0
    width = width * 10 + *format++ - '0';
400
0
  }
401
0
      }
402
403
0
      if (*format == '.')
404
0
      {
405
0
  if (tptr < (tformat + sizeof(tformat) - 1))
406
0
    *tptr++ = *format;
407
408
0
        format ++;
409
410
0
        if (*format == '*')
411
0
  {
412
         /*
413
    * Get precision from argument...
414
    */
415
416
0
    format ++;
417
0
    prec = va_arg(ap, int);
418
419
0
    snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
420
0
    tptr += strlen(tptr);
421
0
  }
422
0
  else
423
0
  {
424
0
    prec = 0;
425
426
0
    while (isdigit(*format & 255))
427
0
    {
428
0
      if (tptr < (tformat + sizeof(tformat) - 1))
429
0
        *tptr++ = *format;
430
431
0
      prec = prec * 10 + *format++ - '0';
432
0
    }
433
0
  }
434
0
      }
435
436
0
      if (*format == 'l' && format[1] == 'l')
437
0
      {
438
0
        size = 'L';
439
440
0
  if (tptr < (tformat + sizeof(tformat) - 2))
441
0
  {
442
0
    *tptr++ = 'l';
443
0
    *tptr++ = 'l';
444
0
  }
445
446
0
  format += 2;
447
0
      }
448
0
      else if (*format == 'h' || *format == 'l' || *format == 'L')
449
0
      {
450
0
  if (tptr < (tformat + sizeof(tformat) - 1))
451
0
    *tptr++ = *format;
452
453
0
        size = *format++;
454
0
      }
455
0
      else
456
0
        size = 0;
457
458
0
      if (!*format)
459
0
        break;
460
461
0
      if (tptr < (tformat + sizeof(tformat) - 1))
462
0
        *tptr++ = *format;
463
464
0
      type  = *format++;
465
0
      *tptr = '\0';
466
467
0
      switch (type)
468
0
      {
469
0
  case 'E' : /* Floating point formats */
470
0
  case 'G' :
471
0
  case 'e' :
472
0
  case 'f' :
473
0
  case 'g' :
474
0
      if ((size_t)(width + 2) > sizeof(temp))
475
0
        break;
476
477
0
      snprintf(temp, sizeof(temp), tformat, va_arg(ap, double));
478
479
0
            bytes += (int)strlen(temp);
480
481
0
            if (bufptr)
482
0
      {
483
0
        strlcpy(bufptr, temp, (size_t)(bufend - bufptr));
484
0
        bufptr += strlen(bufptr);
485
0
      }
486
0
      break;
487
488
0
        case 'B' : /* Integer formats */
489
0
  case 'X' :
490
0
  case 'b' :
491
0
        case 'd' :
492
0
  case 'i' :
493
0
  case 'o' :
494
0
  case 'u' :
495
0
  case 'x' :
496
0
      if ((size_t)(width + 2) > sizeof(temp))
497
0
        break;
498
499
0
#  ifdef HAVE_LONG_LONG
500
0
            if (size == 'L')
501
0
        snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long));
502
0
      else
503
0
#  endif /* HAVE_LONG_LONG */
504
0
            if (size == 'l')
505
0
        snprintf(temp, sizeof(temp), tformat, va_arg(ap, long));
506
0
      else
507
0
        snprintf(temp, sizeof(temp), tformat, va_arg(ap, int));
508
509
0
            bytes += (int)strlen(temp);
510
511
0
      if (bufptr)
512
0
      {
513
0
        strlcpy(bufptr, temp, (size_t)(bufend - bufptr));
514
0
        bufptr += strlen(bufptr);
515
0
      }
516
0
      break;
517
518
0
  case 'p' : /* Pointer value */
519
0
      if ((size_t)(width + 2) > sizeof(temp))
520
0
        break;
521
522
0
      snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *));
523
524
0
            bytes += (int)strlen(temp);
525
526
0
      if (bufptr)
527
0
      {
528
0
        strlcpy(bufptr, temp, (size_t)(bufend - bufptr));
529
0
        bufptr += strlen(bufptr);
530
0
      }
531
0
      break;
532
533
0
        case 'c' : /* Character or character array */
534
0
      bytes += width;
535
536
0
      if (bufptr)
537
0
      {
538
0
        if (width <= 1)
539
0
          *bufptr++ = (char)va_arg(ap, int);
540
0
        else
541
0
        {
542
0
    if ((bufptr + width) > bufend)
543
0
      width = (int)(bufend - bufptr);
544
545
0
    memcpy(bufptr, va_arg(ap, char *), (size_t)width);
546
0
    bufptr += width;
547
0
        }
548
0
      }
549
0
      break;
550
551
0
  case 's' : /* String */
552
0
      if ((s = va_arg(ap, char *)) == NULL)
553
0
        s = "(null)";
554
555
           /*
556
      * Copy the C string, replacing control chars and \ with
557
      * C character escapes...
558
      */
559
560
0
            for (bufend --; *s && bufptr < bufend; s ++)
561
0
      {
562
0
        if (*s == '\n')
563
0
        {
564
0
          *bufptr++ = '\\';
565
0
    *bufptr++ = 'n';
566
0
    bytes += 2;
567
0
        }
568
0
        else if (*s == '\r')
569
0
        {
570
0
          *bufptr++ = '\\';
571
0
    *bufptr++ = 'r';
572
0
    bytes += 2;
573
0
        }
574
0
        else if (*s == '\t')
575
0
        {
576
0
          *bufptr++ = '\\';
577
0
    *bufptr++ = 't';
578
0
    bytes += 2;
579
0
        }
580
0
        else if (*s == '\\')
581
0
        {
582
0
          *bufptr++ = '\\';
583
0
    *bufptr++ = '\\';
584
0
    bytes += 2;
585
0
        }
586
0
        else if (*s == '\'')
587
0
        {
588
0
          *bufptr++ = '\\';
589
0
    *bufptr++ = '\'';
590
0
    bytes += 2;
591
0
        }
592
0
        else if (*s == '\"')
593
0
        {
594
0
          *bufptr++ = '\\';
595
0
    *bufptr++ = '\"';
596
0
    bytes += 2;
597
0
        }
598
0
        else if ((*s & 255) < ' ')
599
0
        {
600
0
          if ((bufptr + 2) >= bufend)
601
0
            break;
602
603
0
          *bufptr++ = '\\';
604
0
    *bufptr++ = '0';
605
0
    *bufptr++ = '0' + *s / 8;
606
0
    *bufptr++ = '0' + (*s & 7);
607
0
    bytes += 4;
608
0
        }
609
0
        else
610
0
        {
611
0
          *bufptr++ = *s;
612
0
    bytes ++;
613
0
        }
614
0
            }
615
616
0
            bufend ++;
617
0
      break;
618
619
0
  case 'n' : /* Output number of chars so far */
620
0
      *(va_arg(ap, int *)) = (int)bytes;
621
0
      break;
622
0
      }
623
0
    }
624
0
    else
625
0
    {
626
0
      bytes ++;
627
628
0
      if (bufptr < bufend)
629
0
        *bufptr++ = *format;
630
631
0
      format ++;
632
0
    }
633
0
  }
634
635
 /*
636
  * Nul-terminate the string and return the number of characters needed.
637
  */
638
639
0
  *bufptr = '\0';
640
641
0
  return (bytes);
642
0
}