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