pjdlog.c revision 293161
1/*- 2 * Copyright (c) 2009-2010 The FreeBSD Foundation 3 * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org> 4 * All rights reserved. 5 * 6 * This software was developed by Pawel Jakub Dawidek under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/types.h> 32#include <sys/socket.h> 33#include <netinet/in.h> 34#include <arpa/inet.h> 35 36#include <assert.h> 37#include <errno.h> 38#ifdef __FreeBSD__ 39#include <libutil.h> 40#include <printf.h> 41#endif 42#include <stdarg.h> 43#include <stdint.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47#include <syslog.h> 48#include <unistd.h> 49 50#include "pjdlog.h" 51 52#define PJDLOG_NEVER_INITIALIZED 0 53#define PJDLOG_NOT_INITIALIZED 1 54#define PJDLOG_INITIALIZED 2 55 56static int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED; 57static int pjdlog_mode, pjdlog_debug_level; 58static char pjdlog_prefix[128]; 59 60#ifdef __FreeBSD__ 61static int 62pjdlog_printf_arginfo_humanized_number(const struct printf_info *pi __unused, 63 size_t n, int *argt) 64{ 65 66 assert(n >= 1); 67 argt[0] = PA_INT | PA_FLAG_INTMAX; 68 return (1); 69} 70 71static int 72pjdlog_printf_render_humanized_number(struct __printf_io *io, 73 const struct printf_info *pi, const void * const *arg) 74{ 75 char buf[5]; 76 intmax_t num; 77 int ret; 78 79 num = *(const intmax_t *)arg[0]; 80 humanize_number(buf, sizeof(buf), (int64_t)num, "", HN_AUTOSCALE, 81 HN_NOSPACE | HN_DECIMAL); 82 ret = __printf_out(io, pi, buf, strlen(buf)); 83 __printf_flush(io); 84 return (ret); 85} 86 87static int 88pjdlog_printf_arginfo_sockaddr(const struct printf_info *pi __unused, 89 size_t n, int *argt) 90{ 91 92 assert(n >= 1); 93 argt[0] = PA_POINTER; 94 return (1); 95} 96 97static int 98pjdlog_printf_render_sockaddr(struct __printf_io *io, 99 const struct printf_info *pi, const void * const *arg) 100{ 101 const struct sockaddr_storage *ss; 102 char buf[64]; 103 int ret; 104 105 ss = *(const struct sockaddr_storage * const *)arg[0]; 106 switch (ss->ss_family) { 107 case AF_INET: 108 { 109 char addr[INET_ADDRSTRLEN]; 110 const struct sockaddr_in *sin; 111 unsigned int port; 112 113 sin = (const struct sockaddr_in *)ss; 114 port = ntohs(sin->sin_port); 115 if (inet_ntop(ss->ss_family, &sin->sin_addr, addr, 116 sizeof(addr)) == NULL) { 117 PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.", 118 strerror(errno)); 119 } 120 snprintf(buf, sizeof(buf), "%s:%u", addr, port); 121 break; 122 } 123 case AF_INET6: 124 { 125 char addr[INET6_ADDRSTRLEN]; 126 const struct sockaddr_in6 *sin; 127 unsigned int port; 128 129 sin = (const struct sockaddr_in6 *)ss; 130 port = ntohs(sin->sin6_port); 131 if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr, 132 sizeof(addr)) == NULL) { 133 PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.", 134 strerror(errno)); 135 } 136 snprintf(buf, sizeof(buf), "[%s]:%u", addr, port); 137 break; 138 } 139 default: 140 snprintf(buf, sizeof(buf), "[unsupported family %hhu]", 141 ss->ss_family); 142 break; 143 } 144 ret = __printf_out(io, pi, buf, strlen(buf)); 145 __printf_flush(io); 146 return (ret); 147} 148#endif /* __FreeBSD__ */ 149 150void 151pjdlog_init(int mode) 152{ 153 int saved_errno; 154 155 assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED || 156 pjdlog_initialized == PJDLOG_NOT_INITIALIZED); 157 assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG); 158 159 saved_errno = errno; 160 161 if (pjdlog_initialized == PJDLOG_NEVER_INITIALIZED) { 162#ifdef __FreeBSD__ 163 __use_xprintf = 1; 164 register_printf_render_std("T"); 165 register_printf_render('N', 166 pjdlog_printf_render_humanized_number, 167 pjdlog_printf_arginfo_humanized_number); 168 register_printf_render('S', 169 pjdlog_printf_render_sockaddr, 170 pjdlog_printf_arginfo_sockaddr); 171#endif 172 } 173 174 if (mode == PJDLOG_MODE_SYSLOG) 175 openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON); 176 pjdlog_mode = mode; 177 pjdlog_debug_level = 0; 178 bzero(pjdlog_prefix, sizeof(pjdlog_prefix)); 179 180 pjdlog_initialized = PJDLOG_INITIALIZED; 181 182 errno = saved_errno; 183} 184 185void 186pjdlog_fini(void) 187{ 188 int saved_errno; 189 190 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 191 192 saved_errno = errno; 193 194 if (pjdlog_mode == PJDLOG_MODE_SYSLOG) 195 closelog(); 196 197 pjdlog_initialized = PJDLOG_NOT_INITIALIZED; 198 199 errno = saved_errno; 200} 201 202/* 203 * Configure where the logs should go. 204 * By default they are send to stdout/stderr, but after going into background 205 * (eg. by calling daemon(3)) application is responsible for changing mode to 206 * PJDLOG_MODE_SYSLOG, so logs will be send to syslog. 207 */ 208void 209pjdlog_mode_set(int mode) 210{ 211 int saved_errno; 212 213 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 214 assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG); 215 216 if (pjdlog_mode == mode) 217 return; 218 219 saved_errno = errno; 220 221 if (mode == PJDLOG_MODE_SYSLOG) 222 openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON); 223 else /* if (mode == PJDLOG_MODE_STD) */ 224 closelog(); 225 226 pjdlog_mode = mode; 227 228 errno = saved_errno; 229} 230 231/* 232 * Return current mode. 233 */ 234int 235pjdlog_mode_get(void) 236{ 237 238 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 239 240 return (pjdlog_mode); 241} 242 243/* 244 * Set debug level. All the logs above the level specified here will be 245 * ignored. 246 */ 247void 248pjdlog_debug_set(int level) 249{ 250 251 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 252 assert(level >= 0); 253 254 pjdlog_debug_level = level; 255} 256 257/* 258 * Return current debug level. 259 */ 260int 261pjdlog_debug_get(void) 262{ 263 264 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 265 266 return (pjdlog_debug_level); 267} 268 269/* 270 * Set prefix that will be used before each log. 271 * Setting prefix to NULL will remove it. 272 */ 273void 274pjdlog_prefix_set(const char *fmt, ...) 275{ 276 va_list ap; 277 278 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 279 280 va_start(ap, fmt); 281 pjdlogv_prefix_set(fmt, ap); 282 va_end(ap); 283} 284 285/* 286 * Set prefix that will be used before each log. 287 * Setting prefix to NULL will remove it. 288 */ 289void 290pjdlogv_prefix_set(const char *fmt, va_list ap) 291{ 292 int saved_errno; 293 294 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 295 assert(fmt != NULL); 296 297 saved_errno = errno; 298 299 vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap); 300 301 errno = saved_errno; 302} 303 304/* 305 * Convert log level into string. 306 */ 307static const char * 308pjdlog_level_string(int loglevel) 309{ 310 311 switch (loglevel) { 312 case LOG_EMERG: 313 return ("EMERG"); 314 case LOG_ALERT: 315 return ("ALERT"); 316 case LOG_CRIT: 317 return ("CRIT"); 318 case LOG_ERR: 319 return ("ERROR"); 320 case LOG_WARNING: 321 return ("WARNING"); 322 case LOG_NOTICE: 323 return ("NOTICE"); 324 case LOG_INFO: 325 return ("INFO"); 326 case LOG_DEBUG: 327 return ("DEBUG"); 328 } 329 assert(!"Invalid log level."); 330 abort(); /* XXX: gcc */ 331} 332 333/* 334 * Common log routine. 335 */ 336void 337pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...) 338{ 339 va_list ap; 340 341 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 342 343 va_start(ap, fmt); 344 pjdlogv_common(loglevel, debuglevel, error, fmt, ap); 345 va_end(ap); 346} 347 348/* 349 * Common log routine, which can handle regular log level as well as debug 350 * level. We decide here where to send the logs (stdout/stderr or syslog). 351 */ 352void 353pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt, 354 va_list ap) 355{ 356 int saved_errno; 357 358 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 359 assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT || 360 loglevel == LOG_CRIT || loglevel == LOG_ERR || 361 loglevel == LOG_WARNING || loglevel == LOG_NOTICE || 362 loglevel == LOG_INFO || loglevel == LOG_DEBUG); 363 assert(loglevel != LOG_DEBUG || debuglevel > 0); 364 assert(error >= -1); 365 366 /* Ignore debug above configured level. */ 367 if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level) 368 return; 369 370 saved_errno = errno; 371 372 switch (pjdlog_mode) { 373 case PJDLOG_MODE_STD: 374 { 375 FILE *out; 376 377 /* 378 * We send errors and warning to stderr and the rest to stdout. 379 */ 380 switch (loglevel) { 381 case LOG_EMERG: 382 case LOG_ALERT: 383 case LOG_CRIT: 384 case LOG_ERR: 385 case LOG_WARNING: 386 out = stderr; 387 break; 388 case LOG_NOTICE: 389 case LOG_INFO: 390 case LOG_DEBUG: 391 out = stdout; 392 break; 393 default: 394 assert(!"Invalid loglevel."); 395 abort(); /* XXX: gcc */ 396 } 397 398 fprintf(out, "(%d) ", getpid()); 399 fprintf(out, "[%s]", pjdlog_level_string(loglevel)); 400 /* Attach debuglevel if this is debug log. */ 401 if (loglevel == LOG_DEBUG) 402 fprintf(out, "[%d]", debuglevel); 403 fprintf(out, " %s", pjdlog_prefix); 404 vfprintf(out, fmt, ap); 405 if (error != -1) 406 fprintf(out, ": %s.", strerror(error)); 407 fprintf(out, "\n"); 408 fflush(out); 409 break; 410 } 411 case PJDLOG_MODE_SYSLOG: 412 { 413 char log[1024]; 414 int len; 415 416 len = snprintf(log, sizeof(log), "%s", pjdlog_prefix); 417 if ((size_t)len < sizeof(log)) 418 len += vsnprintf(log + len, sizeof(log) - len, fmt, ap); 419 if (error != -1 && (size_t)len < sizeof(log)) { 420 (void)snprintf(log + len, sizeof(log) - len, ": %s.", 421 strerror(error)); 422 } 423 syslog(loglevel, "%s", log); 424 break; 425 } 426 default: 427 assert(!"Invalid mode."); 428 } 429 430 errno = saved_errno; 431} 432 433/* 434 * Regular logs. 435 */ 436void 437pjdlogv(int loglevel, const char *fmt, va_list ap) 438{ 439 440 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 441 442 /* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */ 443 assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT || 444 loglevel == LOG_CRIT || loglevel == LOG_ERR || 445 loglevel == LOG_WARNING || loglevel == LOG_NOTICE || 446 loglevel == LOG_INFO); 447 448 pjdlogv_common(loglevel, 0, -1, fmt, ap); 449} 450 451/* 452 * Regular logs. 453 */ 454void 455pjdlog(int loglevel, const char *fmt, ...) 456{ 457 va_list ap; 458 459 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 460 461 va_start(ap, fmt); 462 pjdlogv(loglevel, fmt, ap); 463 va_end(ap); 464} 465 466/* 467 * Debug logs. 468 */ 469void 470pjdlogv_debug(int debuglevel, const char *fmt, va_list ap) 471{ 472 473 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 474 475 pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap); 476} 477 478/* 479 * Debug logs. 480 */ 481void 482pjdlog_debug(int debuglevel, const char *fmt, ...) 483{ 484 va_list ap; 485 486 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 487 488 va_start(ap, fmt); 489 pjdlogv_debug(debuglevel, fmt, ap); 490 va_end(ap); 491} 492 493/* 494 * Error logs with errno logging. 495 */ 496void 497pjdlogv_errno(int loglevel, const char *fmt, va_list ap) 498{ 499 500 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 501 502 pjdlogv_common(loglevel, 0, errno, fmt, ap); 503} 504 505/* 506 * Error logs with errno logging. 507 */ 508void 509pjdlog_errno(int loglevel, const char *fmt, ...) 510{ 511 va_list ap; 512 513 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 514 515 va_start(ap, fmt); 516 pjdlogv_errno(loglevel, fmt, ap); 517 va_end(ap); 518} 519 520/* 521 * Log error, errno and exit. 522 */ 523void 524pjdlogv_exit(int exitcode, const char *fmt, va_list ap) 525{ 526 527 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 528 529 pjdlogv_errno(LOG_ERR, fmt, ap); 530 exit(exitcode); 531 /* NOTREACHED */ 532} 533 534/* 535 * Log error, errno and exit. 536 */ 537void 538pjdlog_exit(int exitcode, const char *fmt, ...) 539{ 540 va_list ap; 541 542 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 543 544 va_start(ap, fmt); 545 pjdlogv_exit(exitcode, fmt, ap); 546 /* NOTREACHED */ 547 va_end(ap); 548} 549 550/* 551 * Log error and exit. 552 */ 553void 554pjdlogv_exitx(int exitcode, const char *fmt, va_list ap) 555{ 556 557 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 558 559 pjdlogv(LOG_ERR, fmt, ap); 560 exit(exitcode); 561 /* NOTREACHED */ 562} 563 564/* 565 * Log error and exit. 566 */ 567void 568pjdlog_exitx(int exitcode, const char *fmt, ...) 569{ 570 va_list ap; 571 572 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 573 574 va_start(ap, fmt); 575 pjdlogv_exitx(exitcode, fmt, ap); 576 /* NOTREACHED */ 577 va_end(ap); 578} 579 580/* 581 * Log failure message and exit. 582 */ 583void 584pjdlog_abort(const char *func, const char *file, int line, 585 const char *failedexpr, const char *fmt, ...) 586{ 587 va_list ap; 588 589 assert(pjdlog_initialized == PJDLOG_INITIALIZED); 590 591 /* 592 * When there is no message we pass __func__ as 'fmt'. 593 * It would be cleaner to pass NULL or "", but gcc generates a warning 594 * for both of those. 595 */ 596 if (fmt != func) { 597 va_start(ap, fmt); 598 pjdlogv_critical(fmt, ap); 599 va_end(ap); 600 } 601 if (failedexpr == NULL) { 602 if (func == NULL) { 603 pjdlog_critical("Aborted at file %s, line %d.", file, 604 line); 605 } else { 606 pjdlog_critical("Aborted at function %s, file %s, line %d.", 607 func, file, line); 608 } 609 } else { 610 if (func == NULL) { 611 pjdlog_critical("Assertion failed: (%s), file %s, line %d.", 612 failedexpr, file, line); 613 } else { 614 pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.", 615 failedexpr, func, file, line); 616 } 617 } 618 abort(); 619} 620