]> eyrie.org Git - kerberos/krb5-strength.git/blob - util/messages.c
Reformat with clang-format
[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] = {message_log_stdout, NULL};
105 static message_handler_func stderr_handlers[2] = {message_log_stderr, NULL};
106
107 /* The list of logging functions currently in effect. */
108 static message_handler_func *debug_handlers = NULL;
109 static message_handler_func *notice_handlers = stdout_handlers;
110 static message_handler_func *warn_handlers = stderr_handlers;
111 static message_handler_func *die_handlers = stderr_handlers;
112
113 /* If non-NULL, called before exit and its return value passed to exit. */
114 int (*message_fatal_cleanup)(void) = NULL;
115
116 /* If non-NULL, prepended (followed by ": ") to messages. */
117 const char *message_program_name = NULL;
118
119
120 /*
121  * Set the handlers for a particular message function.  Takes a pointer to the
122  * handler list, the count of handlers, and the argument list.
123  */
124 static void
125 message_handlers(message_handler_func **list, unsigned int count, va_list args)
126 {
127     unsigned int i;
128
129     if (*list != stdout_handlers && *list != stderr_handlers)
130         free(*list);
131     *list = xcalloc(count + 1, sizeof(message_handler_func));
132     for (i = 0; i < count; i++)
133         (*list)[i] = (message_handler_func) va_arg(args, message_handler_func);
134     (*list)[count] = NULL;
135 }
136
137
138 /*
139  * There's no good way of writing these handlers without a bunch of code
140  * duplication since we can't assume variadic macros, but I can at least make
141  * it easier to write and keep them consistent.
142  */
143 #define HANDLER_FUNCTION(type)                            \
144     void message_handlers_##type(unsigned int count, ...) \
145     {                                                     \
146         va_list args;                                     \
147                                                           \
148         va_start(args, count);                            \
149         message_handlers(&type##_handlers, count, args);  \
150         va_end(args);                                     \
151     }
152 HANDLER_FUNCTION(debug)
153 HANDLER_FUNCTION(notice)
154 HANDLER_FUNCTION(warn)
155 HANDLER_FUNCTION(die)
156
157
158 /*
159  * Reset all handlers back to the defaults and free all allocated memory.
160  * This is primarily useful for programs that undergo comprehensive memory
161  * allocation analysis.
162  */
163 void
164 message_handlers_reset(void)
165 {
166     free(debug_handlers);
167     debug_handlers = NULL;
168     if (notice_handlers != stdout_handlers) {
169         free(notice_handlers);
170         notice_handlers = stdout_handlers;
171     }
172     if (warn_handlers != stderr_handlers) {
173         free(warn_handlers);
174         warn_handlers = stderr_handlers;
175     }
176     if (die_handlers != stderr_handlers) {
177         free(die_handlers);
178         die_handlers = stderr_handlers;
179     }
180 }
181
182
183 /*
184  * Print a message to stdout, supporting message_program_name.
185  */
186 void
187 message_log_stdout(size_t len UNUSED, const char *fmt, va_list args, int err)
188 {
189     if (message_program_name != NULL)
190         fprintf(stdout, "%s: ", message_program_name);
191     vfprintf(stdout, fmt, args);
192     if (err)
193         fprintf(stdout, ": %s", strerror(err));
194     fprintf(stdout, "\n");
195     fflush(stdout);
196 }
197
198
199 /*
200  * Print a message to stderr, supporting message_program_name.  Also flush
201  * stdout so that errors and regular output occur in the right order.
202  */
203 void
204 message_log_stderr(size_t len UNUSED, const char *fmt, va_list args, int err)
205 {
206     fflush(stdout);
207     if (message_program_name != NULL)
208         fprintf(stderr, "%s: ", message_program_name);
209     vfprintf(stderr, fmt, args);
210     if (err)
211         fprintf(stderr, ": %s", strerror(err));
212     fprintf(stderr, "\n");
213 }
214
215
216 /*
217  * Log a message to syslog.  This is a helper function used to implement all
218  * of the syslog message log handlers.  It takes the same arguments as a
219  * regular message handler function but with an additional priority argument.
220  *
221  * This needs further attention on Windows.  For example, it currently doesn't
222  * log the errno information.
223  */
224 static void __attribute__((__format__(printf, 3, 0)))
225 message_log_syslog(int pri, size_t len, const char *fmt, va_list args, int err)
226 {
227     char *buffer;
228     int status;
229
230     buffer = malloc(len + 1);
231     if (buffer == NULL) {
232         fprintf(stderr, "failed to malloc %lu bytes at %s line %d: %s",
233                 (unsigned long) len + 1, __FILE__, __LINE__, strerror(errno));
234         exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
235     }
236     status = vsnprintf(buffer, len + 1, fmt, args);
237     if (status < 0 || (size_t) status >= len + 1) {
238         warn("failed to format output with vsnprintf in syslog handler");
239         free(buffer);
240         return;
241     }
242 #ifdef _WIN32
243     {
244         HANDLE eventlog;
245
246         eventlog = RegisterEventSource(NULL, message_program_name);
247         if (eventlog != NULL) {
248             ReportEvent(eventlog, (WORD) pri, 0, 0, NULL, 1, 0, &buffer, NULL);
249             CloseEventLog(eventlog);
250         }
251     }
252 #else  /* !_WIN32 */
253     if (err == 0)
254         syslog(pri, "%s", buffer);
255     else
256         syslog(pri, "%s: %s", buffer, strerror(err));
257 #endif /* !_WIN32 */
258     free(buffer);
259 }
260
261
262 /*
263  * Do the same sort of wrapper to generate all of the separate syslog logging
264  * functions.
265  */
266 #define SYSLOG_FUNCTION(name, type)                                           \
267     void message_log_syslog_##name(size_t l, const char *f, va_list a, int e) \
268     {                                                                         \
269         message_log_syslog(LOG_##type, l, f, a, e);                           \
270     }
271 SYSLOG_FUNCTION(debug, DEBUG)
272 SYSLOG_FUNCTION(info, INFO)
273 SYSLOG_FUNCTION(notice, NOTICE)
274 SYSLOG_FUNCTION(warning, WARNING)
275 SYSLOG_FUNCTION(err, ERR)
276 SYSLOG_FUNCTION(crit, CRIT)
277
278
279 /*
280  * All of the message handlers.  There's a lot of code duplication here too,
281  * but each one is still *slightly* different and va_start has to be called
282  * multiple times, so it's hard to get rid of the duplication.
283  */
284
285 void
286 debug(const char *format, ...)
287 {
288     va_list args;
289     message_handler_func *log;
290     ssize_t length;
291
292     if (debug_handlers == NULL)
293         return;
294     va_start(args, format);
295     length = vsnprintf(NULL, 0, format, args);
296     va_end(args);
297     if (length < 0)
298         return;
299     for (log = debug_handlers; *log != NULL; log++) {
300         va_start(args, format);
301         (**log)((size_t) length, format, args, 0);
302         va_end(args);
303     }
304 }
305
306 void
307 notice(const char *format, ...)
308 {
309     va_list args;
310     message_handler_func *log;
311     ssize_t length;
312
313     va_start(args, format);
314     length = vsnprintf(NULL, 0, format, args);
315     va_end(args);
316     if (length < 0)
317         return;
318     for (log = notice_handlers; *log != NULL; log++) {
319         va_start(args, format);
320         (**log)((size_t) length, format, args, 0);
321         va_end(args);
322     }
323 }
324
325 void
326 sysnotice(const char *format, ...)
327 {
328     va_list args;
329     message_handler_func *log;
330     ssize_t length;
331     int error = errno;
332
333     va_start(args, format);
334     length = vsnprintf(NULL, 0, format, args);
335     va_end(args);
336     if (length < 0)
337         return;
338     for (log = notice_handlers; *log != NULL; log++) {
339         va_start(args, format);
340         (**log)((size_t) length, format, args, error);
341         va_end(args);
342     }
343 }
344
345 void
346 warn(const char *format, ...)
347 {
348     va_list args;
349     message_handler_func *log;
350     ssize_t length;
351
352     va_start(args, format);
353     length = vsnprintf(NULL, 0, format, args);
354     va_end(args);
355     if (length < 0)
356         return;
357     for (log = warn_handlers; *log != NULL; log++) {
358         va_start(args, format);
359         (**log)((size_t) length, format, args, 0);
360         va_end(args);
361     }
362 }
363
364 void
365 syswarn(const char *format, ...)
366 {
367     va_list args;
368     message_handler_func *log;
369     ssize_t length;
370     int error = errno;
371
372     va_start(args, format);
373     length = vsnprintf(NULL, 0, format, args);
374     va_end(args);
375     if (length < 0)
376         return;
377     for (log = warn_handlers; *log != NULL; log++) {
378         va_start(args, format);
379         (**log)((size_t) length, format, args, error);
380         va_end(args);
381     }
382 }
383
384 void
385 die(const char *format, ...)
386 {
387     va_list args;
388     message_handler_func *log;
389     ssize_t length;
390
391     va_start(args, format);
392     length = vsnprintf(NULL, 0, format, args);
393     va_end(args);
394     if (length >= 0)
395         for (log = die_handlers; *log != NULL; log++) {
396             va_start(args, format);
397             (**log)((size_t) length, format, args, 0);
398             va_end(args);
399         }
400     exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
401 }
402
403 void
404 sysdie(const char *format, ...)
405 {
406     va_list args;
407     message_handler_func *log;
408     ssize_t length;
409     int error = errno;
410
411     va_start(args, format);
412     length = vsnprintf(NULL, 0, format, args);
413     va_end(args);
414     if (length >= 0)
415         for (log = die_handlers; *log != NULL; log++) {
416             va_start(args, format);
417             (**log)((size_t) length, format, args, error);
418             va_end(args);
419         }
420     exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
421 }