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