]>
Commit | Line | Data |
---|---|---|
6032aa6a MM |
1 | /* |
2 | * BIRD Library -- Logging Functions | |
3 | * | |
73275d85 | 4 | * (c) 1998--2000 Martin Mares <mj@ucw.cz> |
6032aa6a MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
73275d85 MM |
9 | /** |
10 | * DOC: Logging | |
11 | * | |
12 | * The Logging module offers a simple set of functions for writing | |
725270cb MM |
13 | * messages to system logs and to the debug output. Message classes |
14 | * used by this module are described in |birdlib.h| and also in the | |
15 | * user's manual. | |
73275d85 MM |
16 | */ |
17 | ||
6032aa6a MM |
18 | #include <stdio.h> |
19 | #include <stdlib.h> | |
20 | #include <stdarg.h> | |
01b776e1 | 21 | #include <time.h> |
6712e772 OZ |
22 | #include <sys/types.h> |
23 | #include <sys/stat.h> | |
e81b440f | 24 | #include <unistd.h> |
f0b822a8 | 25 | #include <errno.h> |
6032aa6a MM |
26 | |
27 | #include "nest/bird.h" | |
34350a52 | 28 | #include "nest/cli.h" |
863ecfc7 | 29 | #include "conf/conf.h" |
9556f225 | 30 | #include "lib/string.h" |
a0c37b45 | 31 | #include "lib/lists.h" |
7152e5ef | 32 | #include "sysdep/unix/unix.h" |
6032aa6a | 33 | |
44d4ab7a | 34 | static FILE *dbgf; |
a0c37b45 | 35 | static list *current_log_list; |
44d4ab7a | 36 | static char *current_syslog_name; /* NULL -> syslog closed */ |
6032aa6a | 37 | |
0e175f9f | 38 | |
1ec52253 | 39 | #ifdef USE_PTHREADS |
0e175f9f OZ |
40 | |
41 | #include <pthread.h> | |
4e398e34 | 42 | |
0e175f9f OZ |
43 | static pthread_mutex_t log_mutex; |
44 | static inline void log_lock(void) { pthread_mutex_lock(&log_mutex); } | |
45 | static inline void log_unlock(void) { pthread_mutex_unlock(&log_mutex); } | |
46 | ||
4e398e34 OZ |
47 | static pthread_t main_thread; |
48 | void main_thread_init(void) { main_thread = pthread_self(); } | |
49 | static int main_thread_self(void) { return pthread_equal(pthread_self(), main_thread); } | |
50 | ||
1ec52253 OZ |
51 | #else |
52 | ||
53 | static inline void log_lock(void) { } | |
54 | static inline void log_unlock(void) { } | |
4e398e34 OZ |
55 | void main_thread_init(void) { } |
56 | static int main_thread_self(void) { return 1; } | |
1ec52253 OZ |
57 | |
58 | #endif | |
59 | ||
cb530392 | 60 | |
5d6dc930 | 61 | #ifdef HAVE_SYSLOG_H |
6032aa6a MM |
62 | #include <sys/syslog.h> |
63 | ||
64 | static int syslog_priorities[] = { | |
a0c37b45 MM |
65 | LOG_DEBUG, |
66 | LOG_DEBUG, | |
6032aa6a MM |
67 | LOG_DEBUG, |
68 | LOG_INFO, | |
a0c37b45 | 69 | LOG_ERR, |
6032aa6a MM |
70 | LOG_WARNING, |
71 | LOG_ERR, | |
a0c37b45 MM |
72 | LOG_ERR, |
73 | LOG_CRIT, | |
6032aa6a MM |
74 | LOG_CRIT |
75 | }; | |
76 | #endif | |
77 | ||
78 | static char *class_names[] = { | |
79 | "???", | |
80 | "DBG", | |
a0c37b45 | 81 | "TRACE", |
6032aa6a | 82 | "INFO", |
a0c37b45 | 83 | "RMT", |
6032aa6a MM |
84 | "WARN", |
85 | "ERR", | |
86 | "AUTH", | |
a0c37b45 MM |
87 | "FATAL", |
88 | "BUG" | |
6032aa6a MM |
89 | }; |
90 | ||
6712e772 OZ |
91 | static inline off_t |
92 | log_size(struct log_config *l) | |
93 | { | |
94 | struct stat st; | |
95 | return (!fstat(rf_fileno(l->rf), &st) && S_ISREG(st.st_mode)) ? st.st_size : 0; | |
96 | } | |
97 | ||
98 | static void | |
99 | log_close(struct log_config *l) | |
100 | { | |
101 | rfree(l->rf); | |
102 | l->rf = NULL; | |
103 | l->fh = NULL; | |
104 | } | |
105 | ||
106 | static int | |
107 | log_open(struct log_config *l) | |
108 | { | |
109 | l->rf = rf_open(config->pool, l->filename, "a"); | |
110 | if (!l->rf) | |
111 | { | |
112 | /* Well, we cannot do much in case of error as log is closed */ | |
113 | l->mask = 0; | |
114 | return -1; | |
115 | } | |
116 | ||
117 | l->fh = rf_file(l->rf); | |
118 | l->pos = log_size(l); | |
119 | ||
120 | return 0; | |
121 | } | |
122 | ||
123 | static int | |
124 | log_rotate(struct log_config *l) | |
125 | { | |
126 | log_close(l); | |
127 | ||
128 | /* If we cannot rename the logfile, we at least try to delete it | |
129 | in order to continue logging and not exceeding logfile size */ | |
130 | if ((rename(l->filename, l->backup) < 0) && | |
131 | (unlink(l->filename) < 0)) | |
132 | { | |
133 | l->mask = 0; | |
134 | return -1; | |
135 | } | |
136 | ||
137 | return log_open(l); | |
138 | } | |
9556f225 | 139 | |
0d1b3c4c OZ |
140 | /** |
141 | * log_commit - commit a log message | |
142 | * @class: message class information (%L_DEBUG to %L_BUG, see |lib/birdlib.h|) | |
8e433d6a | 143 | * @buf: message to write |
0d1b3c4c OZ |
144 | * |
145 | * This function writes a message prepared in the log buffer to the | |
146 | * log file (as specified in the configuration). The log buffer is | |
147 | * reset after that. The log message is a full line, log_commit() | |
148 | * terminates it. | |
149 | * | |
150 | * The message class is an integer, not a first char of a string like | |
151 | * in log(), so it should be written like *L_INFO. | |
152 | */ | |
153 | void | |
0e175f9f | 154 | log_commit(int class, buffer *buf) |
0d1b3c4c OZ |
155 | { |
156 | struct log_config *l; | |
9556f225 | 157 | |
0e175f9f OZ |
158 | if (buf->pos == buf->end) |
159 | strcpy(buf->end - 100, " ... <too long>"); | |
160 | ||
161 | log_lock(); | |
a0c37b45 | 162 | WALK_LIST(l, *current_log_list) |
6032aa6a | 163 | { |
a0c37b45 MM |
164 | if (!(l->mask & (1 << class))) |
165 | continue; | |
166 | if (l->fh) | |
167 | { | |
a0c37b45 MM |
168 | if (l->terminal_flag) |
169 | fputs("bird: ", l->fh); | |
170 | else | |
171 | { | |
c37e7851 | 172 | byte tbuf[TM_DATETIME_BUFFER_SIZE]; |
863ecfc7 OZ |
173 | if (!tm_format_real_time(tbuf, sizeof(tbuf), config->tf_log.fmt1, current_real_time())) |
174 | strcpy(tbuf, "<error>"); | |
6712e772 OZ |
175 | |
176 | if (l->limit) | |
177 | { | |
178 | off_t msg_len = strlen(tbuf) + strlen(class_names[class]) + | |
179 | (buf->pos - buf->start) + 5; | |
180 | ||
181 | if (l->pos < 0) | |
182 | l->pos = log_size(l); | |
183 | ||
184 | if (l->pos + msg_len > l->limit) | |
185 | if (log_rotate(l) < 0) | |
186 | continue; | |
187 | ||
188 | l->pos += msg_len; | |
189 | } | |
190 | ||
c37e7851 | 191 | fprintf(l->fh, "%s <%s> ", tbuf, class_names[class]); |
a0c37b45 | 192 | } |
0e175f9f | 193 | fputs(buf->start, l->fh); |
a0c37b45 MM |
194 | fputc('\n', l->fh); |
195 | fflush(l->fh); | |
196 | } | |
5d6dc930 | 197 | #ifdef HAVE_SYSLOG_H |
a0c37b45 | 198 | else |
0e175f9f | 199 | syslog(syslog_priorities[class], "%s", buf->start); |
6032aa6a | 200 | #endif |
6032aa6a | 201 | } |
0e175f9f | 202 | log_unlock(); |
0d1b3c4c | 203 | |
4e398e34 OZ |
204 | /* cli_echo is not thread-safe, so call it just from the main thread */ |
205 | if (main_thread_self()) | |
206 | cli_echo(class, buf->start); | |
56027b5c OZ |
207 | |
208 | buf->pos = buf->start; | |
0d1b3c4c OZ |
209 | } |
210 | ||
0e175f9f | 211 | int buffer_vprint(buffer *buf, const char *fmt, va_list args); |
0d1b3c4c OZ |
212 | |
213 | static void | |
214 | vlog(int class, const char *msg, va_list args) | |
215 | { | |
0e175f9f OZ |
216 | buffer buf; |
217 | LOG_BUFFER_INIT(buf); | |
218 | buffer_vprint(&buf, msg, args); | |
219 | log_commit(class, &buf); | |
0d1b3c4c OZ |
220 | } |
221 | ||
222 | ||
73275d85 MM |
223 | /** |
224 | * log - log a message | |
225 | * @msg: printf-like formatting string with message class information | |
226 | * prepended (%L_DEBUG to %L_BUG, see |lib/birdlib.h|) | |
227 | * | |
228 | * This function formats a message according to the format string @msg | |
2e9b2421 | 229 | * and writes it to the corresponding log file (as specified in the |
73275d85 MM |
230 | * configuration). Please note that the message is automatically |
231 | * formatted as a full line, no need to include |\n| inside. | |
0d1b3c4c | 232 | * It is essentially a sequence of log_reset(), logn() and log_commit(). |
73275d85 | 233 | */ |
6032aa6a | 234 | void |
e598853e | 235 | log_msg(const char *msg, ...) |
6032aa6a MM |
236 | { |
237 | int class = 1; | |
238 | va_list args; | |
239 | ||
240 | va_start(args, msg); | |
98e87c86 | 241 | if (*msg >= 1 && *msg <= 8) |
6032aa6a MM |
242 | class = *msg++; |
243 | vlog(class, msg, args); | |
244 | va_end(args); | |
245 | } | |
246 | ||
cb530392 | 247 | void |
e598853e | 248 | log_rl(struct tbf *f, const char *msg, ...) |
cb530392 OZ |
249 | { |
250 | int class = 1; | |
251 | va_list args; | |
252 | ||
1123e707 | 253 | /* Rate limiting is a bit tricky here as it also logs '...' during the first hit */ |
574b2324 | 254 | if (tbf_limit(f) && (f->drop > 1)) |
cb530392 OZ |
255 | return; |
256 | ||
cb530392 OZ |
257 | if (*msg >= 1 && *msg <= 8) |
258 | class = *msg++; | |
1123e707 OZ |
259 | |
260 | va_start(args, msg); | |
574b2324 | 261 | vlog(class, (f->drop ? "..." : msg), args); |
cb530392 OZ |
262 | va_end(args); |
263 | } | |
264 | ||
73275d85 MM |
265 | /** |
266 | * bug - report an internal error | |
267 | * @msg: a printf-like error message | |
268 | * | |
269 | * This function logs an internal error and aborts execution | |
270 | * of the program. | |
271 | */ | |
98e87c86 | 272 | void |
e598853e | 273 | bug(const char *msg, ...) |
98e87c86 MM |
274 | { |
275 | va_list args; | |
276 | ||
277 | va_start(args, msg); | |
278 | vlog(L_BUG[0], msg, args); | |
8f01879c | 279 | va_end(args); |
818ff1e2 | 280 | abort(); |
98e87c86 MM |
281 | } |
282 | ||
73275d85 MM |
283 | /** |
284 | * bug - report a fatal error | |
285 | * @msg: a printf-like error message | |
286 | * | |
287 | * This function logs a fatal error and aborts execution | |
288 | * of the program. | |
289 | */ | |
6032aa6a | 290 | void |
e598853e | 291 | die(const char *msg, ...) |
6032aa6a MM |
292 | { |
293 | va_list args; | |
294 | ||
295 | va_start(args, msg); | |
98e87c86 | 296 | vlog(L_FATAL[0], msg, args); |
8f01879c | 297 | va_end(args); |
6032aa6a MM |
298 | exit(1); |
299 | } | |
300 | ||
73275d85 MM |
301 | /** |
302 | * debug - write to debug output | |
303 | * @msg: a printf-like message | |
304 | * | |
305 | * This function formats the message @msg and prints it out | |
306 | * to the debugging output. No newline character is appended. | |
307 | */ | |
6032aa6a | 308 | void |
e598853e | 309 | debug(const char *msg, ...) |
6032aa6a MM |
310 | { |
311 | va_list args; | |
34350a52 | 312 | char buf[1024]; |
6032aa6a MM |
313 | |
314 | va_start(args, msg); | |
315 | if (dbgf) | |
a0c37b45 MM |
316 | { |
317 | if (bvsnprintf(buf, sizeof(buf), msg, args) < 0) | |
318 | bsprintf(buf + sizeof(buf) - 100, " ... <too long>\n"); | |
319 | fputs(buf, dbgf); | |
320 | } | |
6032aa6a MM |
321 | va_end(args); |
322 | } | |
323 | ||
44d4ab7a OZ |
324 | static list * |
325 | default_log_list(int debug, int init, char **syslog_name) | |
6032aa6a | 326 | { |
44d4ab7a | 327 | static list init_log_list; |
a0c37b45 | 328 | init_list(&init_log_list); |
44d4ab7a | 329 | *syslog_name = NULL; |
6032aa6a | 330 | |
5d6dc930 | 331 | #ifdef HAVE_SYSLOG_H |
a0c37b45 | 332 | if (!debug) |
6032aa6a | 333 | { |
4a591d4b | 334 | static struct log_config lc_syslog = { .mask = ~0 }; |
44d4ab7a OZ |
335 | add_tail(&init_log_list, &lc_syslog.n); |
336 | *syslog_name = bird_name; | |
5ddf4a58 | 337 | if (!init) |
44d4ab7a | 338 | return &init_log_list; |
6032aa6a | 339 | } |
a0c37b45 MM |
340 | #endif |
341 | ||
4a591d4b | 342 | static struct log_config lc_stderr = { .mask = ~0, .terminal_flag = 1 }; |
a0c37b45 | 343 | lc_stderr.fh = stderr; |
44d4ab7a OZ |
344 | add_tail(&init_log_list, &lc_stderr.n); |
345 | return &init_log_list; | |
a0c37b45 MM |
346 | } |
347 | ||
348 | void | |
6712e772 | 349 | log_switch(int debug, list *logs, char *new_syslog_name) |
a0c37b45 | 350 | { |
6712e772 OZ |
351 | struct log_config *l; |
352 | ||
353 | if (!logs || EMPTY_LIST(*logs)) | |
354 | logs = default_log_list(debug, !logs, &new_syslog_name); | |
355 | ||
356 | /* Close the logs to avoid pinning them on disk when deleted */ | |
357 | if (current_log_list) | |
358 | WALK_LIST(l, *current_log_list) | |
359 | if (l->rf) | |
360 | log_close(l); | |
361 | ||
362 | /* Reopen the logs, needed for 'configure undo' */ | |
363 | if (logs) | |
364 | WALK_LIST(l, *logs) | |
365 | if (l->filename && !l->rf) | |
366 | log_open(l); | |
44d4ab7a | 367 | |
6712e772 | 368 | current_log_list = logs; |
44d4ab7a | 369 | |
5d6dc930 | 370 | #ifdef HAVE_SYSLOG_H |
17fe57d8 OZ |
371 | if (current_syslog_name && new_syslog_name && |
372 | !strcmp(current_syslog_name, new_syslog_name)) | |
44d4ab7a OZ |
373 | return; |
374 | ||
17fe57d8 OZ |
375 | if (current_syslog_name) |
376 | { | |
44d4ab7a | 377 | closelog(); |
17fe57d8 OZ |
378 | xfree(current_syslog_name); |
379 | current_syslog_name = NULL; | |
380 | } | |
44d4ab7a OZ |
381 | |
382 | if (new_syslog_name) | |
17fe57d8 OZ |
383 | { |
384 | current_syslog_name = xstrdup(new_syslog_name); | |
385 | openlog(current_syslog_name, LOG_CONS | LOG_NDELAY, LOG_DAEMON); | |
386 | } | |
44d4ab7a | 387 | #endif |
6032aa6a MM |
388 | } |
389 | ||
390 | void | |
391 | log_init_debug(char *f) | |
392 | { | |
393 | if (dbgf && dbgf != stderr) | |
394 | fclose(dbgf); | |
395 | if (!f) | |
6032aa6a | 396 | dbgf = NULL; |
a0c37b45 MM |
397 | else if (!*f) |
398 | dbgf = stderr; | |
6032aa6a | 399 | else if (!(dbgf = fopen(f, "a"))) |
f0b822a8 OZ |
400 | { |
401 | /* Cannot use die() nor log() here, logging is not yet initialized */ | |
402 | fprintf(stderr, "bird: Unable to open debug file %s: %s\n", f, strerror(errno)); | |
403 | exit(1); | |
404 | } | |
f098e072 MM |
405 | if (dbgf) |
406 | setvbuf(dbgf, NULL, _IONBF, 0); | |
6032aa6a | 407 | } |