]>
Commit | Line | Data |
---|---|---|
274a4a44 | 1 | /* |
fb66b29c | 2 | * $Id$ |
274a4a44 | 3 | * |
4 | * Logging of zebra | |
718e3744 | 5 | * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro |
6 | * | |
7 | * This file is part of GNU Zebra. | |
8 | * | |
9 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation; either version 2, or (at your option) any | |
12 | * later version. | |
13 | * | |
14 | * GNU Zebra is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with GNU Zebra; see the file COPYING. If not, write to the Free | |
21 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
22 | * 02111-1307, USA. | |
23 | */ | |
24 | ||
25 | #include <zebra.h> | |
26 | ||
27 | #include "log.h" | |
28 | #include "memory.h" | |
29 | #include "command.h" | |
7d149b8e | 30 | #ifndef SUNOS_5 |
31 | #include <sys/un.h> | |
32 | #endif | |
718e3744 | 33 | |
c4c7d0c4 | 34 | static int logfile_fd = -1; /* Used in signal handler. */ |
1e221354 | 35 | |
718e3744 | 36 | struct zlog *zlog_default = NULL; |
37 | ||
38 | const char *zlog_proto_names[] = | |
39 | { | |
40 | "NONE", | |
41 | "DEFAULT", | |
42 | "ZEBRA", | |
43 | "RIP", | |
44 | "BGP", | |
45 | "OSPF", | |
46 | "RIPNG", | |
47 | "OSPF6", | |
9e867fe6 | 48 | "ISIS", |
718e3744 | 49 | "MASC", |
50 | NULL, | |
51 | }; | |
52 | ||
53 | const char *zlog_priority[] = | |
54 | { | |
55 | "emergencies", | |
56 | "alerts", | |
57 | "critical", | |
58 | "errors", | |
59 | "warnings", | |
60 | "notifications", | |
61 | "informational", | |
62 | "debugging", | |
63 | NULL, | |
64 | }; | |
65 | ||
66 | ||
67 | \f | |
68 | /* For time string format. */ | |
718e3744 | 69 | |
1ed72e0b AS |
70 | size_t |
71 | quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) | |
718e3744 | 72 | { |
1ed72e0b AS |
73 | static struct { |
74 | time_t last; | |
75 | size_t len; | |
76 | char buf[28]; | |
77 | } cache; | |
78 | struct timeval clock; | |
79 | ||
80 | /* would it be sufficient to use global 'recent_time' here? I fear not... */ | |
81 | gettimeofday(&clock, NULL); | |
82 | ||
83 | /* first, we update the cache if the time has changed */ | |
84 | if (cache.last != clock.tv_sec) | |
85 | { | |
86 | struct tm *tm; | |
87 | cache.last = clock.tv_sec; | |
88 | tm = localtime(&cache.last); | |
89 | cache.len = strftime(cache.buf, sizeof(cache.buf), | |
90 | "%Y/%m/%d %H:%M:%S", tm); | |
91 | } | |
92 | /* note: it's not worth caching the subsecond part, because | |
93 | chances are that back-to-back calls are not sufficiently close together | |
94 | for the clock not to have ticked forward */ | |
718e3744 | 95 | |
1ed72e0b AS |
96 | if (buflen > cache.len) |
97 | { | |
98 | memcpy(buf, cache.buf, cache.len); | |
99 | if ((timestamp_precision > 0) && | |
100 | (buflen > cache.len+1+timestamp_precision)) | |
101 | { | |
102 | /* should we worry about locale issues? */ | |
bcdda30b AS |
103 | static const int divisor[] = {0, 100000, 10000, 1000, 100, 10, 1}; |
104 | int prec; | |
105 | char *p = buf+cache.len+1+(prec = timestamp_precision); | |
106 | *p-- = '\0'; | |
107 | while (prec > 6) | |
108 | /* this is unlikely to happen, but protect anyway */ | |
109 | { | |
110 | *p-- = '0'; | |
111 | prec--; | |
112 | } | |
113 | clock.tv_usec /= divisor[prec]; | |
1ed72e0b AS |
114 | do |
115 | { | |
bcdda30b AS |
116 | *p-- = '0'+(clock.tv_usec % 10); |
117 | clock.tv_usec /= 10; | |
1ed72e0b | 118 | } |
bcdda30b AS |
119 | while (--prec > 0); |
120 | *p = '.'; | |
121 | return cache.len+1+timestamp_precision; | |
1ed72e0b AS |
122 | } |
123 | buf[cache.len] = '\0'; | |
124 | return cache.len; | |
125 | } | |
126 | if (buflen > 0) | |
127 | buf[0] = '\0'; | |
128 | return 0; | |
129 | } | |
718e3744 | 130 | |
1ed72e0b AS |
131 | /* Utility routine for current time printing. */ |
132 | static void | |
133 | time_print(FILE *fp, struct timestamp_control *ctl) | |
134 | { | |
135 | if (!ctl->already_rendered) | |
136 | { | |
137 | ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf)); | |
138 | ctl->already_rendered = 1; | |
139 | } | |
140 | fprintf(fp, "%s ", ctl->buf); | |
718e3744 | 141 | } |
1ed72e0b | 142 | |
718e3744 | 143 | \f |
144 | /* va_list version of zlog. */ | |
d246bd96 | 145 | static void |
146 | vzlog (struct zlog *zl, int priority, const char *format, va_list args) | |
718e3744 | 147 | { |
1ed72e0b AS |
148 | struct timestamp_control tsctl; |
149 | tsctl.already_rendered = 0; | |
150 | ||
718e3744 | 151 | /* If zlog is not specified, use default one. */ |
152 | if (zl == NULL) | |
153 | zl = zlog_default; | |
154 | ||
155 | /* When zlog_default is also NULL, use stderr for logging. */ | |
156 | if (zl == NULL) | |
157 | { | |
1ed72e0b AS |
158 | tsctl.precision = 0; |
159 | time_print(stderr, &tsctl); | |
718e3744 | 160 | fprintf (stderr, "%s: ", "unknown"); |
d246bd96 | 161 | vfprintf (stderr, format, args); |
718e3744 | 162 | fprintf (stderr, "\n"); |
163 | fflush (stderr); | |
164 | ||
165 | /* In this case we return at here. */ | |
166 | return; | |
167 | } | |
1ed72e0b | 168 | tsctl.precision = zl->timestamp_precision; |
718e3744 | 169 | |
718e3744 | 170 | /* Syslog output */ |
274a4a44 | 171 | if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) |
d246bd96 | 172 | { |
173 | va_list ac; | |
174 | va_copy(ac, args); | |
175 | vsyslog (priority|zlog_default->facility, format, ac); | |
176 | va_end(ac); | |
177 | } | |
718e3744 | 178 | |
179 | /* File output. */ | |
274a4a44 | 180 | if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) |
718e3744 | 181 | { |
d246bd96 | 182 | va_list ac; |
1ed72e0b | 183 | time_print (zl->fp, &tsctl); |
b04c699e | 184 | if (zl->record_priority) |
185 | fprintf (zl->fp, "%s: ", zlog_priority[priority]); | |
718e3744 | 186 | fprintf (zl->fp, "%s: ", zlog_proto_names[zl->protocol]); |
d246bd96 | 187 | va_copy(ac, args); |
188 | vfprintf (zl->fp, format, ac); | |
189 | va_end(ac); | |
718e3744 | 190 | fprintf (zl->fp, "\n"); |
191 | fflush (zl->fp); | |
192 | } | |
193 | ||
194 | /* stdout output. */ | |
274a4a44 | 195 | if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) |
718e3744 | 196 | { |
d246bd96 | 197 | va_list ac; |
1ed72e0b | 198 | time_print (stdout, &tsctl); |
b04c699e | 199 | if (zl->record_priority) |
200 | fprintf (stdout, "%s: ", zlog_priority[priority]); | |
718e3744 | 201 | fprintf (stdout, "%s: ", zlog_proto_names[zl->protocol]); |
d246bd96 | 202 | va_copy(ac, args); |
203 | vfprintf (stdout, format, ac); | |
204 | va_end(ac); | |
718e3744 | 205 | fprintf (stdout, "\n"); |
206 | fflush (stdout); | |
207 | } | |
208 | ||
718e3744 | 209 | /* Terminal monitor. */ |
274a4a44 | 210 | if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) |
211 | vty_log ((zl->record_priority ? zlog_priority[priority] : NULL), | |
1ed72e0b | 212 | zlog_proto_names[zl->protocol], format, &tsctl, args); |
718e3744 | 213 | } |
214 | ||
59a06a91 | 215 | static char * |
216 | str_append(char *dst, int len, const char *src) | |
217 | { | |
218 | while ((len-- > 0) && *src) | |
219 | *dst++ = *src++; | |
220 | return dst; | |
221 | } | |
222 | ||
223 | static char * | |
224 | num_append(char *s, int len, u_long x) | |
225 | { | |
226 | char buf[30]; | |
7d149b8e | 227 | char *t; |
59a06a91 | 228 | |
7d149b8e | 229 | if (!x) |
230 | return str_append(s,len,"0"); | |
231 | *(t = &buf[sizeof(buf)-1]) = '\0'; | |
59a06a91 | 232 | while (x && (t > buf)) |
233 | { | |
234 | *--t = '0'+(x % 10); | |
235 | x /= 10; | |
236 | } | |
237 | return str_append(s,len,t); | |
238 | } | |
239 | ||
fb66b29c | 240 | #if defined(SA_SIGINFO) || defined(HAVE_STACK_TRACE) |
7d149b8e | 241 | static char * |
242 | hex_append(char *s, int len, u_long x) | |
243 | { | |
244 | char buf[30]; | |
245 | char *t; | |
246 | ||
247 | if (!x) | |
248 | return str_append(s,len,"0"); | |
249 | *(t = &buf[sizeof(buf)-1]) = '\0'; | |
250 | while (x && (t > buf)) | |
251 | { | |
252 | u_int cc = (x % 16); | |
253 | *--t = ((cc < 10) ? ('0'+cc) : ('a'+cc-10)); | |
254 | x /= 16; | |
255 | } | |
256 | return str_append(s,len,t); | |
257 | } | |
31364274 | 258 | #endif |
7d149b8e | 259 | |
7d149b8e | 260 | /* Needs to be enhanced to support Solaris. */ |
261 | static int | |
262 | syslog_connect(void) | |
263 | { | |
264 | #ifdef SUNOS_5 | |
265 | return -1; | |
266 | #else | |
267 | int fd; | |
268 | char *s; | |
269 | struct sockaddr_un addr; | |
270 | ||
271 | if ((fd = socket(AF_UNIX,SOCK_DGRAM,0)) < 0) | |
272 | return -1; | |
273 | addr.sun_family = AF_UNIX; | |
274 | #ifdef _PATH_LOG | |
275 | #define SYSLOG_SOCKET_PATH _PATH_LOG | |
276 | #else | |
277 | #define SYSLOG_SOCKET_PATH "/dev/log" | |
278 | #endif | |
279 | s = str_append(addr.sun_path,sizeof(addr.sun_path),SYSLOG_SOCKET_PATH); | |
280 | #undef SYSLOG_SOCKET_PATH | |
281 | *s = '\0'; | |
282 | if (connect(fd,(struct sockaddr *)&addr,sizeof(addr)) < 0) | |
283 | { | |
284 | close(fd); | |
285 | return -1; | |
286 | } | |
287 | return fd; | |
288 | #endif | |
289 | } | |
290 | ||
291 | static void | |
292 | syslog_sigsafe(int priority, const char *msg, size_t msglen) | |
293 | { | |
1e221354 | 294 | static int syslog_fd = -1; |
7d149b8e | 295 | char buf[sizeof("<1234567890>ripngd[1234567890]: ")+msglen+50]; |
296 | char *s; | |
297 | ||
298 | if ((syslog_fd < 0) && ((syslog_fd = syslog_connect()) < 0)) | |
299 | return; | |
300 | ||
301 | #define LOC s,buf+sizeof(buf)-s | |
302 | s = buf; | |
303 | s = str_append(LOC,"<"); | |
304 | s = num_append(LOC,priority); | |
305 | s = str_append(LOC,">"); | |
306 | /* forget about the timestamp, too difficult in a signal handler */ | |
307 | s = str_append(LOC,zlog_default->ident); | |
308 | if (zlog_default->syslog_options & LOG_PID) | |
309 | { | |
310 | s = str_append(LOC,"["); | |
311 | s = num_append(LOC,getpid()); | |
312 | s = str_append(LOC,"]"); | |
313 | } | |
314 | s = str_append(LOC,": "); | |
315 | s = str_append(LOC,msg); | |
316 | write(syslog_fd,buf,s-buf); | |
317 | #undef LOC | |
318 | } | |
319 | ||
1e221354 | 320 | static int |
321 | open_crashlog(void) | |
322 | { | |
323 | #define CRASHLOG_PREFIX "/var/tmp/quagga." | |
324 | #define CRASHLOG_SUFFIX "crashlog" | |
325 | if (zlog_default && zlog_default->ident) | |
326 | { | |
327 | /* Avoid strlen since it is not async-signal-safe. */ | |
328 | const char *p; | |
329 | size_t ilen; | |
330 | ||
331 | for (p = zlog_default->ident, ilen = 0; *p; p++) | |
332 | ilen++; | |
333 | { | |
334 | char buf[sizeof(CRASHLOG_PREFIX)+ilen+sizeof(CRASHLOG_SUFFIX)+3]; | |
335 | char *s = buf; | |
336 | #define LOC s,buf+sizeof(buf)-s | |
337 | s = str_append(LOC, CRASHLOG_PREFIX); | |
338 | s = str_append(LOC, zlog_default->ident); | |
339 | s = str_append(LOC, "."); | |
340 | s = str_append(LOC, CRASHLOG_SUFFIX); | |
341 | #undef LOC | |
342 | *s = '\0'; | |
343 | return open(buf, O_WRONLY|O_CREAT|O_EXCL, LOGFILE_MASK); | |
344 | } | |
345 | } | |
346 | return open(CRASHLOG_PREFIX CRASHLOG_SUFFIX, O_WRONLY|O_CREAT|O_EXCL, | |
347 | LOGFILE_MASK); | |
348 | #undef CRASHLOG_SUFFIX | |
349 | #undef CRASHLOG_PREFIX | |
350 | } | |
351 | ||
7d149b8e | 352 | /* Note: the goal here is to use only async-signal-safe functions. */ |
59a06a91 | 353 | void |
31364274 | 354 | zlog_signal(int signo, const char *action |
355 | #ifdef SA_SIGINFO | |
356 | , siginfo_t *siginfo, void *program_counter | |
357 | #endif | |
358 | ) | |
59a06a91 | 359 | { |
360 | time_t now; | |
40abf239 | 361 | char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")+100]; |
59a06a91 | 362 | char *s = buf; |
7d149b8e | 363 | char *msgstart = buf; |
59a06a91 | 364 | #define LOC s,buf+sizeof(buf)-s |
365 | ||
366 | time(&now); | |
367 | if (zlog_default) | |
368 | { | |
369 | s = str_append(LOC,zlog_proto_names[zlog_default->protocol]); | |
370 | *s++ = ':'; | |
371 | *s++ = ' '; | |
7d149b8e | 372 | msgstart = s; |
59a06a91 | 373 | } |
374 | s = str_append(LOC,"Received signal "); | |
375 | s = num_append(LOC,signo); | |
376 | s = str_append(LOC," at "); | |
377 | s = num_append(LOC,now); | |
31364274 | 378 | #ifdef SA_SIGINFO |
40abf239 | 379 | s = str_append(LOC," (si_addr 0x"); |
380 | s = hex_append(LOC,(u_long)(siginfo->si_addr)); | |
381 | if (program_counter) | |
382 | { | |
383 | s = str_append(LOC,", PC 0x"); | |
384 | s = hex_append(LOC,(u_long)program_counter); | |
385 | } | |
386 | s = str_append(LOC,"); "); | |
31364274 | 387 | #else /* SA_SIGINFO */ |
388 | s = str_append(LOC,"; "); | |
389 | #endif /* SA_SIGINFO */ | |
59a06a91 | 390 | s = str_append(LOC,action); |
7d149b8e | 391 | if (s < buf+sizeof(buf)) |
392 | *s++ = '\n'; | |
59a06a91 | 393 | |
274a4a44 | 394 | /* N.B. implicit priority is most severe */ |
1e221354 | 395 | #define PRI LOG_CRIT |
274a4a44 | 396 | |
1e221354 | 397 | #define DUMP(FD) write(FD, buf, s-buf); |
398 | /* If no file logging configured, try to write to fallback log file. */ | |
c4c7d0c4 | 399 | if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) |
400 | DUMP(logfile_fd) | |
59a06a91 | 401 | if (!zlog_default) |
c4c7d0c4 | 402 | DUMP(STDERR_FILENO) |
59a06a91 | 403 | else |
404 | { | |
274a4a44 | 405 | if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) |
c4c7d0c4 | 406 | DUMP(STDOUT_FILENO) |
274a4a44 | 407 | /* Remove trailing '\n' for monitor and syslog */ |
408 | *--s = '\0'; | |
409 | if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) | |
410 | vty_log_fixed(buf,s-buf); | |
411 | if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) | |
412 | syslog_sigsafe(PRI|zlog_default->facility,msgstart,s-msgstart); | |
59a06a91 | 413 | } |
414 | #undef DUMP | |
415 | ||
31364274 | 416 | zlog_backtrace_sigsafe(PRI, |
417 | #ifdef SA_SIGINFO | |
418 | program_counter | |
419 | #else | |
420 | NULL | |
421 | #endif | |
422 | ); | |
274a4a44 | 423 | #undef PRI |
063ee52a | 424 | #undef LOC |
425 | } | |
426 | ||
427 | /* Log a backtrace using only async-signal-safe functions. | |
428 | Needs to be enhanced to support syslog logging. */ | |
429 | void | |
239c26fd | 430 | zlog_backtrace_sigsafe(int priority, void *program_counter) |
063ee52a | 431 | { |
fb66b29c | 432 | #ifdef HAVE_STACK_TRACE |
239c26fd | 433 | static const char pclabel[] = "Program counter: "; |
063ee52a | 434 | void *array[20]; |
435 | int size; | |
436 | char buf[100]; | |
437 | char *s; | |
438 | #define LOC s,buf+sizeof(buf)-s | |
59a06a91 | 439 | |
fb66b29c | 440 | #ifdef HAVE_GLIBC_BACKTRACE |
063ee52a | 441 | if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) || |
442 | ((size_t)size > sizeof(array)/sizeof(array[0]))) | |
443 | return; | |
59a06a91 | 444 | |
1e221354 | 445 | #define DUMP(FD) { \ |
239c26fd | 446 | if (program_counter) \ |
447 | { \ | |
1e221354 | 448 | write(FD, pclabel, sizeof(pclabel)-1); \ |
449 | backtrace_symbols_fd(&program_counter, 1, FD); \ | |
239c26fd | 450 | } \ |
1e221354 | 451 | write(FD, buf, s-buf); \ |
452 | backtrace_symbols_fd(array, size, FD); \ | |
59a06a91 | 453 | } |
fb66b29c PJ |
454 | #elif defined(HAVE_PRINTSTACK) |
455 | #define DUMP(FD) { \ | |
456 | if (program_counter) \ | |
457 | write((FD), pclabel, sizeof(pclabel)-1); \ | |
458 | write((FD), buf, s-buf); \ | |
459 | printstack((FD)); \ | |
460 | } | |
461 | #endif /* HAVE_GLIBC_BACKTRACE, HAVE_PRINTSTACK */ | |
462 | ||
463 | s = buf; | |
464 | s = str_append(LOC,"Backtrace for "); | |
465 | s = num_append(LOC,size); | |
466 | s = str_append(LOC," stack frames:\n"); | |
59a06a91 | 467 | |
c4c7d0c4 | 468 | if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) |
469 | DUMP(logfile_fd) | |
59a06a91 | 470 | if (!zlog_default) |
c4c7d0c4 | 471 | DUMP(STDERR_FILENO) |
59a06a91 | 472 | else |
473 | { | |
274a4a44 | 474 | if (priority <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) |
c4c7d0c4 | 475 | DUMP(STDOUT_FILENO) |
274a4a44 | 476 | /* Remove trailing '\n' for monitor and syslog */ |
477 | *--s = '\0'; | |
478 | if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) | |
479 | vty_log_fixed(buf,s-buf); | |
480 | if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) | |
481 | syslog_sigsafe(priority|zlog_default->facility,buf,s-buf); | |
482 | { | |
483 | int i; | |
484 | /* Just print the function addresses. */ | |
485 | for (i = 0; i < size; i++) | |
486 | { | |
487 | s = buf; | |
488 | s = str_append(LOC,"[bt "); | |
489 | s = num_append(LOC,i); | |
490 | s = str_append(LOC,"] 0x"); | |
491 | s = hex_append(LOC,(u_long)(array[i])); | |
492 | *s = '\0'; | |
493 | if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) | |
494 | vty_log_fixed(buf,s-buf); | |
495 | if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) | |
7d149b8e | 496 | syslog_sigsafe(priority|zlog_default->facility,buf,s-buf); |
274a4a44 | 497 | } |
498 | } | |
59a06a91 | 499 | } |
500 | #undef DUMP | |
59a06a91 | 501 | #undef LOC |
fb66b29c | 502 | #endif /* HAVE_STRACK_TRACE */ |
063ee52a | 503 | } |
504 | ||
505 | void | |
506 | zlog_backtrace(int priority) | |
507 | { | |
508 | #ifndef HAVE_GLIBC_BACKTRACE | |
509 | zlog(NULL, priority, "No backtrace available on this platform."); | |
510 | #else | |
511 | void *array[20]; | |
512 | int size, i; | |
513 | char **strings; | |
514 | ||
515 | if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) || | |
516 | ((size_t)size > sizeof(array)/sizeof(array[0]))) | |
517 | { | |
518 | zlog_err("Cannot get backtrace, returned invalid # of frames %d " | |
1ed72e0b AS |
519 | "(valid range is between 1 and %lu)", |
520 | size, (unsigned long)(sizeof(array)/sizeof(array[0]))); | |
063ee52a | 521 | return; |
522 | } | |
523 | zlog(NULL, priority, "Backtrace for %d stack frames:", size); | |
524 | if (!(strings = backtrace_symbols(array, size))) | |
525 | { | |
526 | zlog_err("Cannot get backtrace symbols (out of memory?)"); | |
527 | for (i = 0; i < size; i++) | |
528 | zlog(NULL, priority, "[bt %d] %p",i,array[i]); | |
529 | } | |
530 | else | |
531 | { | |
532 | for (i = 0; i < size; i++) | |
533 | zlog(NULL, priority, "[bt %d] %s",i,strings[i]); | |
534 | free(strings); | |
535 | } | |
536 | #endif /* HAVE_GLIBC_BACKTRACE */ | |
59a06a91 | 537 | } |
538 | ||
718e3744 | 539 | void |
540 | zlog (struct zlog *zl, int priority, const char *format, ...) | |
541 | { | |
d246bd96 | 542 | va_list args; |
718e3744 | 543 | |
d246bd96 | 544 | va_start(args, format); |
718e3744 | 545 | vzlog (zl, priority, format, args); |
d246bd96 | 546 | va_end (args); |
718e3744 | 547 | } |
548 | ||
d246bd96 | 549 | #define ZLOG_FUNC(FUNCNAME,PRIORITY) \ |
550 | void \ | |
551 | FUNCNAME(const char *format, ...) \ | |
552 | { \ | |
553 | va_list args; \ | |
554 | va_start(args, format); \ | |
555 | vzlog (NULL, PRIORITY, format, args); \ | |
556 | va_end(args); \ | |
718e3744 | 557 | } |
558 | ||
d246bd96 | 559 | ZLOG_FUNC(zlog_err, LOG_ERR) |
718e3744 | 560 | |
d246bd96 | 561 | ZLOG_FUNC(zlog_warn, LOG_WARNING) |
718e3744 | 562 | |
d246bd96 | 563 | ZLOG_FUNC(zlog_info, LOG_INFO) |
718e3744 | 564 | |
d246bd96 | 565 | ZLOG_FUNC(zlog_notice, LOG_NOTICE) |
718e3744 | 566 | |
d246bd96 | 567 | ZLOG_FUNC(zlog_debug, LOG_DEBUG) |
718e3744 | 568 | |
d246bd96 | 569 | #undef ZLOG_FUNC |
718e3744 | 570 | |
d246bd96 | 571 | #define PLOG_FUNC(FUNCNAME,PRIORITY) \ |
572 | void \ | |
573 | FUNCNAME(struct zlog *zl, const char *format, ...) \ | |
574 | { \ | |
575 | va_list args; \ | |
576 | va_start(args, format); \ | |
577 | vzlog (zl, PRIORITY, format, args); \ | |
578 | va_end(args); \ | |
718e3744 | 579 | } |
580 | ||
d246bd96 | 581 | PLOG_FUNC(plog_err, LOG_ERR) |
718e3744 | 582 | |
d246bd96 | 583 | PLOG_FUNC(plog_warn, LOG_WARNING) |
718e3744 | 584 | |
d246bd96 | 585 | PLOG_FUNC(plog_info, LOG_INFO) |
718e3744 | 586 | |
d246bd96 | 587 | PLOG_FUNC(plog_notice, LOG_NOTICE) |
718e3744 | 588 | |
d246bd96 | 589 | PLOG_FUNC(plog_debug, LOG_DEBUG) |
718e3744 | 590 | |
d246bd96 | 591 | #undef PLOG_FUNC |
718e3744 | 592 | |
cee3df1e | 593 | void |
594 | _zlog_assert_failed (const char *assertion, const char *file, | |
595 | unsigned int line, const char *function) | |
596 | { | |
c4c7d0c4 | 597 | /* Force fallback file logging? */ |
598 | if (zlog_default && !zlog_default->fp && | |
599 | ((logfile_fd = open_crashlog()) >= 0) && | |
600 | ((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL)) | |
601 | zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR; | |
1e221354 | 602 | zlog(NULL, LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s", |
603 | assertion,file,line,(function ? function : "?")); | |
604 | zlog_backtrace(LOG_CRIT); | |
cee3df1e | 605 | abort(); |
606 | } | |
607 | ||
718e3744 | 608 | \f |
609 | /* Open log stream */ | |
610 | struct zlog * | |
274a4a44 | 611 | openzlog (const char *progname, zlog_proto_t protocol, |
718e3744 | 612 | int syslog_flags, int syslog_facility) |
613 | { | |
614 | struct zlog *zl; | |
274a4a44 | 615 | u_int i; |
718e3744 | 616 | |
274a4a44 | 617 | zl = XCALLOC(MTYPE_ZLOG, sizeof (struct zlog)); |
718e3744 | 618 | |
619 | zl->ident = progname; | |
718e3744 | 620 | zl->protocol = protocol; |
621 | zl->facility = syslog_facility; | |
7d149b8e | 622 | zl->syslog_options = syslog_flags; |
718e3744 | 623 | |
274a4a44 | 624 | /* Set default logging levels. */ |
625 | for (i = 0; i < sizeof(zl->maxlvl)/sizeof(zl->maxlvl[0]); i++) | |
626 | zl->maxlvl[i] = ZLOG_DISABLED; | |
627 | zl->maxlvl[ZLOG_DEST_MONITOR] = LOG_DEBUG; | |
628 | zl->default_lvl = LOG_DEBUG; | |
629 | ||
718e3744 | 630 | openlog (progname, syslog_flags, zl->facility); |
631 | ||
632 | return zl; | |
633 | } | |
634 | ||
635 | void | |
636 | closezlog (struct zlog *zl) | |
637 | { | |
638 | closelog(); | |
639 | fclose (zl->fp); | |
640 | ||
641 | XFREE (MTYPE_ZLOG, zl); | |
642 | } | |
643 | ||
644 | /* Called from command.c. */ | |
645 | void | |
274a4a44 | 646 | zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level) |
718e3744 | 647 | { |
648 | if (zl == NULL) | |
649 | zl = zlog_default; | |
650 | ||
274a4a44 | 651 | zl->maxlvl[dest] = log_level; |
718e3744 | 652 | } |
653 | ||
654 | int | |
274a4a44 | 655 | zlog_set_file (struct zlog *zl, const char *filename, int log_level) |
718e3744 | 656 | { |
657 | FILE *fp; | |
aa593d5e | 658 | mode_t oldumask; |
718e3744 | 659 | |
660 | /* There is opend file. */ | |
661 | zlog_reset_file (zl); | |
662 | ||
663 | /* Set default zl. */ | |
664 | if (zl == NULL) | |
665 | zl = zlog_default; | |
666 | ||
667 | /* Open file. */ | |
aa593d5e | 668 | oldumask = umask (0777 & ~LOGFILE_MASK); |
718e3744 | 669 | fp = fopen (filename, "a"); |
aa593d5e | 670 | umask(oldumask); |
274a4a44 | 671 | if (fp == NULL) |
672 | return 0; | |
718e3744 | 673 | |
674 | /* Set flags. */ | |
675 | zl->filename = strdup (filename); | |
274a4a44 | 676 | zl->maxlvl[ZLOG_DEST_FILE] = log_level; |
718e3744 | 677 | zl->fp = fp; |
c4c7d0c4 | 678 | logfile_fd = fileno(fp); |
718e3744 | 679 | |
680 | return 1; | |
681 | } | |
682 | ||
683 | /* Reset opend file. */ | |
684 | int | |
685 | zlog_reset_file (struct zlog *zl) | |
686 | { | |
687 | if (zl == NULL) | |
688 | zl = zlog_default; | |
689 | ||
718e3744 | 690 | if (zl->fp) |
691 | fclose (zl->fp); | |
692 | zl->fp = NULL; | |
c4c7d0c4 | 693 | logfile_fd = -1; |
274a4a44 | 694 | zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED; |
718e3744 | 695 | |
696 | if (zl->filename) | |
697 | free (zl->filename); | |
698 | zl->filename = NULL; | |
699 | ||
700 | return 1; | |
701 | } | |
702 | ||
703 | /* Reopen log file. */ | |
704 | int | |
705 | zlog_rotate (struct zlog *zl) | |
706 | { | |
274a4a44 | 707 | int level; |
718e3744 | 708 | |
709 | if (zl == NULL) | |
710 | zl = zlog_default; | |
711 | ||
712 | if (zl->fp) | |
713 | fclose (zl->fp); | |
714 | zl->fp = NULL; | |
c4c7d0c4 | 715 | logfile_fd = -1; |
274a4a44 | 716 | level = zl->maxlvl[ZLOG_DEST_FILE]; |
717 | zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED; | |
718e3744 | 718 | |
719 | if (zl->filename) | |
720 | { | |
aa593d5e | 721 | mode_t oldumask; |
274a4a44 | 722 | int save_errno; |
aa593d5e | 723 | |
724 | oldumask = umask (0777 & ~LOGFILE_MASK); | |
274a4a44 | 725 | zl->fp = fopen (zl->filename, "a"); |
726 | save_errno = errno; | |
727 | umask(oldumask); | |
728 | if (zl->fp == NULL) | |
aa593d5e | 729 | { |
274a4a44 | 730 | zlog_err("Log rotate failed: cannot open file %s for append: %s", |
731 | zl->filename, safe_strerror(save_errno)); | |
aa593d5e | 732 | return -1; |
733 | } | |
c4c7d0c4 | 734 | logfile_fd = fileno(zl->fp); |
274a4a44 | 735 | zl->maxlvl[ZLOG_DEST_FILE] = level; |
718e3744 | 736 | } |
737 | ||
738 | return 1; | |
739 | } | |
740 | \f | |
718e3744 | 741 | /* Message lookup function. */ |
8c328f11 | 742 | const char * |
718e3744 | 743 | lookup (struct message *mes, int key) |
744 | { | |
745 | struct message *pnt; | |
746 | ||
747 | for (pnt = mes; pnt->key != 0; pnt++) | |
748 | if (pnt->key == key) | |
749 | return pnt->str; | |
750 | ||
751 | return ""; | |
752 | } | |
753 | ||
afb88a66 AS |
754 | /* Older/faster version of message lookup function, but requires caller to pass |
755 | in the array size (instead of relying on a 0 key to terminate the search). */ | |
8c328f11 | 756 | const char * |
718e3744 | 757 | mes_lookup (struct message *meslist, int max, int index) |
758 | { | |
afb88a66 AS |
759 | /* first check for best case: index is in range and matches the key |
760 | value in that slot */ | |
761 | if ((index >= 0) && (index < max) && (meslist[index].key == index)) | |
762 | return meslist[index].str; | |
763 | ||
764 | /* fall back to linear search */ | |
765 | { | |
766 | int i; | |
767 | ||
768 | for (i = 0; i < max; i++, meslist++) | |
769 | { | |
770 | if (meslist->key == index) | |
771 | { | |
772 | zlog_warn("message index %d [%s] found in position %d (max is %d)", | |
773 | index, meslist->str, i, max); | |
774 | return meslist->str; | |
775 | } | |
776 | } | |
777 | } | |
778 | zlog_err("message index %d not found (max is %d)", index, max); | |
779 | return NULL; | |
718e3744 | 780 | } |
ca359769 | 781 | |
782 | /* Wrapper around strerror to handle case where it returns NULL. */ | |
783 | const char * | |
784 | safe_strerror(int errnum) | |
785 | { | |
786 | const char *s = strerror(errnum); | |
787 | return (s != NULL) ? s : "Unknown error"; | |
788 | } | |
f52d13cb | 789 | |
d6d672aa PJ |
790 | struct zebra_desc_table |
791 | { | |
792 | unsigned int type; | |
f52d13cb | 793 | const char *string; |
794 | char chr; | |
f52d13cb | 795 | }; |
796 | ||
d6d672aa PJ |
797 | #define DESC_ENTRY(T,S,C) [(T)] = { (T), (S), (C) } |
798 | static const struct zebra_desc_table route_types[] = { | |
799 | DESC_ENTRY (ZEBRA_ROUTE_SYSTEM, "system", 'X' ), | |
800 | DESC_ENTRY (ZEBRA_ROUTE_KERNEL, "kernel", 'K' ), | |
801 | DESC_ENTRY (ZEBRA_ROUTE_CONNECT, "connected", 'C' ), | |
802 | DESC_ENTRY (ZEBRA_ROUTE_STATIC, "static", 'S' ), | |
803 | DESC_ENTRY (ZEBRA_ROUTE_RIP, "rip", 'R' ), | |
804 | DESC_ENTRY (ZEBRA_ROUTE_RIPNG, "ripng", 'R' ), | |
805 | DESC_ENTRY (ZEBRA_ROUTE_OSPF, "ospf", 'O' ), | |
806 | DESC_ENTRY (ZEBRA_ROUTE_OSPF6, "ospf6", 'O' ), | |
807 | DESC_ENTRY (ZEBRA_ROUTE_ISIS, "isis", 'I' ), | |
808 | DESC_ENTRY (ZEBRA_ROUTE_BGP, "bgp", 'B' ), | |
809 | DESC_ENTRY (ZEBRA_ROUTE_HSLS, "hsls", 'H' ), | |
810 | }; | |
811 | #undef DESC_ENTRY | |
812 | ||
813 | #define DESC_ENTRY(T) [(T)] = { (T), (#T), '\0' } | |
814 | static const struct zebra_desc_table command_types[] = { | |
815 | DESC_ENTRY (ZEBRA_INTERFACE_ADD), | |
816 | DESC_ENTRY (ZEBRA_INTERFACE_DELETE), | |
817 | DESC_ENTRY (ZEBRA_INTERFACE_ADDRESS_ADD), | |
818 | DESC_ENTRY (ZEBRA_INTERFACE_ADDRESS_DELETE), | |
819 | DESC_ENTRY (ZEBRA_INTERFACE_UP), | |
820 | DESC_ENTRY (ZEBRA_INTERFACE_DOWN), | |
821 | DESC_ENTRY (ZEBRA_IPV4_ROUTE_ADD), | |
822 | DESC_ENTRY (ZEBRA_IPV4_ROUTE_DELETE), | |
823 | DESC_ENTRY (ZEBRA_IPV6_ROUTE_ADD), | |
824 | DESC_ENTRY (ZEBRA_IPV6_ROUTE_DELETE), | |
825 | DESC_ENTRY (ZEBRA_REDISTRIBUTE_ADD), | |
826 | DESC_ENTRY (ZEBRA_REDISTRIBUTE_DELETE), | |
827 | DESC_ENTRY (ZEBRA_REDISTRIBUTE_DEFAULT_ADD), | |
828 | DESC_ENTRY (ZEBRA_REDISTRIBUTE_DEFAULT_DELETE), | |
829 | DESC_ENTRY (ZEBRA_IPV4_NEXTHOP_LOOKUP), | |
830 | DESC_ENTRY (ZEBRA_IPV6_NEXTHOP_LOOKUP), | |
831 | DESC_ENTRY (ZEBRA_IPV4_IMPORT_LOOKUP), | |
832 | DESC_ENTRY (ZEBRA_IPV6_IMPORT_LOOKUP), | |
833 | DESC_ENTRY (ZEBRA_INTERFACE_RENAME), | |
834 | DESC_ENTRY (ZEBRA_ROUTER_ID_ADD), | |
835 | DESC_ENTRY (ZEBRA_ROUTER_ID_DELETE), | |
836 | DESC_ENTRY (ZEBRA_ROUTER_ID_UPDATE), | |
837 | }; | |
838 | #undef DESC_ENTRY | |
839 | ||
840 | static const struct zebra_desc_table unknown = { 0, "unknown", '?' }; | |
841 | ||
842 | static const struct zebra_desc_table * | |
f52d13cb | 843 | zroute_lookup(u_int zroute) |
844 | { | |
f52d13cb | 845 | u_int i; |
846 | ||
847 | if (zroute >= sizeof(route_types)/sizeof(route_types[0])) | |
848 | { | |
849 | zlog_err("unknown zebra route type: %u", zroute); | |
850 | return &unknown; | |
851 | } | |
d6d672aa | 852 | if (zroute == route_types[zroute].type) |
f52d13cb | 853 | return &route_types[zroute]; |
854 | for (i = 0; i < sizeof(route_types)/sizeof(route_types[0]); i++) | |
855 | { | |
d6d672aa | 856 | if (zroute == route_types[i].type) |
f52d13cb | 857 | { |
858 | zlog_warn("internal error: route type table out of order " | |
859 | "while searching for %u, please notify developers", zroute); | |
860 | return &route_types[i]; | |
861 | } | |
862 | } | |
863 | zlog_err("internal error: cannot find route type %u in table!", zroute); | |
864 | return &unknown; | |
865 | } | |
866 | ||
867 | const char * | |
868 | zebra_route_string(u_int zroute) | |
869 | { | |
870 | return zroute_lookup(zroute)->string; | |
871 | } | |
872 | ||
873 | char | |
874 | zebra_route_char(u_int zroute) | |
875 | { | |
876 | return zroute_lookup(zroute)->chr; | |
877 | } | |
d6d672aa PJ |
878 | |
879 | const char * | |
880 | zserv_command_string (unsigned int command) | |
881 | { | |
882 | if (command >= sizeof(command_types)/sizeof(command_types[0])) | |
883 | { | |
884 | zlog_err ("unknown zserv command type: %u", command); | |
885 | return unknown.string; | |
886 | } | |
887 | return command_types[command].string; | |
888 | } |