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$"); 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 43 static int pjdlog_mode = PJDLOG_MODE_STD; 44 static int pjdlog_debug_level = 0; 45 static 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 */ 53 void 54 pjdlog_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 */ 68 int 69 pjdlog_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 */ 79 void 80 pjdlog_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 */ 91 int 92 pjdlog_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 */ 102 void 103 pjdlog_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 */ 116 void 117 pjdlog_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 */ 128 static const char * 129 pjdlog_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 */ 157 void 158 pjdlog_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 */ 171 void 172 pjdlogv_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 */ 248 void 249 pjdlogv(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 */ 264 void 265 pjdlog(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 */ 277 void 278 pjdlogv_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 */ 287 void 288 pjdlog_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 */ 300 void 301 pjdlogv_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 */ 310 void 311 pjdlog_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 */ 323 void 324 pjdlogv_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 */ 335 void 336 pjdlog_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 */ 349 void 350 pjdlogv_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 */ 361 void 362 pjdlog_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 */ 375 void 376 pjdlog_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