]> eyrie.org Git - kerberos/krb5-strength.git/blob - util/messages.c
Imported Upstream version 2.2
[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  * Print a message to stdout, supporting message_program_name.
164  */
165 void
166 message_log_stdout(size_t len UNUSED, const char *fmt, va_list args, int err)
167 {
168     if (message_program_name != NULL)
169         fprintf(stdout, "%s: ", message_program_name);
170     vfprintf(stdout, fmt, args);
171     if (err)
172         fprintf(stdout, ": %s", strerror(err));
173     fprintf(stdout, "\n");
174     fflush(stdout);
175 }
176
177
178 /*
179  * Print a message to stderr, supporting message_program_name.  Also flush
180  * stdout so that errors and regular output occur in the right order.
181  */
182 void
183 message_log_stderr(size_t len UNUSED, const char *fmt, va_list args, int err)
184 {
185     fflush(stdout);
186     if (message_program_name != NULL)
187         fprintf(stderr, "%s: ", message_program_name);
188     vfprintf(stderr, fmt, args);
189     if (err)
190         fprintf(stderr, ": %s", strerror(err));
191     fprintf(stderr, "\n");
192 }
193
194
195 /*
196  * Log a message to syslog.  This is a helper function used to implement all
197  * of the syslog message log handlers.  It takes the same arguments as a
198  * regular message handler function but with an additional priority argument.
199  *
200  * This needs further attention on Windows.  For example, it currently doesn't
201  * log the errno information.
202  */
203 static void
204 message_log_syslog(int pri, size_t len, const char *fmt, va_list args, int err)
205 {
206     char *buffer;
207     int status;
208
209     buffer = malloc(len + 1);
210     if (buffer == NULL) {
211         fprintf(stderr, "failed to malloc %lu bytes at %s line %d: %s",
212                 (unsigned long) len + 1, __FILE__, __LINE__, strerror(errno));
213         exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
214     }
215     status = vsnprintf(buffer, len + 1, fmt, args);
216     if (status < 0) {
217         warn("failed to format output with vsnprintf in syslog handler");
218         free(buffer);
219         return;
220     }
221 #ifdef _WIN32
222     {
223         HANDLE eventlog;
224
225         eventlog = RegisterEventSource(NULL, message_program_name);
226         if (eventlog != NULL) {
227             ReportEvent(eventlog, (WORD) pri, 0, 0, NULL, 1, 0, &buffer, NULL);
228             CloseEventLog(eventlog);
229         }
230     }
231 #else /* !_WIN32 */
232     if (err == 0)
233         syslog(pri, "%s", buffer);
234     else
235         syslog(pri, "%s: %s", buffer, strerror(err));
236 #endif /* !_WIN32 */
237     free(buffer);
238 }
239
240
241 /*
242  * Do the same sort of wrapper to generate all of the separate syslog logging
243  * functions.
244  */
245 #define SYSLOG_FUNCTION(name, type)                                        \
246     void                                                                   \
247     message_log_syslog_ ## name(size_t l, const char *f, va_list a, int e) \
248     {                                                                      \
249         message_log_syslog(LOG_ ## type, l, f, a, e);                      \
250     }
251 SYSLOG_FUNCTION(debug,   DEBUG)
252 SYSLOG_FUNCTION(info,    INFO)
253 SYSLOG_FUNCTION(notice,  NOTICE)
254 SYSLOG_FUNCTION(warning, WARNING)
255 SYSLOG_FUNCTION(err,     ERR)
256 SYSLOG_FUNCTION(crit,    CRIT)
257
258
259 /*
260  * All of the message handlers.  There's a lot of code duplication here too,
261  * but each one is still *slightly* different and va_start has to be called
262  * multiple times, so it's hard to get rid of the duplication.
263  */
264
265 void
266 debug(const char *format, ...)
267 {
268     va_list args;
269     message_handler_func *log;
270     ssize_t length;
271
272     if (debug_handlers == NULL)
273         return;
274     va_start(args, format);
275     length = vsnprintf(NULL, 0, format, args);
276     va_end(args);
277     if (length < 0)
278         return;
279     for (log = debug_handlers; *log != NULL; log++) {
280         va_start(args, format);
281         (**log)((size_t) length, format, args, 0);
282         va_end(args);
283     }
284 }
285
286 void
287 notice(const char *format, ...)
288 {
289     va_list args;
290     message_handler_func *log;
291     ssize_t length;
292
293     va_start(args, format);
294     length = vsnprintf(NULL, 0, format, args);
295     va_end(args);
296     if (length < 0)
297         return;
298     for (log = notice_handlers; *log != NULL; log++) {
299         va_start(args, format);
300         (**log)((size_t) length, format, args, 0);
301         va_end(args);
302     }
303 }
304
305 void
306 sysnotice(const char *format, ...)
307 {
308     va_list args;
309     message_handler_func *log;
310     ssize_t length;
311     int error = errno;
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, error);
321         va_end(args);
322     }
323 }
324
325 void
326 warn(const char *format, ...)
327 {
328     va_list args;
329     message_handler_func *log;
330     ssize_t length;
331
332     va_start(args, format);
333     length = vsnprintf(NULL, 0, format, args);
334     va_end(args);
335     if (length < 0)
336         return;
337     for (log = warn_handlers; *log != NULL; log++) {
338         va_start(args, format);
339         (**log)((size_t) length, format, args, 0);
340         va_end(args);
341     }
342 }
343
344 void
345 syswarn(const char *format, ...)
346 {
347     va_list args;
348     message_handler_func *log;
349     ssize_t length;
350     int error = errno;
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, error);
360         va_end(args);
361     }
362 }
363
364 void
365 die(const char *format, ...)
366 {
367     va_list args;
368     message_handler_func *log;
369     ssize_t length;
370
371     va_start(args, format);
372     length = vsnprintf(NULL, 0, format, args);
373     va_end(args);
374     if (length >= 0)
375         for (log = die_handlers; *log != NULL; log++) {
376             va_start(args, format);
377             (**log)((size_t) length, format, args, 0);
378             va_end(args);
379         }
380     exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
381 }
382
383 void
384 sysdie(const char *format, ...)
385 {
386     va_list args;
387     message_handler_func *log;
388     ssize_t length;
389     int error = errno;
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, error);
398             va_end(args);
399         }
400     exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
401 }