Home | History | Annotate | Line # | Download | only in util
      1 /*	$NetBSD: msg.c,v 1.1.1.2 2013/01/02 18:59:13 tron Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	msg 3
      6 /* SUMMARY
      7 /*	diagnostic interface
      8 /* SYNOPSIS
      9 /*	#include <msg.h>
     10 /*
     11 /*	int	msg_verbose;
     12 /*
     13 /*	void	msg_info(format, ...)
     14 /*	const char *format;
     15 /*
     16 /*	void	vmsg_info(format, ap)
     17 /*	const char *format;
     18 /*	va_list	ap;
     19 /*
     20 /*	void	msg_warn(format, ...)
     21 /*	const char *format;
     22 /*
     23 /*	void	vmsg_warn(format, ap)
     24 /*	const char *format;
     25 /*	va_list	ap;
     26 /*
     27 /*	void	msg_error(format, ...)
     28 /*	const char *format;
     29 /*
     30 /*	void	vmsg_error(format, ap)
     31 /*	const char *format;
     32 /*	va_list	ap;
     33 /*
     34 /*	NORETURN msg_fatal(format, ...)
     35 /*	const char *format;
     36 /*
     37 /*	NORETURN vmsg_fatal(format, ap)
     38 /*	const char *format;
     39 /*	va_list	ap;
     40 /*
     41 /*	NORETURN msg_fatal_status(status, format, ...)
     42 /*	int	status;
     43 /*	const char *format;
     44 /*
     45 /*	NORETURN vmsg_fatal_status(status, format, ap)
     46 /*	int	status;
     47 /*	const char *format;
     48 /*	va_list	ap;
     49 /*
     50 /*	NORETURN msg_panic(format, ...)
     51 /*	const char *format;
     52 /*
     53 /*	NORETURN vmsg_panic(format, ap)
     54 /*	const char *format;
     55 /*	va_list	ap;
     56 /*
     57 /*	MSG_CLEANUP_FN msg_cleanup(cleanup)
     58 /*	void (*cleanup)(void);
     59 /* AUXILIARY FUNCTIONS
     60 /*	int	msg_error_limit(count)
     61 /*	int	count;
     62 /*
     63 /*	void	msg_error_clear()
     64 /* DESCRIPTION
     65 /*	This module reports diagnostics. By default, diagnostics are sent
     66 /*	to the standard error stream, but the disposition can be changed
     67 /*	by the user. See the hints below in the SEE ALSO section.
     68 /*
     69 /*	msg_info(), msg_warn(), msg_error(), msg_fatal*() and msg_panic()
     70 /*	produce a one-line record with the program name, a severity code
     71 /*	(except for msg_info()), and an informative message. The program
     72 /*	name must have been set by calling one of the msg_XXX_init()
     73 /*	functions (see the SEE ALSO section).
     74 /*
     75 /*	msg_error() reports a recoverable error and increments the error
     76 /*	counter. When the error count exceeds a pre-set limit (default: 13)
     77 /*	the program terminates by calling msg_fatal().
     78 /*
     79 /*	msg_fatal() reports an unrecoverable error and terminates the program
     80 /*	with a non-zero exit status.
     81 /*
     82 /*	msg_fatal_status() reports an unrecoverable error and terminates the
     83 /*	program with the specified exit status.
     84 /*
     85 /*	msg_panic() reports an internal inconsistency, terminates the
     86 /*	program immediately (i.e. without calling the optional user-specified
     87 /*	cleanup routine), and forces a core dump when possible.
     88 /*
     89 /*	msg_cleanup() specifies a function that msg_fatal[_status]() should
     90 /*	invoke before terminating the program, and returns the
     91 /*	current function pointer. Specify a null argument to disable
     92 /*	this feature.
     93 /*
     94 /*	msg_error_limit() sets the error message count limit, and returns.
     95 /*	the old limit.
     96 /*
     97 /*	msg_error_clear() sets the error message count to zero.
     98 /*
     99 /*	msg_verbose is a global flag that can be set to make software
    100 /*	more verbose about what it is doing. By default the flag is zero.
    101 /*	By convention, a larger value means more noise.
    102 /* REENTRANCY
    103 /* .ad
    104 /* .fi
    105 /*	The msg_info() etc. output routines are protected against
    106 /*	ordinary recursive calls and against re-entry by signal
    107 /*	handlers.
    108 /*
    109 /*	Protection against re-entry by signal handlers is subject
    110 /*	to the following limitations:
    111 /* .IP \(bu
    112 /*	The signal handlers must never return. In other words, the
    113 /*	signal handlers must do one or more of the following: call
    114 /*	_exit(), kill the process with a signal, and permanently block
    115 /*	the process.
    116 /* .IP \(bu
    117 /*	The signal handlers must invoke msg_info() etc. not until
    118 /*	after the msg_XXX_init() functions complete initialization,
    119 /*	and not until after the first formatted output to a VSTRING
    120 /*	or VSTREAM.
    121 /* .IP \(bu
    122 /*	Each msg_cleanup() call-back function, and each Postfix or
    123 /*	system function invoked by that call-back function, either
    124 /*	protects itself against recursive calls and re-entry by a
    125 /*	terminating signal handler, or is called exclusively by the
    126 /*	msg(3) module.
    127 /* .PP
    128 /*	When re-entrancy is detected, the requested output and
    129 /*	optional cleanup operations are skipped. Skipping the output
    130 /*	operations prevents memory corruption of VSTREAM_ERR data
    131 /*	structures, and prevents deadlock on Linux releases that
    132 /*	use mutexes within system library routines such as syslog().
    133 /*	This protection exists under the condition that these
    134 /*	specific resources are accessed exclusively via the msg_info()
    135 /*	etc.  functions.
    136 /* SEE ALSO
    137 /*	msg_output(3) specify diagnostics disposition
    138 /*	msg_stdio(3) direct diagnostics to standard I/O stream
    139 /*	msg_vstream(3) direct diagnostics to VSTREAM.
    140 /*	msg_syslog(3) direct diagnostics to syslog daemon
    141 /* BUGS
    142 /*	Some output functions may suffer from intentional or accidental
    143 /*	record length restrictions that are imposed by library routines
    144 /*	and/or by the runtime environment.
    145 /*
    146 /*	Code that spawns a child process should almost always reset
    147 /*	the cleanup handler. The exception is when the parent exits
    148 /*	immediately and the child continues.
    149 /*
    150 /*	msg_cleanup() may be unsafe in code that changes process
    151 /*	privileges, because the call-back routine may run with the
    152 /*	wrong privileges.
    153 /* LICENSE
    154 /* .ad
    155 /* .fi
    156 /*	The Secure Mailer license must be distributed with this software.
    157 /* AUTHOR(S)
    158 /*	Wietse Venema
    159 /*	IBM T.J. Watson Research
    160 /*	P.O. Box 704
    161 /*	Yorktown Heights, NY 10598, USA
    162 /*--*/
    163 
    164 /* System libraries. */
    165 
    166 #include <sys_defs.h>
    167 #include <stdlib.h>
    168 #include <stdarg.h>
    169 #include <unistd.h>
    170 
    171 /* Application-specific. */
    172 
    173 #include "msg.h"
    174 #include "msg_output.h"
    175 
    176  /*
    177   * Default is verbose logging off.
    178   */
    179 int     msg_verbose = 0;
    180 
    181  /*
    182   * Private state.
    183   */
    184 static MSG_CLEANUP_FN msg_cleanup_fn = 0;
    185 static int msg_error_count = 0;
    186 static int msg_error_bound = 13;
    187 
    188  /*
    189   * The msg_exiting flag prevents us from recursively reporting an error with
    190   * msg_fatal*() or msg_panic(), and provides a first-level safety net for
    191   * optional cleanup actions against signal handler re-entry problems. Note
    192   * that msg_vprintf() implements its own guard against re-entry.
    193   *
    194   * XXX We specify global scope, to discourage the compiler from doing smart
    195   * things.
    196   */
    197 volatile int msg_exiting = 0;
    198 
    199 /* msg_info - report informative message */
    200 
    201 void    msg_info(const char *fmt,...)
    202 {
    203     va_list ap;
    204 
    205     va_start(ap, fmt);
    206     vmsg_info(fmt, ap);
    207     va_end(ap);
    208 }
    209 
    210 void    vmsg_info(const char *fmt, va_list ap)
    211 {
    212     msg_vprintf(MSG_INFO, fmt, ap);
    213 }
    214 
    215 /* msg_warn - report warning message */
    216 
    217 void    msg_warn(const char *fmt,...)
    218 {
    219     va_list ap;
    220 
    221     va_start(ap, fmt);
    222     vmsg_warn(fmt, ap);
    223     va_end(ap);
    224 }
    225 
    226 void    vmsg_warn(const char *fmt, va_list ap)
    227 {
    228     msg_vprintf(MSG_WARN, fmt, ap);
    229 }
    230 
    231 /* msg_error - report recoverable error */
    232 
    233 void    msg_error(const char *fmt,...)
    234 {
    235     va_list ap;
    236 
    237     va_start(ap, fmt);
    238     vmsg_error(fmt, ap);
    239     va_end(ap);
    240 }
    241 
    242 void    vmsg_error(const char *fmt, va_list ap)
    243 {
    244     msg_vprintf(MSG_ERROR, fmt, ap);
    245     if (++msg_error_count >= msg_error_bound)
    246 	msg_fatal("too many errors - program terminated");
    247 }
    248 
    249 /* msg_fatal - report error and terminate gracefully */
    250 
    251 NORETURN msg_fatal(const char *fmt,...)
    252 {
    253     va_list ap;
    254 
    255     va_start(ap, fmt);
    256     vmsg_fatal(fmt, ap);
    257     /* NOTREACHED */
    258 }
    259 
    260 NORETURN vmsg_fatal(const char *fmt, va_list ap)
    261 {
    262     if (msg_exiting++ == 0) {
    263 	msg_vprintf(MSG_FATAL, fmt, ap);
    264 	if (msg_cleanup_fn)
    265 	    msg_cleanup_fn();
    266     }
    267     sleep(1);
    268     /* In case we're running as a signal handler. */
    269     _exit(1);
    270 }
    271 
    272 /* msg_fatal_status - report error and terminate gracefully */
    273 
    274 NORETURN msg_fatal_status(int status, const char *fmt,...)
    275 {
    276     va_list ap;
    277 
    278     va_start(ap, fmt);
    279     vmsg_fatal_status(status, fmt, ap);
    280     /* NOTREACHED */
    281 }
    282 
    283 NORETURN vmsg_fatal_status(int status, const char *fmt, va_list ap)
    284 {
    285     if (msg_exiting++ == 0) {
    286 	msg_vprintf(MSG_FATAL, fmt, ap);
    287 	if (msg_cleanup_fn)
    288 	    msg_cleanup_fn();
    289     }
    290     sleep(1);
    291     /* In case we're running as a signal handler. */
    292     _exit(status);
    293 }
    294 
    295 /* msg_panic - report error and dump core */
    296 
    297 NORETURN msg_panic(const char *fmt,...)
    298 {
    299     va_list ap;
    300 
    301     va_start(ap, fmt);
    302     vmsg_panic(fmt, ap);
    303     /* NOTREACHED */
    304 }
    305 
    306 NORETURN vmsg_panic(const char *fmt, va_list ap)
    307 {
    308     if (msg_exiting++ == 0) {
    309 	msg_vprintf(MSG_PANIC, fmt, ap);
    310     }
    311     sleep(1);
    312     abort();					/* Die! */
    313     /* In case we're running as a signal handler. */
    314     _exit(1);					/* DIE!! */
    315 }
    316 
    317 /* msg_cleanup - specify cleanup routine */
    318 
    319 MSG_CLEANUP_FN msg_cleanup(MSG_CLEANUP_FN cleanup_fn)
    320 {
    321     MSG_CLEANUP_FN old_fn = msg_cleanup_fn;
    322 
    323     msg_cleanup_fn = cleanup_fn;
    324     return (old_fn);
    325 }
    326 
    327 /* msg_error_limit - set error message counter limit */
    328 
    329 int     msg_error_limit(int limit)
    330 {
    331     int     old = msg_error_bound;
    332 
    333     msg_error_bound = limit;
    334     return (old);
    335 }
    336 
    337 /* msg_error_clear - reset error message counter */
    338 
    339 void    msg_error_clear(void)
    340 {
    341     msg_error_count = 0;
    342 }
    343