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