chiark / gitweb /
error/fatal/info -> disorder_error/fatal/info
[disorder] / lib / log.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004-2008 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /** @file lib/log.c @brief Errors and logging
19  *
20  * All messages are initially emitted by one of the four functions
21  * below.  disorder_debug() is generally invoked via D() so that
22  * mostly you just do a test rather than a complete subroutine call.
23  *
24  * Messages are dispatched via @ref log_default.  This defaults to @ref
25  * log_stderr.  daemonize() will turn off @ref log_stderr and use @ref
26  * log_syslog instead.
27  *
28  * disorder_fatal() will call exitfn() with a nonzero status.  The
29  * default value is exit(), but it should be set to _exit() anywhere
30  * but the 'main line' of the program, to guarantee that exit() gets
31  * called at most once.
32  */
33
34 #define NO_MEMORY_ALLOCATION
35 /* because the memory allocation functions report errors */
36
37 #include "common.h"
38
39 #include <errno.h>
40 #include <syslog.h>
41 #include <sys/time.h>
42 #include <time.h>
43
44 #include "log.h"
45 #include "disorder.h"
46 #include "printf.h"
47
48 /** @brief Definition of a log output */
49 struct log_output {
50   /** @brief Function to call */
51   void (*fn)(int pri, const char *msg, void *user);
52   /** @brief User data */
53   void *user;
54 };
55
56 /** @brief Function to call on a fatal error
57  *
58  * This is normally @c exit() but in the presence of @c fork() it
59  * sometimes gets set to @c _exit(). */
60 void (*exitfn)(int) attribute((noreturn)) = exit;
61
62 /** @brief Debug flag */
63 int debugging;
64
65 /** @brief Program name */
66 const char *progname;
67
68 /** @brief Filename for debug messages */
69 const char *debug_filename;
70
71 /** @brief Set to include timestamps in log messages */
72 int logdate;
73
74 /** @brief Line number for debug messages */
75 int debug_lineno;
76
77 /** @brief Pointer to chosen log output structure */
78 struct log_output *log_default = &log_stderr;
79
80 /** @brief Filename to debug for */
81 static const char *debug_only;
82
83 /** @brief Construct log line, encoding special characters
84  *
85  * We might be receiving things in any old encoding, or binary rubbish
86  * in no encoding at all, so escape anything we don't like the look
87  * of.  We limit the log message to a kilobyte.
88  */
89 static void format(char buffer[], size_t bufsize, const char *fmt, va_list ap) {
90   char t[1024];
91   const char *p;
92   int ch;
93   size_t n = 0;
94   
95   if(byte_vsnprintf(t, sizeof t, fmt, ap) < 0) {
96     strcpy(t, "[byte_vsnprintf failed: ");
97     strncat(t, fmt, sizeof t - strlen(t) - 1);
98   }
99   p = t;
100   while((ch = (unsigned char)*p++)) {
101     if(ch >= ' ' && ch <= 126) {
102       if(n < bufsize) buffer[n++] = ch;
103     } else {
104       if(n < bufsize) buffer[n++] = '\\';
105       if(n < bufsize) buffer[n++] = '0' + ((ch >> 6) & 7);
106       if(n < bufsize) buffer[n++] = '0' + ((ch >> 3) & 7);
107       if(n < bufsize) buffer[n++] = '0' + ((ch >> 0) & 7);
108     }
109   }
110   if(n >= bufsize)
111     n = bufsize - 1;
112   buffer[n] = 0;
113 }
114
115 /** @brief Log to a file
116  * @param pri Message priority (as per syslog)
117  * @param msg Messagge to log
118  * @param user The @c FILE @c * to log to or NULL for @c stderr
119  */
120 static void logfp(int pri, const char *msg, void *user) {
121   struct timeval tv;
122   FILE *fp = user ? user : stderr;
123   /* ...because stderr is not a constant so we can't initialize log_stderr
124    * sanely */
125   const char *p;
126   
127   if(logdate) {
128     char timebuf[64];
129     struct tm *tm;
130     gettimeofday(&tv, 0);
131     tm = localtime(&tv.tv_sec);
132     strftime(timebuf, sizeof timebuf, "%Y-%m-%d %H:%M:%S %Z", tm);
133     fprintf(fp, "%s: ", timebuf);
134   } 
135  if(progname)
136     fprintf(fp, "%s: ", progname);
137   if(pri <= LOG_ERR)
138     fputs("ERROR: ", fp);
139   else if(pri < LOG_DEBUG)
140     fputs("INFO: ", fp);
141   else {
142     if(!debug_only) {
143       if(!(debug_only = getenv("DISORDER_DEBUG_ONLY")))
144         debug_only = "";
145     }
146     gettimeofday(&tv, 0);
147     p = debug_filename;
148     while(!strncmp(p, "../", 3)) p += 3;
149     if(*debug_only && strcmp(p, debug_only))
150       return;
151     fprintf(fp, "%llu.%06lu: %s:%d: ",
152             (unsigned long long)tv.tv_sec, (unsigned long)tv.tv_usec,
153             p, debug_lineno);
154   }
155   fputs(msg, fp);
156   fputc('\n', fp);
157 }
158
159 /** @brief Log to syslog */
160 static void logsyslog(int pri, const char *msg,
161                       void attribute((unused)) *user) {
162   if(pri < LOG_DEBUG)
163     syslog(pri, "%s", msg);
164   else
165     syslog(pri, "%s:%d: %s", debug_filename, debug_lineno, msg);
166 }
167
168 /** @brief Log output that writes to @c stderr */
169 struct log_output log_stderr = { logfp, 0 };
170
171 /** @brief Log output that sends to syslog */
172 struct log_output log_syslog = { logsyslog, 0 };
173
174 /** @brief Format and log a message */
175 static void vlogger(int pri, const char *fmt, va_list ap) {
176   char buffer[1024];
177
178   format(buffer, sizeof buffer, fmt, ap);
179   log_default->fn(pri, buffer, log_default->user);
180 }
181
182 /** @brief Format and log a message */
183 static void logger(int pri, const char *fmt, ...) {
184   va_list ap;
185
186   va_start(ap, fmt);
187   vlogger(pri, fmt, ap);
188   va_end(ap);
189 }
190
191 /** @brief Format and log a message
192  * @param pri Message priority (as per syslog)
193  * @param fmt Format string
194  * @param errno_value Errno value to include as a string, or 0
195  * @param ap Argument list
196  */
197 void elog(int pri, int errno_value, const char *fmt, va_list ap) {
198   char buffer[1024];
199
200   if(errno_value == 0)
201     vlogger(pri, fmt, ap);
202   else {
203     memset(buffer, 0, sizeof buffer);
204     byte_vsnprintf(buffer, sizeof buffer, fmt, ap);
205     buffer[sizeof buffer - 1] = 0;
206     logger(pri, "%s: %s", buffer, strerror(errno_value));
207   }
208 }
209
210 /** @brief Log an error and quit
211  *
212  * If @c ${DISORDER_FATAL_ABORT} is defined (as anything) then the process
213  * is aborted, so you can get a backtrace.
214  */
215 void disorder_fatal(int errno_value, const char *msg, ...) {
216   va_list ap;
217
218   va_start(ap, msg);
219   elog(LOG_CRIT, errno_value, msg, ap);
220   va_end(ap);
221   if(getenv("DISORDER_FATAL_ABORT")) abort();
222   exitfn(EXIT_FAILURE);
223 }
224
225 /** @brief Log an error */
226 void disorder_error(int errno_value, const char *msg, ...) {
227   va_list ap;
228
229   va_start(ap, msg);
230   elog(LOG_ERR, errno_value, msg, ap);
231   va_end(ap);
232 }
233
234 /** @brief Log an informational message */
235 void disorder_info(const char *msg, ...) {
236   va_list ap;
237
238   va_start(ap, msg);
239   elog(LOG_INFO, 0, msg, ap);
240   va_end(ap);
241 }
242
243 /** @brief Log a debug message */
244 void disorder_debug(const char *msg, ...) {
245   va_list ap;
246
247   va_start(ap, msg);
248   vlogger(LOG_DEBUG, msg, ap);
249   va_end(ap);
250 }
251
252 /** @brief Set the program name from @c argc */
253 void set_progname(char **argv) {
254   if((progname = strrchr(argv[0], '/')))
255     ++progname;
256   else
257     progname = argv[0];
258 }
259
260 /*
261 Local Variables:
262 c-basic-offset:2
263 comment-column:40
264 End:
265 */