pjdlog.c revision 225736
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: stable/9/sbin/hastd/pjdlog.c 222087 2011-05-18 22:43:56Z pjd $"); 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 56static int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED; 57static int pjdlog_mode, pjdlog_debug_level; 58static char pjdlog_prefix[128]; 59 60static int 61pjdlog_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 70static int 71pjdlog_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 86static int 87pjdlog_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 96static int 97pjdlog_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 148void 149pjdlog_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 176void 177pjdlog_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 */ 194void 195pjdlog_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 */ 215int 216pjdlog_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 */ 228void 229pjdlog_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 */ 241int 242pjdlog_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 */ 254void 255pjdlog_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 */ 270void 271pjdlogv_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 */ 283static const char * 284pjdlog_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 */ 312void 313pjdlog_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 */ 328void 329pjdlogv_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 */ 406void 407pjdlogv(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 */ 424void 425pjdlog(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 */ 439void 440pjdlogv_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 */ 451void 452pjdlog_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 */ 466void 467pjdlogv_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 */ 478void 479pjdlog_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 */ 493void 494pjdlogv_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 */ 507void 508pjdlog_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 */ 523void 524pjdlogv_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 */ 537void 538pjdlog_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 */ 553void 554pjdlog_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