pjdlog.c revision 212052
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 212052 2010-08-31 12:05:13Z 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, " "); 218 fprintf(out, "%s", pjdlog_prefix); 219 vfprintf(out, fmt, ap); 220 if (error != -1) 221 fprintf(out, ": %s.", strerror(error)); 222 fprintf(out, "\n"); 223 fflush(out); 224 break; 225 } 226 case PJDLOG_MODE_SYSLOG: 227 { 228 char log[1024]; 229 int len; 230 231 len = snprintf(log, sizeof(log), "%s", pjdlog_prefix); 232 if ((size_t)len < sizeof(log)) 233 len += vsnprintf(log + len, sizeof(log) - len, fmt, ap); 234 if (error != -1 && (size_t)len < sizeof(log)) { 235 (void)snprintf(log + len, sizeof(log) - len, ": %s.", 236 strerror(error)); 237 } 238 syslog(loglevel, "%s", log); 239 break; 240 } 241 default: 242 assert(!"Invalid mode."); 243 } 244} 245 246/* 247 * Regular logs. 248 */ 249void 250pjdlogv(int loglevel, const char *fmt, va_list ap) 251{ 252 253 /* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */ 254 assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT || 255 loglevel == LOG_CRIT || loglevel == LOG_ERR || 256 loglevel == LOG_WARNING || loglevel == LOG_NOTICE || 257 loglevel == LOG_INFO); 258 259 pjdlogv_common(loglevel, 0, -1, fmt, ap); 260} 261 262/* 263 * Regular logs. 264 */ 265void 266pjdlog(int loglevel, const char *fmt, ...) 267{ 268 va_list ap; 269 270 va_start(ap, fmt); 271 pjdlogv(loglevel, fmt, ap); 272 va_end(ap); 273} 274 275/* 276 * Debug logs. 277 */ 278void 279pjdlogv_debug(int debuglevel, const char *fmt, va_list ap) 280{ 281 282 pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap); 283} 284 285/* 286 * Debug logs. 287 */ 288void 289pjdlog_debug(int debuglevel, const char *fmt, ...) 290{ 291 va_list ap; 292 293 va_start(ap, fmt); 294 pjdlogv_debug(debuglevel, fmt, ap); 295 va_end(ap); 296} 297 298/* 299 * Error logs with errno logging. 300 */ 301void 302pjdlogv_errno(int loglevel, const char *fmt, va_list ap) 303{ 304 305 pjdlogv_common(loglevel, 0, errno, fmt, ap); 306} 307 308/* 309 * Error logs with errno logging. 310 */ 311void 312pjdlog_errno(int loglevel, const char *fmt, ...) 313{ 314 va_list ap; 315 316 va_start(ap, fmt); 317 pjdlogv_errno(loglevel, fmt, ap); 318 va_end(ap); 319} 320 321/* 322 * Log error, errno and exit. 323 */ 324void 325pjdlogv_exit(int exitcode, const char *fmt, va_list ap) 326{ 327 328 pjdlogv_errno(LOG_ERR, fmt, ap); 329 exit(exitcode); 330 /* NOTREACHED */ 331} 332 333/* 334 * Log error, errno and exit. 335 */ 336void 337pjdlog_exit(int exitcode, const char *fmt, ...) 338{ 339 va_list ap; 340 341 va_start(ap, fmt); 342 pjdlogv_exit(exitcode, fmt, ap); 343 /* NOTREACHED */ 344 va_end(ap); 345} 346 347/* 348 * Log error and exit. 349 */ 350void 351pjdlogv_exitx(int exitcode, const char *fmt, va_list ap) 352{ 353 354 pjdlogv(LOG_ERR, fmt, ap); 355 exit(exitcode); 356 /* NOTREACHED */ 357} 358 359/* 360 * Log error and exit. 361 */ 362void 363pjdlog_exitx(int exitcode, const char *fmt, ...) 364{ 365 va_list ap; 366 367 va_start(ap, fmt); 368 pjdlogv_exitx(exitcode, fmt, ap); 369 /* NOTREACHED */ 370 va_end(ap); 371} 372 373/* 374 * Log assertion and exit. 375 */ 376void 377pjdlog_verify(const char *func, const char *file, int line, 378 const char *failedexpr) 379{ 380 381 if (func == NULL) { 382 pjdlog_critical("Assertion failed: (%s), file %s, line %d.", 383 failedexpr, file, line); 384 } else { 385 pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.", 386 failedexpr, func, file, line); 387 } 388 abort(); 389 /* NOTREACHED */ 390} 391 392