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