]> eyrie.org Git - kerberos/krb5-strength.git/blob - util/messages.c
Fix branch name in GitHub CI configuration
[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, 2020 Russ Allbery <eagle@eyrie.org>
58  * Copyright 2008-2010, 2013-2014
59  *     The Board of Trustees of the Leland Stanford Junior University
60  * Copyright 2004-2006 Internet Systems Consortium, Inc. ("ISC")
61  * Copyright 1991, 1994-2003 The Internet Software Consortium and Rich Salz
62  *
63  * This code is derived from software contributed to the Internet Software
64  * Consortium by Rich Salz.
65  *
66  * Permission to use, copy, modify, and distribute this software for any
67  * purpose with or without fee is hereby granted, provided that the above
68  * copyright notice and this permission notice appear in all copies.
69  *
70  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
71  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
72  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
73  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
74  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
75  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
76  * PERFORMANCE OF THIS SOFTWARE.
77  *
78  * SPDX-License-Identifier: ISC
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 /* clang-format off */
144 #define HANDLER_FUNCTION(type)                              \
145     void                                                    \
146     message_handlers_ ## type(unsigned int count, ...)      \
147     {                                                       \
148         va_list args;                                       \
149                                                             \
150         va_start(args, count);                              \
151         message_handlers(& type ## _handlers, count, args); \
152         va_end(args);                                       \
153     }
154 /* clang-format on */
155 HANDLER_FUNCTION(debug)
156 HANDLER_FUNCTION(notice)
157 HANDLER_FUNCTION(warn)
158 HANDLER_FUNCTION(die)
159
160
161 /*
162  * Reset all handlers back to the defaults and free all allocated memory.
163  * This is primarily useful for programs that undergo comprehensive memory
164  * allocation analysis.
165  */
166 void
167 message_handlers_reset(void)
168 {
169     free(debug_handlers);
170     debug_handlers = NULL;
171     if (notice_handlers != stdout_handlers) {
172         free(notice_handlers);
173         notice_handlers = stdout_handlers;
174     }
175     if (warn_handlers != stderr_handlers) {
176         free(warn_handlers);
177         warn_handlers = stderr_handlers;
178     }
179     if (die_handlers != stderr_handlers) {
180         free(die_handlers);
181         die_handlers = stderr_handlers;
182     }
183 }
184
185
186 /*
187  * Print a message to stdout, supporting message_program_name.
188  */
189 void
190 message_log_stdout(size_t len UNUSED, const char *fmt, va_list args, int err)
191 {
192     if (message_program_name != NULL)
193         fprintf(stdout, "%s: ", message_program_name);
194     vfprintf(stdout, fmt, args);
195     if (err)
196         fprintf(stdout, ": %s", strerror(err));
197     fprintf(stdout, "\n");
198     fflush(stdout);
199 }
200
201
202 /*
203  * Print a message to stderr, supporting message_program_name.  Also flush
204  * stdout so that errors and regular output occur in the right order.
205  */
206 void
207 message_log_stderr(size_t len UNUSED, const char *fmt, va_list args, int err)
208 {
209     fflush(stdout);
210     if (message_program_name != NULL)
211         fprintf(stderr, "%s: ", message_program_name);
212     vfprintf(stderr, fmt, args);
213     if (err)
214         fprintf(stderr, ": %s", strerror(err));
215     fprintf(stderr, "\n");
216 }
217
218
219 /*
220  * Log a message to syslog.  This is a helper function used to implement all
221  * of the syslog message log handlers.  It takes the same arguments as a
222  * regular message handler function but with an additional priority argument.
223  *
224  * This needs further attention on Windows.  For example, it currently doesn't
225  * log the errno information.
226  */
227 static void __attribute__((__format__(printf, 3, 0)))
228 message_log_syslog(int pri, size_t len, const char *fmt, va_list args, int err)
229 {
230     char *buffer;
231     int status;
232
233     buffer = malloc(len + 1);
234     if (buffer == NULL) {
235         fprintf(stderr, "failed to malloc %lu bytes at %s line %d: %s",
236                 (unsigned long) len + 1, __FILE__, __LINE__, strerror(errno));
237         exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
238     }
239     status = vsnprintf(buffer, len + 1, fmt, args);
240     if (status < 0 || (size_t) status >= len + 1) {
241         warn("failed to format output with vsnprintf in syslog handler");
242         free(buffer);
243         return;
244     }
245 #ifdef _WIN32
246     {
247         HANDLE eventlog;
248
249         eventlog = RegisterEventSource(NULL, message_program_name);
250         if (eventlog != NULL) {
251             ReportEvent(eventlog, (WORD) pri, 0, 0, NULL, 1, 0, &buffer, NULL);
252             CloseEventLog(eventlog);
253         }
254     }
255 #else  /* !_WIN32 */
256     if (err == 0)
257         syslog(pri, "%s", buffer);
258     else
259         syslog(pri, "%s: %s", buffer, strerror(err));
260 #endif /* !_WIN32 */
261     free(buffer);
262 }
263
264
265 /*
266  * Do the same sort of wrapper to generate all of the separate syslog logging
267  * functions.
268  */
269 /* clang-format off */
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 /* clang-format on */
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 }