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 DLLExport 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 DLLExport 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 DLLExport 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 { 336 char *bufptr, /* Pointer to position in buffer */ 337 *bufend, /* Pointer to end of buffer */ 338 size, /* Size character (h, l, L) */ 339 type; /* Format type character */ 340 int width, /* Width of field */ 341 prec; /* Number of characters of precision */ 342 char tformat[100], /* Temporary format string for snprintf() */ 343 *tptr, /* Pointer into temporary format */ 344 temp[1024]; /* Buffer for formatted numbers */ 345 char *s; /* Pointer to string */ 346 ssize_t bytes; /* Total number of bytes needed */ 347 348 349 if (!buffer || bufsize < 2 || !format) 350 return (-1); 351 352 /* 353 * Loop through the format string, formatting as needed... 354 */ 355 356 bufptr = buffer; 357 bufend = buffer + bufsize - 1; 358 bytes = 0; 359 360 while (*format) 361 { 362 if (*format == '%') 363 { 364 tptr = tformat; 365 *tptr++ = *format++; 366 367 if (*format == '%') 368 { 369 if (bufptr < bufend) 370 *bufptr++ = *format; 371 bytes ++; 372 format ++; 373 continue; 374 } 375 else if (strchr(" -+#\'", *format)) 376 *tptr++ = *format++; 377 378 if (*format == '*') 379 { 380 /* 381 * Get width from argument... 382 */ 383 384 format ++; 385 width = va_arg(ap, int); 386 387 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width); 388 tptr += strlen(tptr); 389 } 390 else 391 { 392 width = 0; 393 394 while (isdigit(*format & 255)) 395 { 396 if (tptr < (tformat + sizeof(tformat) - 1)) 397 *tptr++ = *format; 398 399 width = width * 10 + *format++ - '0'; 400 } 401 } 402 403 if (*format == '.') 404 { 405 if (tptr < (tformat + sizeof(tformat) - 1)) 406 *tptr++ = *format; 407 408 format ++; 409 410 if (*format == '*') 411 { 412 /* 413 * Get precision from argument... 414 */ 415 416 format ++; 417 prec = va_arg(ap, int); 418 419 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec); 420 tptr += strlen(tptr); 421 } 422 else 423 { 424 prec = 0; 425 426 while (isdigit(*format & 255)) 427 { 428 if (tptr < (tformat + sizeof(tformat) - 1)) 429 *tptr++ = *format; 430 431 prec = prec * 10 + *format++ - '0'; 432 } 433 } 434 } 435 436 if (*format == 'l' && format[1] == 'l') 437 { 438 size = 'L'; 439 440 if (tptr < (tformat + sizeof(tformat) - 2)) 441 { 442 *tptr++ = 'l'; 443 *tptr++ = 'l'; 444 } 445 446 format += 2; 447 } 448 else if (*format == 'h' || *format == 'l' || *format == 'L') 449 { 450 if (tptr < (tformat + sizeof(tformat) - 1)) 451 *tptr++ = *format; 452 453 size = *format++; 454 } 455 else 456 size = 0; 457 458 if (!*format) 459 break; 460 461 if (tptr < (tformat + sizeof(tformat) - 1)) 462 *tptr++ = *format; 463 464 type = *format++; 465 *tptr = '\0'; 466 467 switch (type) 468 { 469 case 'E' : /* Floating point formats */ 470 case 'G' : 471 case 'e' : 472 case 'f' : 473 case 'g' : 474 if ((size_t)(width + 2) > sizeof(temp)) 475 break; 476 477 snprintf(temp, sizeof(temp), tformat, va_arg(ap, double)); 478 479 bytes += (int)strlen(temp); 480 481 if (bufptr) 482 { 483 strlcpy(bufptr, temp, (size_t)(bufend - bufptr)); 484 bufptr += strlen(bufptr); 485 } 486 break; 487 488 case 'B' : /* Integer formats */ 489 case 'X' : 490 case 'b' : 491 case 'd' : 492 case 'i' : 493 case 'o' : 494 case 'u' : 495 case 'x' : 496 if ((size_t)(width + 2) > sizeof(temp)) 497 break; 498 499 # ifdef HAVE_LONG_LONG 500 if (size == 'L') 501 snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long)); 502 else 503 # endif /* HAVE_LONG_LONG */ 504 if (size == 'l') 505 snprintf(temp, sizeof(temp), tformat, va_arg(ap, long)); 506 else 507 snprintf(temp, sizeof(temp), tformat, va_arg(ap, int)); 508 509 bytes += (int)strlen(temp); 510 511 if (bufptr) 512 { 513 strlcpy(bufptr, temp, (size_t)(bufend - bufptr)); 514 bufptr += strlen(bufptr); 515 } 516 break; 517 518 case 'p' : /* Pointer value */ 519 if ((size_t)(width + 2) > sizeof(temp)) 520 break; 521 522 snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *)); 523 524 bytes += (int)strlen(temp); 525 526 if (bufptr) 527 { 528 strlcpy(bufptr, temp, (size_t)(bufend - bufptr)); 529 bufptr += strlen(bufptr); 530 } 531 break; 532 533 case 'c' : /* Character or character array */ 534 bytes += width; 535 536 if (bufptr) 537 { 538 if (width <= 1) 539 *bufptr++ = (char)va_arg(ap, int); 540 else 541 { 542 if ((bufptr + width) > bufend) 543 width = (int)(bufend - bufptr); 544 545 memcpy(bufptr, va_arg(ap, char *), (size_t)width); 546 bufptr += width; 547 } 548 } 549 break; 550 551 case 's' : /* String */ 552 if ((s = va_arg(ap, char *)) == NULL) 553 s = "(null)"; 554 555 /* 556 * Copy the C string, replacing control chars and \ with 557 * C character escapes... 558 */ 559 560 for (bufend --; *s && bufptr < bufend; s ++) 561 { 562 if (*s == '\n') 563 { 564 *bufptr++ = '\\'; 565 *bufptr++ = 'n'; 566 bytes += 2; 567 } 568 else if (*s == '\r') 569 { 570 *bufptr++ = '\\'; 571 *bufptr++ = 'r'; 572 bytes += 2; 573 } 574 else if (*s == '\t') 575 { 576 *bufptr++ = '\\'; 577 *bufptr++ = 't'; 578 bytes += 2; 579 } 580 else if (*s == '\\') 581 { 582 *bufptr++ = '\\'; 583 *bufptr++ = '\\'; 584 bytes += 2; 585 } 586 else if (*s == '\'') 587 { 588 *bufptr++ = '\\'; 589 *bufptr++ = '\''; 590 bytes += 2; 591 } 592 else if (*s == '\"') 593 { 594 *bufptr++ = '\\'; 595 *bufptr++ = '\"'; 596 bytes += 2; 597 } 598 else if ((*s & 255) < ' ') 599 { 600 if ((bufptr + 2) >= bufend) 601 break; 602 603 *bufptr++ = '\\'; 604 *bufptr++ = '0'; 605 *bufptr++ = '0' + *s / 8; 606 *bufptr++ = '0' + (*s & 7); 607 bytes += 4; 608 } 609 else 610 { 611 *bufptr++ = *s; 612 bytes ++; 613 } 614 } 615 616 bufend ++; 617 break; 618 619 case 'n' : /* Output number of chars so far */ 620 *(va_arg(ap, int *)) = (int)bytes; 621 break; 622 } 623 } 624 else 625 { 626 bytes ++; 627 628 if (bufptr < bufend) 629 *bufptr++ = *format; 630 631 format ++; 632 } 633 } 634 635 /* 636 * Nul-terminate the string and return the number of characters needed. 637 */ 638 639 *bufptr = '\0'; 640 641 return (bytes); 642 } 643