chiark / gitweb /
debugging for thing that crashed
[innduct.git] / lib / messages.c
1 /* $Id: messages.c 5496 2002-06-07 13:59:06Z alexk $
2 **
3 **  Message and error reporting (possibly fatal).
4 **
5 **  Usage:
6 **
7 **      extern int cleanup(void);
8 **      extern void log(int, const char *, va_list, int);
9 **
10 **      message_fatal_cleanup = cleanup;
11 **      message_program_name = argv[0];
12 **
13 **      warn("Something horrible happened at %lu", time);
14 **      syswarn("Couldn't unlink temporary file %s", tmpfile);
15 **
16 **      die("Something fatal happened at %lu", time);
17 **      sysdie("open of %s failed", filename);
18 **
19 **      debug("Some debugging message about %s", string);
20 **      trace(TRACE_PROGRAM, "Program trace output");
21 **      notice("Informational notices");
22 **
23 **      message_handlers_warn(1, log);
24 **      warn("This now goes through our log function");
25 **
26 **  These functions implement message reporting through user-configurable
27 **  handler functions.  debug() only does something if DEBUG is defined,
28 **  trace() supports sending trace messages in one of a number of configurable
29 **  classes of traces so that they can be turned on or off independently, and
30 **  notice() and warn() just output messages as configured.  die() similarly
31 **  outputs a message but then exits, normally with a status of 1.
32 **
33 **  The sys* versions do the same, but append a colon, a space, and the
34 **  results of strerror(errno) to the end of the message.  All functions
35 **  accept printf-style formatting strings and arguments.
36 **
37 **  If message_fatal_cleanup is non-NULL, it is called before exit by die and
38 **  sysdie and its return value is used as the argument to exit.  It is a
39 **  pointer to a function taking no arguments and returning an int, and can be
40 **  used to call cleanup functions or to exit in some alternate fashion (such
41 **  as by calling _exit).
42 **
43 **  If message_program_name is non-NULL, the string it points to, followed by
44 **  a colon and a space, is prepended to all error messages logged through the
45 **  message_log_stdout and message_log_stderr message handlers (the former is
46 **  the default for notice, and the latter is the default for warn and die).
47 **
48 **  Honoring error_program_name and printing to stderr is just the default
49 **  handler; with message_handlers_* the handlers for any message function can
50 **  be changed.  By default, notice prints to stdout, warn and die print to
51 **  stderr, and the others don't do anything at all.  These functions take a
52 **  count of handlers and then that many function pointers, each one to a
53 **  function that takes a message length (the number of characters snprintf
54 **  generates given the format and arguments), a format, an argument list as a
55 **  va_list, and the applicable errno value (if any).
56 */
57
58 #include "config.h"
59 #include "clibrary.h"
60 #include <errno.h>
61 #include <syslog.h>
62
63 #include "inn/messages.h"
64 #include "libinn.h"
65
66 /* The default handler lists. */
67 static message_handler_func stdout_handlers[2] = {
68     message_log_stdout, NULL
69 };
70 static message_handler_func stderr_handlers[2] = {
71     message_log_stderr, NULL
72 };
73
74 /* The list of logging functions currently in effect. */
75 static message_handler_func *debug_handlers  = NULL;
76 static message_handler_func *trace_handlers  = NULL;
77 static message_handler_func *notice_handlers = stdout_handlers;
78 static message_handler_func *warn_handlers   = stderr_handlers;
79 static message_handler_func *die_handlers    = stderr_handlers;
80
81 /* If non-NULL, called before exit and its return value passed to exit. */
82 int (*message_fatal_cleanup)(void) = NULL;
83
84 /* If non-NULL, prepended (followed by ": ") to messages. */
85 const char *message_program_name = NULL;
86
87 /* Whether or not we're currently outputting a particular type of trace. */
88 static bool tracing[TRACE_ALL] = { false /* false, ... */ };
89
90
91 /*
92 **  Set the handlers for a particular message function.  Takes a pointer to
93 **  the handler list, the count of handlers, and the argument list.
94 */
95 static void
96 message_handlers(message_handler_func **list, int count, va_list args)
97 {
98     int i;
99
100     if (*list != stdout_handlers && *list != stderr_handlers)
101         free(*list);
102     *list = xmalloc(sizeof(message_handler_func) * (count + 1));
103     for (i = 0; i < count; i++)
104         (*list)[i] = (message_handler_func) va_arg(args, message_handler_func);
105     (*list)[count] = NULL;
106 }
107
108
109 /*
110 **  There's no good way of writing these handlers without a bunch of code
111 **  duplication since we can't assume variadic macros, but I can at least make
112 **  it easier to write and keep them consistent.
113 */
114 #define HANDLER_FUNCTION(type)                                  \
115     void                                                        \
116     message_handlers_ ## type(int count, ...)                   \
117     {                                                           \
118         va_list args;                                           \
119                                                                 \
120         va_start(args, count);                                  \
121         message_handlers(& type ## _handlers, count, args);     \
122         va_end(args);                                           \
123     }
124 HANDLER_FUNCTION(debug)
125 HANDLER_FUNCTION(trace)
126 HANDLER_FUNCTION(notice)
127 HANDLER_FUNCTION(warn)
128 HANDLER_FUNCTION(die)
129
130
131 /*
132 **  Print a message to stdout, supporting message_program_name.
133 */
134 void
135 message_log_stdout(int len UNUSED, const char *fmt, va_list args, int err)
136 {
137     if (message_program_name != NULL)
138         fprintf(stdout, "%s: ", message_program_name);
139     vfprintf(stdout, fmt, args);
140     if (err)
141         fprintf(stdout, ": %s", strerror(err));
142     fprintf(stdout, "\n");
143 }
144
145
146 /*
147 **  Print a message to stderr, supporting message_program_name.  Also flush
148 **  stdout so that errors and regular output occur in the right order.
149 */
150 void
151 message_log_stderr(int len UNUSED, const char *fmt, va_list args, int err)
152 {
153     fflush(stdout);
154     if (message_program_name != NULL)
155         fprintf(stderr, "%s: ", message_program_name);
156     vfprintf(stderr, fmt, args);
157     if (err)
158         fprintf(stderr, ": %s", strerror(err));
159     fprintf(stderr, "\n");
160 }
161
162
163 /*
164 **  Log a message to syslog.  This is a helper function used to implement all
165 **  of the syslog message log handlers.  It takes the same arguments as a
166 **  regular message handler function but with an additional priority
167 **  argument.
168 */
169 static void
170 message_log_syslog(int pri, int len, const char *fmt, va_list args, int err)
171 {
172     char *buffer;
173
174     buffer = malloc(len + 1);
175     if (buffer == NULL) {
176         fprintf(stderr, "failed to malloc %u bytes at %s line %d: %s",
177                 len + 1, __FILE__, __LINE__, strerror(errno));
178         exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
179     }
180     vsnprintf(buffer, len + 1, fmt, args);
181     syslog(pri, err ? "%s: %m" : "%s", buffer);
182     free(buffer);
183 }
184
185
186 /*
187 **  Do the same sort of wrapper to generate all of the separate syslog logging
188 **  functions.
189 */
190 #define SYSLOG_FUNCTION(name, type)                                     \
191     void                                                                \
192     message_log_syslog_ ## name(int l, const char *f, va_list a, int e) \
193     {                                                                   \
194         message_log_syslog(LOG_ ## type, l, f, a, e);                   \
195     }
196 SYSLOG_FUNCTION(debug,   DEBUG)
197 SYSLOG_FUNCTION(info,    INFO)
198 SYSLOG_FUNCTION(notice,  NOTICE)
199 SYSLOG_FUNCTION(warning, WARNING)
200 SYSLOG_FUNCTION(err,     ERR)
201 SYSLOG_FUNCTION(crit,    CRIT)
202
203
204 /*
205 **  Enable or disable tracing for particular classes of messages.
206 */
207 void
208 message_trace_enable(enum message_trace type, bool enable)
209 {
210     if (type > TRACE_ALL)
211         return;
212     if (type == TRACE_ALL) {
213         int i;
214
215         for (i = 0; i < TRACE_ALL; i++)
216             tracing[i] = enable;
217     } else {
218         tracing[type] = enable;
219     }
220 }
221
222
223 /*
224 **  All of the message handlers.  There's a lot of code duplication here too,
225 **  but each one is still *slightly* different and va_start has to be called
226 **  multiple times, so it's hard to get rid of the duplication.
227 */
228
229 #ifdef DEBUG
230 void
231 debug(const char *format, ...)
232 {
233     va_list args;
234     message_handler_func *log;
235     int length;
236
237     if (debug_handlers == NULL)
238         return;
239     va_start(args, format);
240     length = vsnprintf(NULL, 0, format, args);
241     va_end(args);
242     if (length < 0)
243         return;
244     for (log = debug_handlers; *log != NULL; log++) {
245         va_start(args, format);
246         (**log)(length, format, args, 0);
247         va_end(args);
248     }
249 }
250 #elif !INN_HAVE_C99_VAMACROS && !INN_HAVE_GNU_VAMACROS
251 void debug(const char *format UNUSED, ...) { }
252 #endif
253
254 void
255 trace(enum message_trace type, const char *format, ...)
256 {
257     va_list args;
258     message_handler_func *log;
259     int length;
260
261     if (trace_handlers == NULL || !tracing[type])
262         return;
263     va_start(args, format);
264     length = vsnprintf(NULL, 0, format, args);
265     va_end(args);
266     if (length < 0)
267         return;
268     for (log = trace_handlers; *log != NULL; log++) {
269         va_start(args, format);
270         (**log)(length, format, args, 0);
271         va_end(args);
272     }
273 }
274
275 void
276 notice(const char *format, ...)
277 {
278     va_list args;
279     message_handler_func *log;
280     int length;
281
282     va_start(args, format);
283     length = vsnprintf(NULL, 0, format, args);
284     va_end(args);
285     if (length < 0)
286         return;
287     for (log = notice_handlers; *log != NULL; log++) {
288         va_start(args, format);
289         (**log)(length, format, args, 0);
290         va_end(args);
291     }
292 }
293
294 void
295 sysnotice(const char *format, ...)
296 {
297     va_list args;
298     message_handler_func *log;
299     int length;
300     int error = errno;
301
302     va_start(args, format);
303     length = vsnprintf(NULL, 0, format, args);
304     va_end(args);
305     if (length < 0)
306         return;
307     for (log = notice_handlers; *log != NULL; log++) {
308         va_start(args, format);
309         (**log)(length, format, args, error);
310         va_end(args);
311     }
312 }
313
314 void
315 warn(const char *format, ...)
316 {
317     va_list args;
318     message_handler_func *log;
319     int length;
320
321     va_start(args, format);
322     length = vsnprintf(NULL, 0, format, args);
323     va_end(args);
324     if (length < 0)
325         return;
326     for (log = warn_handlers; *log != NULL; log++) {
327         va_start(args, format);
328         (**log)(length, format, args, 0);
329         va_end(args);
330     }
331 }
332
333 void
334 syswarn(const char *format, ...)
335 {
336     va_list args;
337     message_handler_func *log;
338     int length;
339     int error = errno;
340
341     va_start(args, format);
342     length = vsnprintf(NULL, 0, format, args);
343     va_end(args);
344     if (length < 0)
345         return;
346     for (log = warn_handlers; *log != NULL; log++) {
347         va_start(args, format);
348         (**log)(length, format, args, error);
349         va_end(args);
350     }
351 }
352
353 void
354 die(const char *format, ...)
355 {
356     va_list args;
357     message_handler_func *log;
358     int length;
359
360     va_start(args, format);
361     length = vsnprintf(NULL, 0, format, args);
362     va_end(args);
363     if (length >= 0)
364         for (log = die_handlers; *log != NULL; log++) {
365             va_start(args, format);
366             (**log)(length, format, args, 0);
367             va_end(args);
368         }
369     exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
370 }
371
372 void
373 sysdie(const char *format, ...)
374 {
375     va_list args;
376     message_handler_func *log;
377     int length;
378     int error = errno;
379
380     va_start(args, format);
381     length = vsnprintf(NULL, 0, format, args);
382     va_end(args);
383     if (length >= 0)
384         for (log = die_handlers; *log != NULL; log++) {
385             va_start(args, format);
386             (**log)(length, format, args, error);
387             va_end(args);
388         }
389     exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
390 }