pjdlog.c revision 222087
1224110Sjchandra/*- 2224110Sjchandra * Copyright (c) 2009-2010 The FreeBSD Foundation 3224110Sjchandra * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org> 4224110Sjchandra * All rights reserved. 5224110Sjchandra * 6224110Sjchandra * This software was developed by Pawel Jakub Dawidek under sponsorship from 7224110Sjchandra * the FreeBSD Foundation. 8224110Sjchandra * 9224110Sjchandra * Redistribution and use in source and binary forms, with or without 10224110Sjchandra * modification, are permitted provided that the following conditions 11224110Sjchandra * are met: 12224110Sjchandra * 1. Redistributions of source code must retain the above copyright 13224110Sjchandra * notice, this list of conditions and the following disclaimer. 14224110Sjchandra * 2. Redistributions in binary form must reproduce the above copyright 15224110Sjchandra * notice, this list of conditions and the following disclaimer in the 16224110Sjchandra * documentation and/or other materials provided with the distribution. 17224110Sjchandra * 18224110Sjchandra * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19224110Sjchandra * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20224110Sjchandra * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21224110Sjchandra * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22224110Sjchandra * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23224110Sjchandra * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24224110Sjchandra * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25224110Sjchandra * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26224110Sjchandra * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27224110Sjchandra * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28225394Sjchandra * SUCH DAMAGE. 29224110Sjchandra */ 30225394Sjchandra 31224110Sjchandra#include <sys/cdefs.h> 32225394Sjchandra__FBSDID("$FreeBSD: head/sbin/hastd/pjdlog.c 222087 2011-05-18 22:43:56Z pjd $"); 33227783Sjchandra 34224110Sjchandra#include <sys/types.h> 35224110Sjchandra#include <sys/socket.h> 36227783Sjchandra#include <netinet/in.h> 37224110Sjchandra#include <arpa/inet.h> 38225394Sjchandra 39227783Sjchandra#include <assert.h> 40227783Sjchandra#include <errno.h> 41227783Sjchandra#include <libutil.h> 42227783Sjchandra#include <printf.h> 43227783Sjchandra#include <stdarg.h> 44227783Sjchandra#include <stdint.h> 45227783Sjchandra#include <stdio.h> 46227783Sjchandra#include <stdlib.h> 47227783Sjchandra#include <string.h> 48224110Sjchandra#include <syslog.h> 49225394Sjchandra 50227783Sjchandra#include "pjdlog.h" 51227783Sjchandra 52227783Sjchandra#define PJDLOG_NEVER_INITIALIZED 0 53227783Sjchandra#define PJDLOG_NOT_INITIALIZED 1 54227783Sjchandra#define PJDLOG_INITIALIZED 2 55224110Sjchandra 56225394Sjchandrastatic int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED; 57227783Sjchandrastatic int pjdlog_mode, pjdlog_debug_level; 58227783Sjchandrastatic char pjdlog_prefix[128]; 59227783Sjchandra 60227783Sjchandrastatic int 61224110Sjchandrapjdlog_printf_arginfo_humanized_number(const struct printf_info *pi __unused, 62225394Sjchandra size_t n, int *argt) 63227783Sjchandra{ 64227783Sjchandra 65227783Sjchandra assert(n >= 1); 66227783Sjchandra argt[0] = PA_INT | PA_FLAG_INTMAX; 67227783Sjchandra return (1); 68227783Sjchandra} 69227783Sjchandra 70224110Sjchandrastatic int 71227783Sjchandrapjdlog_printf_render_humanized_number(struct __printf_io *io, 72227783Sjchandra const struct printf_info *pi, const void * const *arg) 73227783Sjchandra{ 74227783Sjchandra char buf[5]; 75227783Sjchandra intmax_t num; 76227783Sjchandra int ret; 77227783Sjchandra 78227783Sjchandra num = *(const intmax_t *)arg[0]; 79227783Sjchandra humanize_number(buf, sizeof(buf), (int64_t)num, "", HN_AUTOSCALE, 80227783Sjchandra HN_NOSPACE | HN_DECIMAL); 81227783Sjchandra ret = __printf_out(io, pi, buf, strlen(buf)); 82224110Sjchandra __printf_flush(io); 83227783Sjchandra return (ret); 84227783Sjchandra} 85227783Sjchandra 86227783Sjchandrastatic int 87227783Sjchandrapjdlog_printf_arginfo_sockaddr(const struct printf_info *pi __unused, 88227783Sjchandra size_t n, int *argt) 89227783Sjchandra{ 90224110Sjchandra 91227783Sjchandra assert(n >= 1); 92227783Sjchandra argt[0] = PA_POINTER; 93227783Sjchandra return (1); 94227783Sjchandra} 95227783Sjchandra 96227783Sjchandrastatic int 97227783Sjchandrapjdlog_printf_render_sockaddr(struct __printf_io *io, 98224110Sjchandra const struct printf_info *pi, const void * const *arg) 99227783Sjchandra{ 100227783Sjchandra const struct sockaddr_storage *ss; 101227783Sjchandra char buf[64]; 102227783Sjchandra int ret; 103227783Sjchandra 104227783Sjchandra ss = *(const struct sockaddr_storage * const *)arg[0]; 105227783Sjchandra switch (ss->ss_family) { 106224110Sjchandra case AF_INET: 107227783Sjchandra { 108227783Sjchandra char addr[INET_ADDRSTRLEN]; 109227783Sjchandra const struct sockaddr_in *sin; 110227783Sjchandra unsigned int port; 111227783Sjchandra 112227783Sjchandra sin = (const struct sockaddr_in *)ss; 113227783Sjchandra port = ntohs(sin->sin_port); 114227783Sjchandra if (inet_ntop(ss->ss_family, &sin->sin_addr, addr, 115227783Sjchandra sizeof(addr)) == NULL) { 116224110Sjchandra PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.", 117227783Sjchandra strerror(errno)); 118227783Sjchandra } 119227783Sjchandra snprintf(buf, sizeof(buf), "%s:%u", addr, port); 120227783Sjchandra break; 121227783Sjchandra } 122227783Sjchandra case AF_INET6: 123227783Sjchandra { 124227783Sjchandra char addr[INET6_ADDRSTRLEN]; 125227783Sjchandra const struct sockaddr_in6 *sin; 126224110Sjchandra unsigned int port; 127227783Sjchandra 128227783Sjchandra sin = (const struct sockaddr_in6 *)ss; 129227783Sjchandra port = ntohs(sin->sin6_port); 130227783Sjchandra if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr, 131227783Sjchandra sizeof(addr)) == NULL) { 132227783Sjchandra PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.", 133227783Sjchandra strerror(errno)); 134227783Sjchandra } 135227783Sjchandra snprintf(buf, sizeof(buf), "[%s]:%u", addr, port); 136225394Sjchandra break; 137227783Sjchandra } 138227783Sjchandra default: 139227783Sjchandra snprintf(buf, sizeof(buf), "[unsupported family %hhu]", 140227783Sjchandra ss->ss_family); 141227783Sjchandra break; 142227783Sjchandra } 143227783Sjchandra ret = __printf_out(io, pi, buf, strlen(buf)); 144227783Sjchandra __printf_flush(io); 145227783Sjchandra return (ret); 146225394Sjchandra} 147227783Sjchandra 148227783Sjchandravoid 149225394Sjchandrapjdlog_init(int mode) 150227783Sjchandra{ 151225394Sjchandra 152225394Sjchandra assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED || 153225394Sjchandra pjdlog_initialized == PJDLOG_NOT_INITIALIZED); 154225394Sjchandra assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG); 155227783Sjchandra 156227783Sjchandra if (pjdlog_initialized == PJDLOG_NEVER_INITIALIZED) { 157227783Sjchandra __use_xprintf = 1; 158227783Sjchandra register_printf_render_std("T"); 159227783Sjchandra register_printf_render('N', 160227783Sjchandra pjdlog_printf_render_humanized_number, 161227783Sjchandra pjdlog_printf_arginfo_humanized_number); 162227783Sjchandra register_printf_render('S', 163227783Sjchandra pjdlog_printf_render_sockaddr, 164227783Sjchandra pjdlog_printf_arginfo_sockaddr); 165227783Sjchandra } 166227783Sjchandra 167227783Sjchandra if (mode == PJDLOG_MODE_SYSLOG) 168227783Sjchandra openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON); 169225394Sjchandra pjdlog_mode = mode; 170227783Sjchandra pjdlog_debug_level = 0; 171225394Sjchandra bzero(pjdlog_prefix, sizeof(pjdlog_prefix)); 172225394Sjchandra 173224110Sjchandra pjdlog_initialized = PJDLOG_INITIALIZED; 174225394Sjchandra} 175225394Sjchandra 176225394Sjchandravoid 177227783Sjchandrapjdlog_fini(void) 178227783Sjchandra{ 179227783Sjchandra 180224110Sjchandra assert(pjdlog_initialized == PJDLOG_INITIALIZED); 181227783Sjchandra 182227783Sjchandra if (pjdlog_mode == PJDLOG_MODE_SYSLOG) 183227783Sjchandra closelog(); 184227783Sjchandra 185224110Sjchandra pjdlog_initialized = PJDLOG_NOT_INITIALIZED; 186224110Sjchandra} 187225394Sjchandra 188225394Sjchandra/* 189224110Sjchandra * Configure where the logs should go. 190225394Sjchandra * By default they are send to stdout/stderr, but after going into background 191224110Sjchandra * (eg. by calling daemon(3)) application is responsible for changing mode to 192224110Sjchandra * PJDLOG_MODE_SYSLOG, so logs will be send to syslog. 193225394Sjchandra */ 194225394Sjchandravoid 195224110Sjchandrapjdlog_mode_set(int mode) 196225394Sjchandra{ 197225394Sjchandra 198224110Sjchandra assert(pjdlog_initialized == PJDLOG_INITIALIZED); 199225394Sjchandra assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG); 200225394Sjchandra 201225394Sjchandra if (pjdlog_mode == mode) 202225394Sjchandra return; 203225394Sjchandra 204225394Sjchandra if (mode == PJDLOG_MODE_SYSLOG) 205224110Sjchandra openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON); 206225394Sjchandra else /* if (mode == PJDLOG_MODE_STD) */ 207224110Sjchandra closelog(); 208224110Sjchandra 209225394Sjchandra pjdlog_mode = mode; 210225394Sjchandra} 211224110Sjchandra 212225394Sjchandra/* 213224110Sjchandra * Return current mode. 214224110Sjchandra */ 215225394Sjchandraint 216225394Sjchandrapjdlog_mode_get(void) 217224110Sjchandra{ 218225394Sjchandra 219224110Sjchandra assert(pjdlog_initialized == PJDLOG_INITIALIZED); 220224110Sjchandra 221225394Sjchandra return (pjdlog_mode); 222225394Sjchandra} 223224110Sjchandra 224225394Sjchandra/* 225224110Sjchandra * Set debug level. All the logs above the level specified here will be 226225394Sjchandra * ignored. 227225394Sjchandra */ 228224110Sjchandravoid 229224110Sjchandrapjdlog_debug_set(int level) 230225394Sjchandra{ 231225394Sjchandra 232224110Sjchandra assert(pjdlog_initialized == PJDLOG_INITIALIZED); 233225394Sjchandra assert(level >= 0); 234224110Sjchandra 235225394Sjchandra pjdlog_debug_level = level; 236225394Sjchandra} 237225394Sjchandra 238224110Sjchandra/* 239224110Sjchandra * Return current debug level. 240225394Sjchandra */ 241225394Sjchandraint 242224110Sjchandrapjdlog_debug_get(void) 243225394Sjchandra{ 244224110Sjchandra 245225394Sjchandra assert(pjdlog_initialized == PJDLOG_INITIALIZED); 246225394Sjchandra 247225394Sjchandra return (pjdlog_debug_level); 248225394Sjchandra} 249225394Sjchandra 250224110Sjchandra/* 251224110Sjchandra * Set prefix that will be used before each log. 252225394Sjchandra * Setting prefix to NULL will remove it. 253225394Sjchandra */ 254225394Sjchandravoid 255224110Sjchandrapjdlog_prefix_set(const char *fmt, ...) 256225394Sjchandra{ 257224110Sjchandra va_list ap; 258225394Sjchandra 259225394Sjchandra assert(pjdlog_initialized == PJDLOG_INITIALIZED); 260225394Sjchandra 261225394Sjchandra va_start(ap, fmt); 262224110Sjchandra pjdlogv_prefix_set(fmt, ap); 263225394Sjchandra va_end(ap); 264224110Sjchandra} 265224110Sjchandra 266225394Sjchandra/* 267225394Sjchandra * Set prefix that will be used before each log. 268225394Sjchandra * Setting prefix to NULL will remove it. 269224110Sjchandra */ 270225394Sjchandravoid 271225394Sjchandrapjdlogv_prefix_set(const char *fmt, va_list ap) 272225394Sjchandra{ 273224110Sjchandra 274224110Sjchandra assert(pjdlog_initialized == PJDLOG_INITIALIZED); 275225394Sjchandra assert(fmt != NULL); 276225394Sjchandra 277225394Sjchandra vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap); 278225394Sjchandra} 279225394Sjchandra 280224110Sjchandra/* 281225394Sjchandra * Convert log level into string. 282225394Sjchandra */ 283224110Sjchandrastatic const char * 284225394Sjchandrapjdlog_level_string(int loglevel) 285224110Sjchandra{ 286224110Sjchandra 287225394Sjchandra switch (loglevel) { 288225394Sjchandra case LOG_EMERG: 289224110Sjchandra return ("EMERG"); 290227783Sjchandra case LOG_ALERT: 291227783Sjchandra return ("ALERT"); 292224110Sjchandra case LOG_CRIT: 293227783Sjchandra return ("CRIT"); 294227783Sjchandra case LOG_ERR: 295227783Sjchandra return ("ERROR"); 296227783Sjchandra case LOG_WARNING: 297227783Sjchandra return ("WARNING"); 298227783Sjchandra case LOG_NOTICE: 299227783Sjchandra return ("NOTICE"); 300225394Sjchandra case LOG_INFO: 301225394Sjchandra return ("INFO"); 302227783Sjchandra case LOG_DEBUG: 303225394Sjchandra return ("DEBUG"); 304225394Sjchandra } 305227783Sjchandra assert(!"Invalid log level."); 306225394Sjchandra abort(); /* XXX: gcc */ 307225394Sjchandra} 308224110Sjchandra 309224110Sjchandra/* 310225394Sjchandra * Common log routine. 311225394Sjchandra */ 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