]> eyrie.org Git - kerberos/krb5-strength.git/blob - util/messages.c
Update to rra-c-util 5.3 and C TAP Harness 3.0
[kerberos/krb5-strength.git] / util / messages.c
1 /*
2  * Message and error reporting (possibly fatal).
3  *
4  * Usage:
5  *
6  *     extern int cleanup(void);
7  *     extern void log(int, const char *, va_list, int);
8  *
9  *     message_fatal_cleanup = cleanup;
10  *     message_program_name = argv[0];
11  *
12  *     warn("Something horrible happened at %lu", time);
13  *     syswarn("Couldn't unlink temporary file %s", tmpfile);
14  *
15  *     die("Something fatal happened at %lu", time);
16  *     sysdie("open of %s failed", filename);
17  *
18  *     debug("Some debugging message about %s", string);
19  *     notice("Informational notices");
20  *
21  *     message_handlers_warn(1, log);
22  *     warn("This now goes through our log function");
23  *
24  * These functions implement message reporting through user-configurable
25  * handler functions.  debug() only does something if DEBUG is defined, and
26  * notice() and warn() just output messages as configured.  die() similarly
27  * outputs a message but then exits, normally with a status of 1.
28  *
29  * The sys* versions do the same, but append a colon, a space, and the results
30  * of strerror(errno) to the end of the message.  All functions accept
31  * printf-style formatting strings and arguments.
32  *
33  * If message_fatal_cleanup is non-NULL, it is called before exit by die and
34  * sysdie and its return value is used as the argument to exit.  It is a
35  * pointer to a function taking no arguments and returning an int, and can be
36  * used to call cleanup functions or to exit in some alternate fashion (such
37  * as by calling _exit).
38  *
39  * If message_program_name is non-NULL, the string it points to, followed by a
40  * colon and a space, is prepended to all error messages logged through the
41  * message_log_stdout and message_log_stderr message handlers (the former is
42  * the default for notice, and the latter is the default for warn and die).
43  *
44  * Honoring error_program_name and printing to stderr is just the default
45  * handler; with message_handlers_* the handlers for any message function can
46  * be changed.  By default, notice prints to stdout, warn and die print to
47  * stderr, and the others don't do anything at all.  These functions take a
48  * count of handlers and then that many function pointers, each one to a
49  * function that takes a message length (the number of characters snprintf
50  * generates given the format and arguments), a format, an argument list as a
51  * va_list, and the applicable errno value (if any).
52  *
53  * The canonical version of this file is maintained in the rra-c-util package,
54  * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
55  *
56  * Written by Russ Allbery <eagle@eyrie.org>
57  * Copyright 2008, 2009, 2010, 2013
58  *     The Board of Trustees of the Leland Stanford Junior University
59  * Copyright (c) 2004, 2005, 2006
60  *     by Internet Systems Consortium, Inc. ("ISC")
61  * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
62  *     2002, 2003 by The Internet Software Consortium and Rich Salz
63  *
64  * This code is derived from software contributed to the Internet Software
65  * Consortium by Rich Salz.
66  *
67  * Permission to use, copy, modify, and distribute this software for any
68  * purpose with or without fee is hereby granted, provided that the above
69  * copyright notice and this permission notice appear in all copies.
70  *
71  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
72  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
73  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
74  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
75  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
76  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
77  * PERFORMANCE OF THIS SOFTWARE.
78  */
79
80 #include <config.h>
81 #include <portable/system.h>
82
83 #include <errno.h>
84 #ifdef HAVE_SYSLOG_H
85 # include <syslog.h>
86 #endif
87
88 #ifdef _WIN32
89 # include <windows.h>
90 # define LOG_DEBUG      EVENTLOG_SUCCESS
91 # define LOG_INFO       EVENTLOG_INFORMATION_TYPE
92 # define LOG_NOTICE     EVENTLOG_INFORMATION_TYPE
93 # define LOG_WARNING    EVENTLOG_WARNING_TYPE
94 # define LOG_ERR        EVENTLOG_ERROR_TYPE
95 # define LOG_CRIT       EVENTLOG_ERROR_TYPE
96 #endif
97
98 #include <util/macros.h>
99 #include <util/messages.h>
100 #include <util/xmalloc.h>
101
102 /* The default handler lists. */
103 static message_handler_func stdout_handlers[2] = {
104     message_log_stdout, NULL
105 };
106 static message_handler_func stderr_handlers[2] = {
107     message_log_stderr, NULL
108 };
109
110 /* The list of logging functions currently in effect. */
111 static message_handler_func *debug_handlers  = NULL;
112 static message_handler_func *notice_handlers = stdout_handlers;
113 static message_handler_func *warn_handlers   = stderr_handlers;
114 static message_handler_func *die_handlers    = stderr_handlers;
115
116 /* If non-NULL, called before exit and its return value passed to exit. */
117 int (*message_fatal_cleanup)(void) = NULL;
118
119 /* If non-NULL, prepended (followed by ": ") to messages. */
120 const char *message_program_name = NULL;
121
122
123 /*
124  * Set the handlers for a particular message function.  Takes a pointer to the
125  * handler list, the count of handlers, and the argument list.
126  */
127 static void
128 message_handlers(message_handler_func **list, unsigned int count, va_list args)
129 {
130     unsigned int i;
131
132     if (*list != stdout_handlers && *list != stderr_handlers)
133         free(*list);
134     *list = xmalloc(sizeof(message_handler_func) * (count + 1));
135     for (i = 0; i < count; i++)
136         (*list)[i] = (message_handler_func) va_arg(args, message_handler_func);
137     (*list)[count] = NULL;
138 }
139
140
141 /*
142  * There's no good way of writing these handlers without a bunch of code
143  * duplication since we can't assume variadic macros, but I can at least make
144  * it easier to write and keep them consistent.
145  */
146 #define HANDLER_FUNCTION(type)                                  \
147     void                                                        \
148     message_handlers_ ## type(unsigned int count, ...)          \
149     {                                                           \
150         va_list args;                                           \
151                                                                 \
152         va_start(args, count);                                  \
153         message_handlers(& type ## _handlers, count, args);     \
154         va_end(args);                                           \
155     }
156 HANDLER_FUNCTION(debug)
157 HANDLER_FUNCTION(notice)
158 HANDLER_FUNCTION(warn)
159 HANDLER_FUNCTION(die)
160
161
162 /*
163  * Reset all handlers back to the defaults and free all allocated memory.
164  * This is primarily useful for programs that undergo comprehensive memory
165  * allocation analysis.
166  */
167 void
168 message_handlers_reset(void)
169 {
170     free(debug_handlers);
171     debug_handlers = NULL;
172     if (notice_handlers != stdout_handlers) {
173         free(notice_handlers);
174         notice_handlers = stdout_handlers;
175     }
176     if (warn_handlers != stderr_handlers) {
177         free(warn_handlers);
178         warn_handlers = stderr_handlers;
179     }
180     if (die_handlers != stderr_handlers) {
181         free(die_handlers);
182         die_handlers = stderr_handlers;
183     }
184 }
185
186
187 /*
188  * Print a message to stdout, supporting message_program_name.
189  */
190 void
191 message_log_stdout(size_t len UNUSED, const char *fmt, va_list args, int err)
192 {
193     if (message_program_name != NULL)
194         fprintf(stdout, "%s: ", message_program_name);
195     vfprintf(stdout, fmt, args);
196     if (err)
197         fprintf(stdout, ": %s", strerror(err));
198     fprintf(stdout, "\n");
199     fflush(stdout);
200 }
201
202
203 /*
204  * Print a message to stderr, supporting message_program_name.  Also flush
205  * stdout so that errors and regular output occur in the right order.
206  */
207 void
208 message_log_stderr(size_t len UNUSED, const char *fmt, va_list args, int err)
209 {
210     fflush(stdout);
211     if (message_program_name != NULL)
212         fprintf(stderr, "%s: ", message_program_name);
213     vfprintf(stderr, fmt, args);
214     if (err)
215         fprintf(stderr, ": %s", strerror(err));
216     fprintf(stderr, "\n");
217 }
218
219
220 /*
221  * Log a message to syslog.  This is a helper function used to implement all
222  * of the syslog message log handlers.  It takes the same arguments as a
223  * regular message handler function but with an additional priority argument.
224  *
225  * This needs further attention on Windows.  For example, it currently doesn't
226  * log the errno information.
227  */
228 static void
229 message_log_syslog(int pri, size_t len, const char *fmt, va_list args, int err)
230 {
231     char *buffer;
232     int status;
233
234     buffer = malloc(len + 1);
235     if (buffer == NULL) {
236         fprintf(stderr, "failed to malloc %lu bytes at %s line %d: %s",
237                 (unsigned long) len + 1, __FILE__, __LINE__, strerror(errno));
238         exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
239     }
240     status = vsnprintf(buffer, len + 1, fmt, args);
241     if (status < 0) {
242         warn("failed to format output with vsnprintf in syslog handler");
243         free(buffer);
244         return;
245     }
246 #ifdef _WIN32
247     {
248         HANDLE eventlog;
249
250         eventlog = RegisterEventSource(NULL, message_program_name);
251         if (eventlog != NULL) {
252             ReportEvent(eventlog, (WORD) pri, 0, 0, NULL, 1, 0, &buffer, NULL);
253             CloseEventLog(eventlog);
254         }
255     }
256 #else /* !_WIN32 */
257     if (err == 0)
258         syslog(pri, "%s", buffer);
259     else
260         syslog(pri, "%s: %s", buffer, strerror(err));
261 #endif /* !_WIN32 */
262     free(buffer);
263 }
264
265
266 /*
267  * Do the same sort of wrapper to generate all of the separate syslog logging
268  * functions.
269  */
270 #define SYSLOG_FUNCTION(name, type)                                        \
271     void                                                                   \
272     message_log_syslog_ ## name(size_t l, const char *f, va_list a, int e) \
273     {                                                                      \
274         message_log_syslog(LOG_ ## type, l, f, a, e);                      \
275     }
276 SYSLOG_FUNCTION(debug,   DEBUG)
277 SYSLOG_FUNCTION(info,    INFO)
278 SYSLOG_FUNCTION(notice,  NOTICE)
279 SYSLOG_FUNCTION(warning, WARNING)
280 SYSLOG_FUNCTION(err,     ERR)
281 SYSLOG_FUNCTION(crit,    CRIT)
282
283
284 /*
285  * All of the message handlers.  There's a lot of code duplication here too,
286  * but each one is still *slightly* different and va_start has to be called
287  * multiple times, so it's hard to get rid of the duplication.
288  */
289
290 void
291 debug(const char *format, ...)
292 {
293     va_list args;
294     message_handler_func *log;
295     ssize_t length;
296
297     if (debug_handlers == NULL)
298         return;
299     va_start(args, format);
300     length = vsnprintf(NULL, 0, format, args);
301     va_end(args);
302     if (length < 0)
303         return;
304     for (log = debug_handlers; *log != NULL; log++) {
305         va_start(args, format);
306         (**log)((size_t) length, format, args, 0);
307         va_end(args);
308     }
309 }
310
311 void
312 notice(const char *format, ...)
313 {
314     va_list args;
315     message_handler_func *log;
316     ssize_t length;
317
318     va_start(args, format);
319     length = vsnprintf(NULL, 0, format, args);
320     va_end(args);
321     if (length < 0)
322         return;
323     for (log = notice_handlers; *log != NULL; log++) {
324         va_start(args, format);
325         (**log)((size_t) length, format, args, 0);
326         va_end(args);
327     }
328 }
329
330 void
331 sysnotice(const char *format, ...)
332 {
333     va_list args;
334     message_handler_func *log;
335     ssize_t length;
336     int error = errno;
337
338     va_start(args, format);
339     length = vsnprintf(NULL, 0, format, args);
340     va_end(args);
341     if (length < 0)
342         return;
343     for (log = notice_handlers; *log != NULL; log++) {
344         va_start(args, format);
345         (**log)((size_t) length, format, args, error);
346         va_end(args);
347     }
348 }
349
350 void
351 warn(const char *format, ...)
352 {
353     va_list args;
354     message_handler_func *log;
355     ssize_t length;
356
357     va_start(args, format);
358     length = vsnprintf(NULL, 0, format, args);
359     va_end(args);
360     if (length < 0)
361         return;
362     for (log = warn_handlers; *log != NULL; log++) {
363         va_start(args, format);
364         (**log)((size_t) length, format, args, 0);
365         va_end(args);
366     }
367 }
368
369 void
370 syswarn(const char *format, ...)
371 {
372     va_list args;
373     message_handler_func *log;
374     ssize_t length;
375     int error = errno;
376
377     va_start(args, format);
378     length = vsnprintf(NULL, 0, format, args);
379     va_end(args);
380     if (length < 0)
381         return;
382     for (log = warn_handlers; *log != NULL; log++) {
383         va_start(args, format);
384         (**log)((size_t) length, format, args, error);
385         va_end(args);
386     }
387 }
388
389 void
390 die(const char *format, ...)
391 {
392     va_list args;
393     message_handler_func *log;
394     ssize_t length;
395
396     va_start(args, format);
397     length = vsnprintf(NULL, 0, format, args);
398     va_end(args);
399     if (length >= 0)
400         for (log = die_handlers; *log != NULL; log++) {
401             va_start(args, format);
402             (**log)((size_t) length, format, args, 0);
403             va_end(args);
404         }
405     exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
406 }
407
408 void
409 sysdie(const char *format, ...)
410 {
411     va_list args;
412     message_handler_func *log;
413     ssize_t length;
414     int error = errno;
415
416     va_start(args, format);
417     length = vsnprintf(NULL, 0, format, args);
418     va_end(args);
419     if (length >= 0)
420         for (log = die_handlers; *log != NULL; log++) {
421             va_start(args, format);
422             (**log)((size_t) length, format, args, error);
423             va_end(args);
424         }
425     exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
426 }