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