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 
msg_info(const char * fmt,...)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 
vmsg_info(const char * fmt,va_list ap)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 
msg_warn(const char * fmt,...)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 
vmsg_warn(const char * fmt,va_list ap)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 
msg_error(const char * fmt,...)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 
vmsg_error(const char * fmt,va_list ap)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 
msg_fatal(const char * fmt,...)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 
vmsg_fatal(const char * fmt,va_list ap)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 
msg_fatal_status(int status,const char * fmt,...)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 
vmsg_fatal_status(int status,const char * fmt,va_list ap)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 
msg_panic(const char * fmt,...)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 
vmsg_panic(const char * fmt,va_list ap)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 
msg_cleanup(MSG_CLEANUP_FN cleanup_fn)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 
msg_error_limit(int limit)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 
msg_error_clear(void)339 void    msg_error_clear(void)
340 {
341     msg_error_count = 0;
342 }
343