pjdlog.c revision 213939
1/*- 2 * Copyright (c) 2009-2010 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Pawel Jakub Dawidek under sponsorship from 6 * the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: head/sbin/hastd/pjdlog.c 213939 2010-10-16 22:50:12Z pjd $"); 32 33#include <assert.h> 34#include <errno.h> 35#include <stdarg.h> 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39#include <syslog.h> 40 41#include "pjdlog.h" 42 43static int pjdlog_mode = PJDLOG_MODE_STD; 44static int pjdlog_debug_level = 0; 45static char pjdlog_prefix[128]; 46 47/* 48 * Configure where the logs should go. 49 * By default they are send to stdout/stderr, but after going into background 50 * (eg. by calling daemon(3)) application is responsible for changing mode to 51 * PJDLOG_MODE_SYSLOG, so logs will be send to syslog. 52 */ 53void 54pjdlog_mode_set(int mode) 55{ 56 57 assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG); 58 59 pjdlog_mode = mode; 60 61 if (mode == PJDLOG_MODE_SYSLOG) 62 openlog(NULL, LOG_PID, LOG_DAEMON); 63} 64 65/* 66 * Return current mode. 67 */ 68int 69pjdlog_mode_get(void) 70{ 71 72 return (pjdlog_mode); 73} 74 75/* 76 * Set debug level. All the logs above the level specified here will be 77 * ignored. 78 */ 79void 80pjdlog_debug_set(int level) 81{ 82 83 assert(level >= 0); 84 85 pjdlog_debug_level = level; 86} 87 88/* 89 * Return current debug level. 90 */ 91int 92pjdlog_debug_get(void) 93{ 94 95 return (pjdlog_debug_level); 96} 97 98/* 99 * Set prefix that will be used before each log. 100 * Setting prefix to NULL will remove it. 101 */ 102void 103pjdlog_prefix_set(const char *fmt, ...) 104{ 105 va_list ap; 106 107 va_start(ap, fmt); 108 pjdlog_prefix_setv(fmt, ap); 109 va_end(ap); 110} 111 112/* 113 * Set prefix that will be used before each log. 114 * Setting prefix to NULL will remove it. 115 */ 116void 117pjdlog_prefix_setv(const char *fmt, va_list ap) 118{ 119 120 assert(fmt != NULL); 121 122 vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap); 123} 124 125/* 126 * Convert log level into string. 127 */ 128static const char * 129pjdlog_level_string(int loglevel) 130{ 131 132 switch (loglevel) { 133 case LOG_EMERG: 134 return ("EMERG"); 135 case LOG_ALERT: 136 return ("ALERT"); 137 case LOG_CRIT: 138 return ("CRIT"); 139 case LOG_ERR: 140 return ("ERROR"); 141 case LOG_WARNING: 142 return ("WARNING"); 143 case LOG_NOTICE: 144 return ("NOTICE"); 145 case LOG_INFO: 146 return ("INFO"); 147 case LOG_DEBUG: 148 return ("DEBUG"); 149 } 150 assert(!"Invalid log level."); 151 abort(); /* XXX: gcc */ 152} 153 154/* 155 * Common log routine. 156 */ 157void 158pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...) 159{ 160 va_list ap; 161 162 va_start(ap, fmt); 163 pjdlogv_common(loglevel, debuglevel, error, fmt, ap); 164 va_end(ap); 165} 166 167/* 168 * Common log routine, which can handle regular log level as well as debug 169 * level. We decide here where to send the logs (stdout/stderr or syslog). 170 */ 171void 172pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt, 173 va_list ap) 174{ 175 176 assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT || 177 loglevel == LOG_CRIT || loglevel == LOG_ERR || 178 loglevel == LOG_WARNING || loglevel == LOG_NOTICE || 179 loglevel == LOG_INFO || loglevel == LOG_DEBUG); 180 assert(loglevel != LOG_DEBUG || debuglevel > 0); 181 assert(error >= -1); 182 183 /* Ignore debug above configured level. */ 184 if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level) 185 return; 186 187 switch (pjdlog_mode) { 188 case PJDLOG_MODE_STD: 189 { 190 FILE *out; 191 192 /* 193 * We send errors and warning to stderr and the rest to stdout. 194 */ 195 switch (loglevel) { 196 case LOG_EMERG: 197 case LOG_ALERT: 198 case LOG_CRIT: 199 case LOG_ERR: 200 case LOG_WARNING: 201 out = stderr; 202 break; 203 case LOG_NOTICE: 204 case LOG_INFO: 205 case LOG_DEBUG: 206 out = stdout; 207 break; 208 default: 209 assert(!"Invalid loglevel."); 210 abort(); /* XXX: gcc */ 211 } 212 213 fprintf(out, "[%s]", pjdlog_level_string(loglevel)); 214 /* Attach debuglevel if this is debug log. */ 215 if (loglevel == LOG_DEBUG) 216 fprintf(out, "[%d]", debuglevel); 217 fprintf(out, " %s", pjdlog_prefix); 218 vfprintf(out, fmt, ap); 219 if (error != -1) 220 fprintf(out, ": %s.", strerror(error)); 221 fprintf(out, "\n"); 222 fflush(out); 223 break; 224 } 225 case PJDLOG_MODE_SYSLOG: 226 { 227 char log[1024]; 228 int len; 229 230 len = snprintf(log, sizeof(log), "%s", pjdlog_prefix); 231 if ((size_t)len < sizeof(log)) 232 len += vsnprintf(log + len, sizeof(log) - len, fmt, ap); 233 if (error != -1 && (size_t)len < sizeof(log)) { 234 (void)snprintf(log + len, sizeof(log) - len, ": %s.", 235 strerror(error)); 236 } 237 syslog(loglevel, "%s", log); 238 break; 239 } 240 default: 241 assert(!"Invalid mode."); 242 } 243} 244 245/* 246 * Regular logs. 247 */ 248void 249pjdlogv(int loglevel, const char *fmt, va_list ap) 250{ 251 252 /* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */ 253 assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT || 254 loglevel == LOG_CRIT || loglevel == LOG_ERR || 255 loglevel == LOG_WARNING || loglevel == LOG_NOTICE || 256 loglevel == LOG_INFO); 257 258 pjdlogv_common(loglevel, 0, -1, fmt, ap); 259} 260 261/* 262 * Regular logs. 263 */ 264void 265pjdlog(int loglevel, const char *fmt, ...) 266{ 267 va_list ap; 268 269 va_start(ap, fmt); 270 pjdlogv(loglevel, fmt, ap); 271 va_end(ap); 272} 273 274/* 275 * Debug logs. 276 */ 277void 278pjdlogv_debug(int debuglevel, const char *fmt, va_list ap) 279{ 280 281 pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap); 282} 283 284/* 285 * Debug logs. 286 */ 287void 288pjdlog_debug(int debuglevel, const char *fmt, ...) 289{ 290 va_list ap; 291 292 va_start(ap, fmt); 293 pjdlogv_debug(debuglevel, fmt, ap); 294 va_end(ap); 295} 296 297/* 298 * Error logs with errno logging. 299 */ 300void 301pjdlogv_errno(int loglevel, const char *fmt, va_list ap) 302{ 303 304 pjdlogv_common(loglevel, 0, errno, fmt, ap); 305} 306 307/* 308 * Error logs with errno logging. 309 */ 310void 311pjdlog_errno(int loglevel, const char *fmt, ...) 312{ 313 va_list ap; 314 315 va_start(ap, fmt); 316 pjdlogv_errno(loglevel, fmt, ap); 317 va_end(ap); 318} 319 320/* 321 * Log error, errno and exit. 322 */ 323void 324pjdlogv_exit(int exitcode, const char *fmt, va_list ap) 325{ 326 327 pjdlogv_errno(LOG_ERR, fmt, ap); 328 exit(exitcode); 329 /* NOTREACHED */ 330} 331 332/* 333 * Log error, errno and exit. 334 */ 335void 336pjdlog_exit(int exitcode, const char *fmt, ...) 337{ 338 va_list ap; 339 340 va_start(ap, fmt); 341 pjdlogv_exit(exitcode, fmt, ap); 342 /* NOTREACHED */ 343 va_end(ap); 344} 345 346/* 347 * Log error and exit. 348 */ 349void 350pjdlogv_exitx(int exitcode, const char *fmt, va_list ap) 351{ 352 353 pjdlogv(LOG_ERR, fmt, ap); 354 exit(exitcode); 355 /* NOTREACHED */ 356} 357 358/* 359 * Log error and exit. 360 */ 361void 362pjdlog_exitx(int exitcode, const char *fmt, ...) 363{ 364 va_list ap; 365 366 va_start(ap, fmt); 367 pjdlogv_exitx(exitcode, fmt, ap); 368 /* NOTREACHED */ 369 va_end(ap); 370} 371 372/* 373 * Log assertion and exit. 374 */ 375void 376pjdlog_verify(const char *func, const char *file, int line, 377 const char *failedexpr) 378{ 379 380 if (func == NULL) { 381 pjdlog_critical("Assertion failed: (%s), file %s, line %d.", 382 failedexpr, file, line); 383 } else { 384 pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.", 385 failedexpr, func, file, line); 386 } 387 abort(); 388 /* NOTREACHED */ 389} 390 391