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