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