pjdlog.c revision 217966
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: head/sbin/hastd/pjdlog.c 217966 2011-01-27 19:28:29Z pjd $"); 33 34#include <assert.h> 35#include <errno.h> 36#include <stdarg.h> 37#include <stdbool.h> 38#include <stdio.h> 39#include <stdlib.h> 40#include <string.h> 41#include <syslog.h> 42 43#include "pjdlog.h" 44 45static bool pjdlog_initialized = false; 46static int pjdlog_mode = PJDLOG_MODE_STD; 47static int pjdlog_debug_level = 0; 48static char pjdlog_prefix[128]; 49 50void 51pjdlog_init(int mode) 52{ 53 54 assert(!pjdlog_initialized); 55 assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG); 56 57 if (mode == PJDLOG_MODE_SYSLOG) 58 openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON); 59 pjdlog_mode = mode; 60 61 pjdlog_initialized = true; 62} 63 64void 65pjdlog_fini(void) 66{ 67 68 assert(pjdlog_initialized); 69 70 if (pjdlog_mode == PJDLOG_MODE_SYSLOG) 71 closelog(); 72 73 pjdlog_initialized = false; 74} 75 76/* 77 * Configure where the logs should go. 78 * By default they are send to stdout/stderr, but after going into background 79 * (eg. by calling daemon(3)) application is responsible for changing mode to 80 * PJDLOG_MODE_SYSLOG, so logs will be send to syslog. 81 */ 82void 83pjdlog_mode_set(int mode) 84{ 85 86 assert(pjdlog_initialized); 87 assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG); 88 89 if (pjdlog_mode == mode) 90 return; 91 92 if (mode == PJDLOG_MODE_SYSLOG) 93 openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON); 94 else /* if (mode == PJDLOG_MODE_STD) */ 95 closelog(); 96 97 pjdlog_mode = mode; 98} 99 100/* 101 * Return current mode. 102 */ 103int 104pjdlog_mode_get(void) 105{ 106 107 assert(pjdlog_initialized); 108 109 return (pjdlog_mode); 110} 111 112/* 113 * Set debug level. All the logs above the level specified here will be 114 * ignored. 115 */ 116void 117pjdlog_debug_set(int level) 118{ 119 120 assert(pjdlog_initialized); 121 assert(level >= 0); 122 123 pjdlog_debug_level = level; 124} 125 126/* 127 * Return current debug level. 128 */ 129int 130pjdlog_debug_get(void) 131{ 132 133 assert(pjdlog_initialized); 134 135 return (pjdlog_debug_level); 136} 137 138/* 139 * Set prefix that will be used before each log. 140 * Setting prefix to NULL will remove it. 141 */ 142void 143pjdlog_prefix_set(const char *fmt, ...) 144{ 145 va_list ap; 146 147 assert(pjdlog_initialized); 148 149 va_start(ap, fmt); 150 pjdlogv_prefix_set(fmt, ap); 151 va_end(ap); 152} 153 154/* 155 * Set prefix that will be used before each log. 156 * Setting prefix to NULL will remove it. 157 */ 158void 159pjdlogv_prefix_set(const char *fmt, va_list ap) 160{ 161 162 assert(pjdlog_initialized); 163 assert(fmt != NULL); 164 165 vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap); 166} 167 168/* 169 * Convert log level into string. 170 */ 171static const char * 172pjdlog_level_string(int loglevel) 173{ 174 175 switch (loglevel) { 176 case LOG_EMERG: 177 return ("EMERG"); 178 case LOG_ALERT: 179 return ("ALERT"); 180 case LOG_CRIT: 181 return ("CRIT"); 182 case LOG_ERR: 183 return ("ERROR"); 184 case LOG_WARNING: 185 return ("WARNING"); 186 case LOG_NOTICE: 187 return ("NOTICE"); 188 case LOG_INFO: 189 return ("INFO"); 190 case LOG_DEBUG: 191 return ("DEBUG"); 192 } 193 assert(!"Invalid log level."); 194 abort(); /* XXX: gcc */ 195} 196 197/* 198 * Common log routine. 199 */ 200void 201pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...) 202{ 203 va_list ap; 204 205 assert(pjdlog_initialized); 206 207 va_start(ap, fmt); 208 pjdlogv_common(loglevel, debuglevel, error, fmt, ap); 209 va_end(ap); 210} 211 212/* 213 * Common log routine, which can handle regular log level as well as debug 214 * level. We decide here where to send the logs (stdout/stderr or syslog). 215 */ 216void 217pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt, 218 va_list ap) 219{ 220 221 assert(pjdlog_initialized); 222 assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT || 223 loglevel == LOG_CRIT || loglevel == LOG_ERR || 224 loglevel == LOG_WARNING || loglevel == LOG_NOTICE || 225 loglevel == LOG_INFO || loglevel == LOG_DEBUG); 226 assert(loglevel != LOG_DEBUG || debuglevel > 0); 227 assert(error >= -1); 228 229 /* Ignore debug above configured level. */ 230 if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level) 231 return; 232 233 switch (pjdlog_mode) { 234 case PJDLOG_MODE_STD: 235 { 236 FILE *out; 237 238 /* 239 * We send errors and warning to stderr and the rest to stdout. 240 */ 241 switch (loglevel) { 242 case LOG_EMERG: 243 case LOG_ALERT: 244 case LOG_CRIT: 245 case LOG_ERR: 246 case LOG_WARNING: 247 out = stderr; 248 break; 249 case LOG_NOTICE: 250 case LOG_INFO: 251 case LOG_DEBUG: 252 out = stdout; 253 break; 254 default: 255 assert(!"Invalid loglevel."); 256 abort(); /* XXX: gcc */ 257 } 258 259 fprintf(out, "[%s]", pjdlog_level_string(loglevel)); 260 /* Attach debuglevel if this is debug log. */ 261 if (loglevel == LOG_DEBUG) 262 fprintf(out, "[%d]", debuglevel); 263 fprintf(out, " %s", pjdlog_prefix); 264 vfprintf(out, fmt, ap); 265 if (error != -1) 266 fprintf(out, ": %s.", strerror(error)); 267 fprintf(out, "\n"); 268 fflush(out); 269 break; 270 } 271 case PJDLOG_MODE_SYSLOG: 272 { 273 char log[1024]; 274 int len; 275 276 len = snprintf(log, sizeof(log), "%s", pjdlog_prefix); 277 if ((size_t)len < sizeof(log)) 278 len += vsnprintf(log + len, sizeof(log) - len, fmt, ap); 279 if (error != -1 && (size_t)len < sizeof(log)) { 280 (void)snprintf(log + len, sizeof(log) - len, ": %s.", 281 strerror(error)); 282 } 283 syslog(loglevel, "%s", log); 284 break; 285 } 286 default: 287 assert(!"Invalid mode."); 288 } 289} 290 291/* 292 * Regular logs. 293 */ 294void 295pjdlogv(int loglevel, const char *fmt, va_list ap) 296{ 297 298 assert(pjdlog_initialized); 299 300 /* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */ 301 assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT || 302 loglevel == LOG_CRIT || loglevel == LOG_ERR || 303 loglevel == LOG_WARNING || loglevel == LOG_NOTICE || 304 loglevel == LOG_INFO); 305 306 pjdlogv_common(loglevel, 0, -1, fmt, ap); 307} 308 309/* 310 * Regular logs. 311 */ 312void 313pjdlog(int loglevel, const char *fmt, ...) 314{ 315 va_list ap; 316 317 assert(pjdlog_initialized); 318 319 va_start(ap, fmt); 320 pjdlogv(loglevel, fmt, ap); 321 va_end(ap); 322} 323 324/* 325 * Debug logs. 326 */ 327void 328pjdlogv_debug(int debuglevel, const char *fmt, va_list ap) 329{ 330 331 assert(pjdlog_initialized); 332 333 pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap); 334} 335 336/* 337 * Debug logs. 338 */ 339void 340pjdlog_debug(int debuglevel, const char *fmt, ...) 341{ 342 va_list ap; 343 344 assert(pjdlog_initialized); 345 346 va_start(ap, fmt); 347 pjdlogv_debug(debuglevel, fmt, ap); 348 va_end(ap); 349} 350 351/* 352 * Error logs with errno logging. 353 */ 354void 355pjdlogv_errno(int loglevel, const char *fmt, va_list ap) 356{ 357 358 assert(pjdlog_initialized); 359 360 pjdlogv_common(loglevel, 0, errno, fmt, ap); 361} 362 363/* 364 * Error logs with errno logging. 365 */ 366void 367pjdlog_errno(int loglevel, const char *fmt, ...) 368{ 369 va_list ap; 370 371 assert(pjdlog_initialized); 372 373 va_start(ap, fmt); 374 pjdlogv_errno(loglevel, fmt, ap); 375 va_end(ap); 376} 377 378/* 379 * Log error, errno and exit. 380 */ 381void 382pjdlogv_exit(int exitcode, const char *fmt, va_list ap) 383{ 384 385 assert(pjdlog_initialized); 386 387 pjdlogv_errno(LOG_ERR, fmt, ap); 388 exit(exitcode); 389 /* NOTREACHED */ 390} 391 392/* 393 * Log error, errno and exit. 394 */ 395void 396pjdlog_exit(int exitcode, const char *fmt, ...) 397{ 398 va_list ap; 399 400 assert(pjdlog_initialized); 401 402 va_start(ap, fmt); 403 pjdlogv_exit(exitcode, fmt, ap); 404 /* NOTREACHED */ 405 va_end(ap); 406} 407 408/* 409 * Log error and exit. 410 */ 411void 412pjdlogv_exitx(int exitcode, const char *fmt, va_list ap) 413{ 414 415 assert(pjdlog_initialized); 416 417 pjdlogv(LOG_ERR, fmt, ap); 418 exit(exitcode); 419 /* NOTREACHED */ 420} 421 422/* 423 * Log error and exit. 424 */ 425void 426pjdlog_exitx(int exitcode, const char *fmt, ...) 427{ 428 va_list ap; 429 430 assert(pjdlog_initialized); 431 432 va_start(ap, fmt); 433 pjdlogv_exitx(exitcode, fmt, ap); 434 /* NOTREACHED */ 435 va_end(ap); 436} 437 438/* 439 * Log assertion and exit. 440 */ 441void 442pjdlog_verify(const char *func, const char *file, int line, 443 const char *failedexpr, const char *fmt, ...) 444{ 445 va_list ap; 446 447 assert(pjdlog_initialized); 448 449 /* 450 * When there is no message we pass __func__ as 'fmt'. 451 * It would be cleaner to pass NULL or "", but gcc generates a warning 452 * for both of those. 453 */ 454 if (fmt != func) { 455 va_start(ap, fmt); 456 pjdlogv_critical(fmt, ap); 457 va_end(ap); 458 } 459 if (failedexpr == NULL) { 460 if (func == NULL) { 461 pjdlog_critical("Aborted at file %s, line %d.", file, 462 line); 463 } else { 464 pjdlog_critical("Aborted at function %s, file %s, line %d.", 465 func, file, line); 466 } 467 } else { 468 if (func == NULL) { 469 pjdlog_critical("Assertion failed: (%s), file %s, line %d.", 470 failedexpr, file, line); 471 } else { 472 pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.", 473 failedexpr, func, file, line); 474 } 475 } 476 abort(); 477} 478