]> eyrie.org Git - kerberos/krb5-strength.git/blob - util/messages.c
Change my email address to eagle@eyrie.org
[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
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
208     buffer = malloc(len + 1);
209     if (buffer == NULL) {
210         fprintf(stderr, "failed to malloc %lu bytes at %s line %d: %s",
211                 (unsigned long) len + 1, __FILE__, __LINE__, strerror(errno));
212         exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
213     }
214     vsnprintf(buffer, len + 1, fmt, args);
215 #ifdef _WIN32
216     {
217         HANDLE eventlog;
218
219         eventlog = RegisterEventSource(NULL, message_program_name);
220         if (eventlog != NULL) {
221             ReportEvent(eventlog, (WORD) pri, 0, 0, NULL, 1, 0, &buffer, NULL);
222             CloseEventLog(eventlog);
223         }
224     }
225 #else /* !_WIN32 */
226     if (err == 0)
227         syslog(pri, "%s", buffer);
228     else
229         syslog(pri, "%s: %s", buffer, strerror(err));
230 #endif /* !_WIN32 */
231     free(buffer);
232 }
233
234
235 /*
236  * Do the same sort of wrapper to generate all of the separate syslog logging
237  * functions.
238  */
239 #define SYSLOG_FUNCTION(name, type)                                        \
240     void                                                                   \
241     message_log_syslog_ ## name(size_t l, const char *f, va_list a, int e) \
242     {                                                                      \
243         message_log_syslog(LOG_ ## type, l, f, a, e);                      \
244     }
245 SYSLOG_FUNCTION(debug,   DEBUG)
246 SYSLOG_FUNCTION(info,    INFO)
247 SYSLOG_FUNCTION(notice,  NOTICE)
248 SYSLOG_FUNCTION(warning, WARNING)
249 SYSLOG_FUNCTION(err,     ERR)
250 SYSLOG_FUNCTION(crit,    CRIT)
251
252
253 /*
254  * All of the message handlers.  There's a lot of code duplication here too,
255  * but each one is still *slightly* different and va_start has to be called
256  * multiple times, so it's hard to get rid of the duplication.
257  */
258
259 void
260 debug(const char *format, ...)
261 {
262     va_list args;
263     message_handler_func *log;
264     ssize_t length;
265
266     if (debug_handlers == NULL)
267         return;
268     va_start(args, format);
269     length = vsnprintf(NULL, 0, format, args);
270     va_end(args);
271     if (length < 0)
272         return;
273     for (log = debug_handlers; *log != NULL; log++) {
274         va_start(args, format);
275         (**log)((size_t) length, format, args, 0);
276         va_end(args);
277     }
278 }
279
280 void
281 notice(const char *format, ...)
282 {
283     va_list args;
284     message_handler_func *log;
285     ssize_t length;
286
287     va_start(args, format);
288     length = vsnprintf(NULL, 0, format, args);
289     va_end(args);
290     if (length < 0)
291         return;
292     for (log = notice_handlers; *log != NULL; log++) {
293         va_start(args, format);
294         (**log)((size_t) length, format, args, 0);
295         va_end(args);
296     }
297 }
298
299 void
300 sysnotice(const char *format, ...)
301 {
302     va_list args;
303     message_handler_func *log;
304     ssize_t length;
305     int error = errno;
306
307     va_start(args, format);
308     length = vsnprintf(NULL, 0, format, args);
309     va_end(args);
310     if (length < 0)
311         return;
312     for (log = notice_handlers; *log != NULL; log++) {
313         va_start(args, format);
314         (**log)((size_t) length, format, args, error);
315         va_end(args);
316     }
317 }
318
319 void
320 warn(const char *format, ...)
321 {
322     va_list args;
323     message_handler_func *log;
324     ssize_t length;
325
326     va_start(args, format);
327     length = vsnprintf(NULL, 0, format, args);
328     va_end(args);
329     if (length < 0)
330         return;
331     for (log = warn_handlers; *log != NULL; log++) {
332         va_start(args, format);
333         (**log)((size_t) length, format, args, 0);
334         va_end(args);
335     }
336 }
337
338 void
339 syswarn(const char *format, ...)
340 {
341     va_list args;
342     message_handler_func *log;
343     ssize_t length;
344     int error = errno;
345
346     va_start(args, format);
347     length = vsnprintf(NULL, 0, format, args);
348     va_end(args);
349     if (length < 0)
350         return;
351     for (log = warn_handlers; *log != NULL; log++) {
352         va_start(args, format);
353         (**log)((size_t) length, format, args, error);
354         va_end(args);
355     }
356 }
357
358 void
359 die(const char *format, ...)
360 {
361     va_list args;
362     message_handler_func *log;
363     ssize_t length;
364
365     va_start(args, format);
366     length = vsnprintf(NULL, 0, format, args);
367     va_end(args);
368     if (length >= 0)
369         for (log = die_handlers; *log != NULL; log++) {
370             va_start(args, format);
371             (**log)((size_t) length, format, args, 0);
372             va_end(args);
373         }
374     exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
375 }
376
377 void
378 sysdie(const char *format, ...)
379 {
380     va_list args;
381     message_handler_func *log;
382     ssize_t length;
383     int error = errno;
384
385     va_start(args, format);
386     length = vsnprintf(NULL, 0, format, args);
387     va_end(args);
388     if (length >= 0)
389         for (log = die_handlers; *log != NULL; log++) {
390             va_start(args, format);
391             (**log)((size_t) length, format, args, error);
392             va_end(args);
393         }
394     exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
395 }