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 * $FreeBSD$ 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include <assert.h> 36 #include <errno.h> 37 #include <stdarg.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <syslog.h> 42 43 #include "pjdlog.h" 44 45 static int pjdlog_mode = PJDLOG_MODE_STD; 46 static int pjdlog_debug_level = 0; 47 static char pjdlog_prefix[128]; 48 49 /* 50 * Configure where the logs should go. 51 * By default they are send to stdout/stderr, but after going into background 52 * (eg. by calling daemon(3)) application is responsible for changing mode to 53 * PJDLOG_MODE_SYSLOG, so logs will be send to syslog. 54 */ 55 void 56 pjdlog_mode_set(int mode) 57 { 58 59 assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG); 60 61 pjdlog_mode = mode; 62 } 63 64 /* 65 * Return current mode. 66 */ 67 int 68 pjdlog_mode_get(void) 69 { 70 71 return (pjdlog_mode); 72 } 73 74 /* 75 * Set debug level. All the logs above the level specified here will be 76 * ignored. 77 */ 78 void 79 pjdlog_debug_set(int level) 80 { 81 82 assert(level >= 0); 83 84 pjdlog_debug_level = level; 85 } 86 87 /* 88 * Return current debug level. 89 */ 90 int 91 pjdlog_debug_get(void) 92 { 93 94 return (pjdlog_debug_level); 95 } 96 97 /* 98 * Set prefix that will be used before each log. 99 * Setting prefix to NULL will remove it. 100 */ 101 void 102 pjdlog_prefix_set(const char *fmt, ...) 103 { 104 va_list ap; 105 106 va_start(ap, fmt); 107 pjdlog_prefix_setv(fmt, ap); 108 va_end(ap); 109 } 110 111 /* 112 * Set prefix that will be used before each log. 113 * Setting prefix to NULL will remove it. 114 */ 115 void 116 pjdlog_prefix_setv(const char *fmt, va_list ap) 117 { 118 119 assert(fmt != NULL); 120 121 vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap); 122 } 123 124 /* 125 * Convert log level into string. 126 */ 127 static const char * 128 pjdlog_level_string(int loglevel) 129 { 130 131 switch (loglevel) { 132 case LOG_EMERG: 133 return ("EMERG"); 134 case LOG_ALERT: 135 return ("ALERT"); 136 case LOG_CRIT: 137 return ("CRIT"); 138 case LOG_ERR: 139 return ("ERROR"); 140 case LOG_WARNING: 141 return ("WARNING"); 142 case LOG_NOTICE: 143 return ("NOTICE"); 144 case LOG_INFO: 145 return ("INFO"); 146 case LOG_DEBUG: 147 return ("DEBUG"); 148 } 149 assert(!"Invalid log level."); 150 abort(); /* XXX: gcc */ 151 } 152 153 /* 154 * Common log routine. 155 */ 156 void 157 pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...) 158 { 159 va_list ap; 160 161 va_start(ap, fmt); 162 pjdlogv_common(loglevel, debuglevel, error, fmt, ap); 163 va_end(ap); 164 } 165 166 /* 167 * Common log routine, which can handle regular log level as well as debug 168 * level. We decide here where to send the logs (stdout/stderr or syslog). 169 */ 170 void 171 pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt, 172 va_list ap) 173 { 174 175 assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT || 176 loglevel == LOG_CRIT || loglevel == LOG_ERR || 177 loglevel == LOG_WARNING || loglevel == LOG_NOTICE || 178 loglevel == LOG_INFO || loglevel == LOG_DEBUG); 179 assert(loglevel != LOG_DEBUG || debuglevel > 0); 180 assert(error >= -1); 181 182 /* Ignore debug above configured level. */ 183 if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level) 184 return; 185 186 switch (pjdlog_mode) { 187 case PJDLOG_MODE_STD: 188 { 189 FILE *out; 190 191 /* 192 * We send errors and warning to stderr and the rest to stdout. 193 */ 194 switch (loglevel) { 195 case LOG_EMERG: 196 case LOG_ALERT: 197 case LOG_CRIT: 198 case LOG_ERR: 199 case LOG_WARNING: 200 out = stderr; 201 break; 202 case LOG_NOTICE: 203 case LOG_INFO: 204 case LOG_DEBUG: 205 out = stdout; 206 break; 207 default: 208 assert(!"Invalid loglevel."); 209 abort(); /* XXX: gcc */ 210 } 211 212 fprintf(out, "[%s]", pjdlog_level_string(loglevel)); 213 /* Attach debuglevel if this is debug log. */ 214 if (loglevel == LOG_DEBUG) 215 fprintf(out, "[%d]", debuglevel); 216 fprintf(out, " "); 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 break; 223 } 224 case PJDLOG_MODE_SYSLOG: 225 { 226 char log[1024]; 227 int len; 228 229 len = snprintf(log, sizeof(log), "%s", pjdlog_prefix); 230 if ((size_t)len < sizeof(log)) 231 len = vsnprintf(log + len, sizeof(log) - len, fmt, ap); 232 if (error != -1 && (size_t)len < sizeof(log)) { 233 (void)snprintf(log + len, sizeof(log) - len, ": %s.", 234 strerror(error)); 235 } 236 syslog(loglevel, "%s", log); 237 break; 238 } 239 default: 240 assert(!"Invalid mode."); 241 } 242 } 243 244 /* 245 * Regular logs. 246 */ 247 void 248 pjdlogv(int loglevel, const char *fmt, va_list ap) 249 { 250 251 /* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */ 252 assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT || 253 loglevel == LOG_CRIT || loglevel == LOG_ERR || 254 loglevel == LOG_WARNING || loglevel == LOG_NOTICE || 255 loglevel == LOG_INFO); 256 257 pjdlogv_common(loglevel, 0, -1, fmt, ap); 258 } 259 260 /* 261 * Regular logs. 262 */ 263 void 264 pjdlog(int loglevel, const char *fmt, ...) 265 { 266 va_list ap; 267 268 va_start(ap, fmt); 269 pjdlogv(loglevel, fmt, ap); 270 va_end(ap); 271 } 272 273 /* 274 * Debug logs. 275 */ 276 void 277 pjdlogv_debug(int debuglevel, const char *fmt, va_list ap) 278 { 279 280 pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap); 281 } 282 283 /* 284 * Debug logs. 285 */ 286 void 287 pjdlog_debug(int debuglevel, const char *fmt, ...) 288 { 289 va_list ap; 290 291 va_start(ap, fmt); 292 pjdlogv_debug(debuglevel, fmt, ap); 293 va_end(ap); 294 } 295 296 /* 297 * Error logs with errno logging. 298 */ 299 void 300 pjdlogv_errno(int loglevel, const char *fmt, va_list ap) 301 { 302 303 pjdlogv_common(loglevel, 0, errno, fmt, ap); 304 } 305 306 /* 307 * Error logs with errno logging. 308 */ 309 void 310 pjdlog_errno(int loglevel, const char *fmt, ...) 311 { 312 va_list ap; 313 314 va_start(ap, fmt); 315 pjdlogv_errno(loglevel, fmt, ap); 316 va_end(ap); 317 } 318 319 /* 320 * Log error, errno and exit. 321 */ 322 void 323 pjdlogv_exit(int exitcode, const char *fmt, va_list ap) 324 { 325 326 pjdlogv_errno(LOG_ERR, fmt, ap); 327 exit(exitcode); 328 } 329 330 /* 331 * Log error, errno and exit. 332 */ 333 void 334 pjdlog_exit(int exitcode, const char *fmt, ...) 335 { 336 va_list ap; 337 338 va_start(ap, fmt); 339 pjdlogv_exit(exitcode, fmt, ap); 340 /* NOTREACHED */ 341 va_end(ap); 342 } 343 344 /* 345 * Log error and exit. 346 */ 347 void 348 pjdlogv_exitx(int exitcode, const char *fmt, va_list ap) 349 { 350 351 pjdlogv(LOG_ERR, fmt, ap); 352 exit(exitcode); 353 } 354 355 /* 356 * Log error and exit. 357 */ 358 void 359 pjdlog_exitx(int exitcode, const char *fmt, ...) 360 { 361 va_list ap; 362 363 va_start(ap, fmt); 364 pjdlogv_exitx(exitcode, fmt, ap); 365 /* NOTREACHED */ 366 va_end(ap); 367 } 368