pjdlog.c revision 217965
179455Sobrien/*- 279455Sobrien * Copyright (c) 2009-2010 The FreeBSD Foundation 379455Sobrien * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org> 479455Sobrien * All rights reserved. 579455Sobrien * 679455Sobrien * This software was developed by Pawel Jakub Dawidek under sponsorship from 779455Sobrien * the FreeBSD Foundation. 879455Sobrien * 979455Sobrien * Redistribution and use in source and binary forms, with or without 1079455Sobrien * modification, are permitted provided that the following conditions 1179455Sobrien * are met: 1279455Sobrien * 1. Redistributions of source code must retain the above copyright 1379455Sobrien * notice, this list of conditions and the following disclaimer. 1479455Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1579455Sobrien * notice, this list of conditions and the following disclaimer in the 1679455Sobrien * documentation and/or other materials provided with the distribution. 1779455Sobrien * 1879455Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 1979455Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2079455Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2179455Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 2279455Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2379455Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2479455Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2579455Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2679455Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2779455Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2879455Sobrien * SUCH DAMAGE. 2979455Sobrien */ 3079455Sobrien 3179455Sobrien#include <sys/cdefs.h> 3279455Sobrien__FBSDID("$FreeBSD: head/sbin/hastd/pjdlog.c 217965 2011-01-27 19:24:07Z pjd $"); 3379455Sobrien 3479455Sobrien#include <assert.h> 3579455Sobrien#include <errno.h> 3679455Sobrien#include <stdarg.h> 3779455Sobrien#include <stdbool.h> 3879455Sobrien#include <stdio.h> 3979455Sobrien#include <stdlib.h> 4079455Sobrien#include <string.h> 4179455Sobrien#include <syslog.h> 4279455Sobrien 4379455Sobrien#include "pjdlog.h" 4479455Sobrien 4579455Sobrienstatic bool pjdlog_initialized = false; 4679455Sobrienstatic int pjdlog_mode = PJDLOG_MODE_STD; 4779455Sobrienstatic int pjdlog_debug_level = 0; 4879455Sobrienstatic char pjdlog_prefix[128]; 49123883Sbde 5079455Sobrienvoid 5179455Sobrienpjdlog_init(int mode) 5279455Sobrien{ 5379455Sobrien 5479455Sobrien assert(!pjdlog_initialized); 5579455Sobrien assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG); 5679455Sobrien 5779455Sobrien if (mode == PJDLOG_MODE_SYSLOG) 5892839Simp openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON); 5979455Sobrien pjdlog_mode = mode; 6079455Sobrien 61123883Sbde pjdlog_initialized = true; 62123883Sbde} 63123883Sbde 64123883Sbdevoid 65123883Sbdepjdlog_fini(void) 66123883Sbde{ 67123883Sbde 68102231Strhodes assert(pjdlog_initialized); 6979455Sobrien 7092839Simp if (pjdlog_mode == PJDLOG_MODE_SYSLOG) 7179455Sobrien closelog(); 7279455Sobrien 7379455Sobrien pjdlog_initialized = false; 7479455Sobrien} 7579455Sobrien 7679455Sobrien/* 7779455Sobrien * Configure where the logs should go. 7879455Sobrien * By default they are send to stdout/stderr, but after going into background 7979455Sobrien * (eg. by calling daemon(3)) application is responsible for changing mode to 8079455Sobrien * PJDLOG_MODE_SYSLOG, so logs will be send to syslog. 8179455Sobrien */ 82102231Strhodesvoid 8379455Sobrienpjdlog_mode_set(int mode) 8479455Sobrien{ 8579455Sobrien 8679455Sobrien assert(pjdlog_initialized); 8779455Sobrien assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG); 8892839Simp 8979455Sobrien if (pjdlog_mode == mode) 9079455Sobrien return; 9179455Sobrien 9279455Sobrien if (mode == PJDLOG_MODE_SYSLOG) 9392839Simp openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON); 9479455Sobrien else /* if (mode == PJDLOG_MODE_STD) */ 9579455Sobrien closelog(); 9679455Sobrien 9779455Sobrien pjdlog_mode = mode; 9879455Sobrien} 9992839Simp 10079455Sobrien/* 10179455Sobrien * Return current mode. 10279455Sobrien */ 10379455Sobrienint 10479455Sobrienpjdlog_mode_get(void) 10592839Simp{ 10679455Sobrien 10779455Sobrien assert(pjdlog_initialized); 10879455Sobrien 10979455Sobrien return (pjdlog_mode); 11092839Simp} 11179455Sobrien 11279455Sobrien/* 11379455Sobrien * Set debug level. All the logs above the level specified here will be 11479455Sobrien * ignored. 11592839Simp */ 11679455Sobrienvoid 11779455Sobrienpjdlog_debug_set(int level) 11879455Sobrien{ 11979455Sobrien 12092839Simp assert(pjdlog_initialized); 12192839Simp assert(level >= 0); 12292839Simp 12379455Sobrien pjdlog_debug_level = level; 12479455Sobrien} 12579455Sobrien 12679455Sobrien/* 12779455Sobrien * Return current debug level. 12879455Sobrien */ 12979455Sobrienint 13092839Simppjdlog_debug_get(void) 13179455Sobrien{ 13279455Sobrien 13379455Sobrien assert(pjdlog_initialized); 13492839Simp 13592839Simp return (pjdlog_debug_level); 13679455Sobrien} 13779455Sobrien 13879455Sobrien/* 13979455Sobrien * Set prefix that will be used before each log. 14079455Sobrien * Setting prefix to NULL will remove it. 14179455Sobrien */ 14279455Sobrienvoid 14392839Simppjdlog_prefix_set(const char *fmt, ...) 14479455Sobrien{ 14579455Sobrien va_list ap; 14679455Sobrien 14779455Sobrien assert(pjdlog_initialized); 14892839Simp 14979455Sobrien va_start(ap, fmt); 15079455Sobrien 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) 444{ 445 446 if (func == NULL) { 447 pjdlog_critical("Assertion failed: (%s), file %s, line %d.", 448 failedexpr, file, line); 449 } else { 450 pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.", 451 failedexpr, func, file, line); 452 } 453 abort(); 454} 455