]> git.proxmox.com Git - mirror_frr.git/blame - lib/log.c
2005-10-01 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
[mirror_frr.git] / lib / log.c
CommitLineData
274a4a44 1/*
c4c7d0c4 2 * $Id: log.c,v 1.25 2005/02/03 19:22:05 ajs Exp $
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 34static int logfile_fd = -1; /* Used in signal handler. */
1e221354 35
718e3744 36struct zlog *zlog_default = NULL;
37
38const 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
53const 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. */
69#define TIME_BUF 27
70
71/* Utility routine for current time printing. */
72static void
73time_print (FILE *fp)
74{
75 int ret;
76 char buf [TIME_BUF];
77 time_t clock;
78 struct tm *tm;
79
80 time (&clock);
81 tm = localtime (&clock);
82
83 ret = strftime (buf, TIME_BUF, "%Y/%m/%d %H:%M:%S", tm);
84 if (ret == 0) {
85 zlog_warn ("strftime error");
86 }
87
88 fprintf (fp, "%s ", buf);
89}
90\f
91/* va_list version of zlog. */
d246bd96 92static void
93vzlog (struct zlog *zl, int priority, const char *format, va_list args)
718e3744 94{
95 /* If zlog is not specified, use default one. */
96 if (zl == NULL)
97 zl = zlog_default;
98
99 /* When zlog_default is also NULL, use stderr for logging. */
100 if (zl == NULL)
101 {
102 time_print (stderr);
103 fprintf (stderr, "%s: ", "unknown");
d246bd96 104 vfprintf (stderr, format, args);
718e3744 105 fprintf (stderr, "\n");
106 fflush (stderr);
107
108 /* In this case we return at here. */
109 return;
110 }
111
718e3744 112 /* Syslog output */
274a4a44 113 if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
d246bd96 114 {
115 va_list ac;
116 va_copy(ac, args);
117 vsyslog (priority|zlog_default->facility, format, ac);
118 va_end(ac);
119 }
718e3744 120
121 /* File output. */
274a4a44 122 if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
718e3744 123 {
d246bd96 124 va_list ac;
718e3744 125 time_print (zl->fp);
b04c699e 126 if (zl->record_priority)
127 fprintf (zl->fp, "%s: ", zlog_priority[priority]);
718e3744 128 fprintf (zl->fp, "%s: ", zlog_proto_names[zl->protocol]);
d246bd96 129 va_copy(ac, args);
130 vfprintf (zl->fp, format, ac);
131 va_end(ac);
718e3744 132 fprintf (zl->fp, "\n");
133 fflush (zl->fp);
134 }
135
136 /* stdout output. */
274a4a44 137 if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
718e3744 138 {
d246bd96 139 va_list ac;
718e3744 140 time_print (stdout);
b04c699e 141 if (zl->record_priority)
142 fprintf (stdout, "%s: ", zlog_priority[priority]);
718e3744 143 fprintf (stdout, "%s: ", zlog_proto_names[zl->protocol]);
d246bd96 144 va_copy(ac, args);
145 vfprintf (stdout, format, ac);
146 va_end(ac);
718e3744 147 fprintf (stdout, "\n");
148 fflush (stdout);
149 }
150
718e3744 151 /* Terminal monitor. */
274a4a44 152 if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
153 vty_log ((zl->record_priority ? zlog_priority[priority] : NULL),
154 zlog_proto_names[zl->protocol], format, args);
718e3744 155}
156
59a06a91 157static char *
158str_append(char *dst, int len, const char *src)
159{
160 while ((len-- > 0) && *src)
161 *dst++ = *src++;
162 return dst;
163}
164
165static char *
166num_append(char *s, int len, u_long x)
167{
168 char buf[30];
7d149b8e 169 char *t;
59a06a91 170
7d149b8e 171 if (!x)
172 return str_append(s,len,"0");
173 *(t = &buf[sizeof(buf)-1]) = '\0';
59a06a91 174 while (x && (t > buf))
175 {
176 *--t = '0'+(x % 10);
177 x /= 10;
178 }
179 return str_append(s,len,t);
180}
181
31364274 182#if defined(SA_SIGINFO) || defined(HAVE_GLIBC_BACKTRACE)
7d149b8e 183static char *
184hex_append(char *s, int len, u_long x)
185{
186 char buf[30];
187 char *t;
188
189 if (!x)
190 return str_append(s,len,"0");
191 *(t = &buf[sizeof(buf)-1]) = '\0';
192 while (x && (t > buf))
193 {
194 u_int cc = (x % 16);
195 *--t = ((cc < 10) ? ('0'+cc) : ('a'+cc-10));
196 x /= 16;
197 }
198 return str_append(s,len,t);
199}
31364274 200#endif
7d149b8e 201
7d149b8e 202/* Needs to be enhanced to support Solaris. */
203static int
204syslog_connect(void)
205{
206#ifdef SUNOS_5
207 return -1;
208#else
209 int fd;
210 char *s;
211 struct sockaddr_un addr;
212
213 if ((fd = socket(AF_UNIX,SOCK_DGRAM,0)) < 0)
214 return -1;
215 addr.sun_family = AF_UNIX;
216#ifdef _PATH_LOG
217#define SYSLOG_SOCKET_PATH _PATH_LOG
218#else
219#define SYSLOG_SOCKET_PATH "/dev/log"
220#endif
221 s = str_append(addr.sun_path,sizeof(addr.sun_path),SYSLOG_SOCKET_PATH);
222#undef SYSLOG_SOCKET_PATH
223 *s = '\0';
224 if (connect(fd,(struct sockaddr *)&addr,sizeof(addr)) < 0)
225 {
226 close(fd);
227 return -1;
228 }
229 return fd;
230#endif
231}
232
233static void
234syslog_sigsafe(int priority, const char *msg, size_t msglen)
235{
1e221354 236 static int syslog_fd = -1;
7d149b8e 237 char buf[sizeof("<1234567890>ripngd[1234567890]: ")+msglen+50];
238 char *s;
239
240 if ((syslog_fd < 0) && ((syslog_fd = syslog_connect()) < 0))
241 return;
242
243#define LOC s,buf+sizeof(buf)-s
244 s = buf;
245 s = str_append(LOC,"<");
246 s = num_append(LOC,priority);
247 s = str_append(LOC,">");
248 /* forget about the timestamp, too difficult in a signal handler */
249 s = str_append(LOC,zlog_default->ident);
250 if (zlog_default->syslog_options & LOG_PID)
251 {
252 s = str_append(LOC,"[");
253 s = num_append(LOC,getpid());
254 s = str_append(LOC,"]");
255 }
256 s = str_append(LOC,": ");
257 s = str_append(LOC,msg);
258 write(syslog_fd,buf,s-buf);
259#undef LOC
260}
261
1e221354 262static int
263open_crashlog(void)
264{
265#define CRASHLOG_PREFIX "/var/tmp/quagga."
266#define CRASHLOG_SUFFIX "crashlog"
267 if (zlog_default && zlog_default->ident)
268 {
269 /* Avoid strlen since it is not async-signal-safe. */
270 const char *p;
271 size_t ilen;
272
273 for (p = zlog_default->ident, ilen = 0; *p; p++)
274 ilen++;
275 {
276 char buf[sizeof(CRASHLOG_PREFIX)+ilen+sizeof(CRASHLOG_SUFFIX)+3];
277 char *s = buf;
278#define LOC s,buf+sizeof(buf)-s
279 s = str_append(LOC, CRASHLOG_PREFIX);
280 s = str_append(LOC, zlog_default->ident);
281 s = str_append(LOC, ".");
282 s = str_append(LOC, CRASHLOG_SUFFIX);
283#undef LOC
284 *s = '\0';
285 return open(buf, O_WRONLY|O_CREAT|O_EXCL, LOGFILE_MASK);
286 }
287 }
288 return open(CRASHLOG_PREFIX CRASHLOG_SUFFIX, O_WRONLY|O_CREAT|O_EXCL,
289 LOGFILE_MASK);
290#undef CRASHLOG_SUFFIX
291#undef CRASHLOG_PREFIX
292}
293
7d149b8e 294/* Note: the goal here is to use only async-signal-safe functions. */
59a06a91 295void
31364274 296zlog_signal(int signo, const char *action
297#ifdef SA_SIGINFO
298 , siginfo_t *siginfo, void *program_counter
299#endif
300 )
59a06a91 301{
302 time_t now;
40abf239 303 char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")+100];
59a06a91 304 char *s = buf;
7d149b8e 305 char *msgstart = buf;
59a06a91 306#define LOC s,buf+sizeof(buf)-s
307
308 time(&now);
309 if (zlog_default)
310 {
311 s = str_append(LOC,zlog_proto_names[zlog_default->protocol]);
312 *s++ = ':';
313 *s++ = ' ';
7d149b8e 314 msgstart = s;
59a06a91 315 }
316 s = str_append(LOC,"Received signal ");
317 s = num_append(LOC,signo);
318 s = str_append(LOC," at ");
319 s = num_append(LOC,now);
31364274 320#ifdef SA_SIGINFO
40abf239 321 s = str_append(LOC," (si_addr 0x");
322 s = hex_append(LOC,(u_long)(siginfo->si_addr));
323 if (program_counter)
324 {
325 s = str_append(LOC,", PC 0x");
326 s = hex_append(LOC,(u_long)program_counter);
327 }
328 s = str_append(LOC,"); ");
31364274 329#else /* SA_SIGINFO */
330 s = str_append(LOC,"; ");
331#endif /* SA_SIGINFO */
59a06a91 332 s = str_append(LOC,action);
7d149b8e 333 if (s < buf+sizeof(buf))
334 *s++ = '\n';
59a06a91 335
274a4a44 336 /* N.B. implicit priority is most severe */
1e221354 337#define PRI LOG_CRIT
274a4a44 338
1e221354 339#define DUMP(FD) write(FD, buf, s-buf);
340 /* If no file logging configured, try to write to fallback log file. */
c4c7d0c4 341 if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0))
342 DUMP(logfile_fd)
59a06a91 343 if (!zlog_default)
c4c7d0c4 344 DUMP(STDERR_FILENO)
59a06a91 345 else
346 {
274a4a44 347 if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
c4c7d0c4 348 DUMP(STDOUT_FILENO)
274a4a44 349 /* Remove trailing '\n' for monitor and syslog */
350 *--s = '\0';
351 if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
352 vty_log_fixed(buf,s-buf);
353 if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
354 syslog_sigsafe(PRI|zlog_default->facility,msgstart,s-msgstart);
59a06a91 355 }
356#undef DUMP
357
31364274 358 zlog_backtrace_sigsafe(PRI,
359#ifdef SA_SIGINFO
360 program_counter
361#else
362 NULL
363#endif
364 );
274a4a44 365#undef PRI
063ee52a 366#undef LOC
367}
368
369/* Log a backtrace using only async-signal-safe functions.
370 Needs to be enhanced to support syslog logging. */
371void
239c26fd 372zlog_backtrace_sigsafe(int priority, void *program_counter)
063ee52a 373{
59a06a91 374#ifdef HAVE_GLIBC_BACKTRACE
239c26fd 375 static const char pclabel[] = "Program counter: ";
063ee52a 376 void *array[20];
377 int size;
378 char buf[100];
379 char *s;
380#define LOC s,buf+sizeof(buf)-s
59a06a91 381
063ee52a 382 if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) ||
383 ((size_t)size > sizeof(array)/sizeof(array[0])))
384 return;
385 s = buf;
386 s = str_append(LOC,"Backtrace for ");
387 s = num_append(LOC,size);
388 s = str_append(LOC," stack frames:\n");
59a06a91 389
1e221354 390#define DUMP(FD) { \
239c26fd 391 if (program_counter) \
392 { \
1e221354 393 write(FD, pclabel, sizeof(pclabel)-1); \
394 backtrace_symbols_fd(&program_counter, 1, FD); \
239c26fd 395 } \
1e221354 396 write(FD, buf, s-buf); \
397 backtrace_symbols_fd(array, size, FD); \
59a06a91 398}
399
c4c7d0c4 400 if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0))
401 DUMP(logfile_fd)
59a06a91 402 if (!zlog_default)
c4c7d0c4 403 DUMP(STDERR_FILENO)
59a06a91 404 else
405 {
274a4a44 406 if (priority <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
c4c7d0c4 407 DUMP(STDOUT_FILENO)
274a4a44 408 /* Remove trailing '\n' for monitor and syslog */
409 *--s = '\0';
410 if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
411 vty_log_fixed(buf,s-buf);
412 if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
413 syslog_sigsafe(priority|zlog_default->facility,buf,s-buf);
414 {
415 int i;
416 /* Just print the function addresses. */
417 for (i = 0; i < size; i++)
418 {
419 s = buf;
420 s = str_append(LOC,"[bt ");
421 s = num_append(LOC,i);
422 s = str_append(LOC,"] 0x");
423 s = hex_append(LOC,(u_long)(array[i]));
424 *s = '\0';
425 if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
426 vty_log_fixed(buf,s-buf);
427 if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
7d149b8e 428 syslog_sigsafe(priority|zlog_default->facility,buf,s-buf);
274a4a44 429 }
430 }
59a06a91 431 }
432#undef DUMP
59a06a91 433#undef LOC
063ee52a 434#endif /* HAVE_GLIBC_BACKTRACE */
435}
436
437void
438zlog_backtrace(int priority)
439{
440#ifndef HAVE_GLIBC_BACKTRACE
441 zlog(NULL, priority, "No backtrace available on this platform.");
442#else
443 void *array[20];
444 int size, i;
445 char **strings;
446
447 if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) ||
448 ((size_t)size > sizeof(array)/sizeof(array[0])))
449 {
450 zlog_err("Cannot get backtrace, returned invalid # of frames %d "
451 "(valid range is between 1 and %u)",
452 size, sizeof(array)/sizeof(array[0]));
453 return;
454 }
455 zlog(NULL, priority, "Backtrace for %d stack frames:", size);
456 if (!(strings = backtrace_symbols(array, size)))
457 {
458 zlog_err("Cannot get backtrace symbols (out of memory?)");
459 for (i = 0; i < size; i++)
460 zlog(NULL, priority, "[bt %d] %p",i,array[i]);
461 }
462 else
463 {
464 for (i = 0; i < size; i++)
465 zlog(NULL, priority, "[bt %d] %s",i,strings[i]);
466 free(strings);
467 }
468#endif /* HAVE_GLIBC_BACKTRACE */
59a06a91 469}
470
718e3744 471void
472zlog (struct zlog *zl, int priority, const char *format, ...)
473{
d246bd96 474 va_list args;
718e3744 475
d246bd96 476 va_start(args, format);
718e3744 477 vzlog (zl, priority, format, args);
d246bd96 478 va_end (args);
718e3744 479}
480
d246bd96 481#define ZLOG_FUNC(FUNCNAME,PRIORITY) \
482void \
483FUNCNAME(const char *format, ...) \
484{ \
485 va_list args; \
486 va_start(args, format); \
487 vzlog (NULL, PRIORITY, format, args); \
488 va_end(args); \
718e3744 489}
490
d246bd96 491ZLOG_FUNC(zlog_err, LOG_ERR)
718e3744 492
d246bd96 493ZLOG_FUNC(zlog_warn, LOG_WARNING)
718e3744 494
d246bd96 495ZLOG_FUNC(zlog_info, LOG_INFO)
718e3744 496
d246bd96 497ZLOG_FUNC(zlog_notice, LOG_NOTICE)
718e3744 498
d246bd96 499ZLOG_FUNC(zlog_debug, LOG_DEBUG)
718e3744 500
d246bd96 501#undef ZLOG_FUNC
718e3744 502
d246bd96 503#define PLOG_FUNC(FUNCNAME,PRIORITY) \
504void \
505FUNCNAME(struct zlog *zl, const char *format, ...) \
506{ \
507 va_list args; \
508 va_start(args, format); \
509 vzlog (zl, PRIORITY, format, args); \
510 va_end(args); \
718e3744 511}
512
d246bd96 513PLOG_FUNC(plog_err, LOG_ERR)
718e3744 514
d246bd96 515PLOG_FUNC(plog_warn, LOG_WARNING)
718e3744 516
d246bd96 517PLOG_FUNC(plog_info, LOG_INFO)
718e3744 518
d246bd96 519PLOG_FUNC(plog_notice, LOG_NOTICE)
718e3744 520
d246bd96 521PLOG_FUNC(plog_debug, LOG_DEBUG)
718e3744 522
d246bd96 523#undef PLOG_FUNC
718e3744 524
cee3df1e 525void
526_zlog_assert_failed (const char *assertion, const char *file,
527 unsigned int line, const char *function)
528{
c4c7d0c4 529 /* Force fallback file logging? */
530 if (zlog_default && !zlog_default->fp &&
531 ((logfile_fd = open_crashlog()) >= 0) &&
532 ((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL))
533 zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR;
1e221354 534 zlog(NULL, LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s",
535 assertion,file,line,(function ? function : "?"));
536 zlog_backtrace(LOG_CRIT);
cee3df1e 537 abort();
538}
539
718e3744 540\f
541/* Open log stream */
542struct zlog *
274a4a44 543openzlog (const char *progname, zlog_proto_t protocol,
718e3744 544 int syslog_flags, int syslog_facility)
545{
546 struct zlog *zl;
274a4a44 547 u_int i;
718e3744 548
274a4a44 549 zl = XCALLOC(MTYPE_ZLOG, sizeof (struct zlog));
718e3744 550
551 zl->ident = progname;
718e3744 552 zl->protocol = protocol;
553 zl->facility = syslog_facility;
7d149b8e 554 zl->syslog_options = syslog_flags;
718e3744 555
274a4a44 556 /* Set default logging levels. */
557 for (i = 0; i < sizeof(zl->maxlvl)/sizeof(zl->maxlvl[0]); i++)
558 zl->maxlvl[i] = ZLOG_DISABLED;
559 zl->maxlvl[ZLOG_DEST_MONITOR] = LOG_DEBUG;
560 zl->default_lvl = LOG_DEBUG;
561
718e3744 562 openlog (progname, syslog_flags, zl->facility);
563
564 return zl;
565}
566
567void
568closezlog (struct zlog *zl)
569{
570 closelog();
571 fclose (zl->fp);
572
573 XFREE (MTYPE_ZLOG, zl);
574}
575
576/* Called from command.c. */
577void
274a4a44 578zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level)
718e3744 579{
580 if (zl == NULL)
581 zl = zlog_default;
582
274a4a44 583 zl->maxlvl[dest] = log_level;
718e3744 584}
585
586int
274a4a44 587zlog_set_file (struct zlog *zl, const char *filename, int log_level)
718e3744 588{
589 FILE *fp;
aa593d5e 590 mode_t oldumask;
718e3744 591
592 /* There is opend file. */
593 zlog_reset_file (zl);
594
595 /* Set default zl. */
596 if (zl == NULL)
597 zl = zlog_default;
598
599 /* Open file. */
aa593d5e 600 oldumask = umask (0777 & ~LOGFILE_MASK);
718e3744 601 fp = fopen (filename, "a");
aa593d5e 602 umask(oldumask);
274a4a44 603 if (fp == NULL)
604 return 0;
718e3744 605
606 /* Set flags. */
607 zl->filename = strdup (filename);
274a4a44 608 zl->maxlvl[ZLOG_DEST_FILE] = log_level;
718e3744 609 zl->fp = fp;
c4c7d0c4 610 logfile_fd = fileno(fp);
718e3744 611
612 return 1;
613}
614
615/* Reset opend file. */
616int
617zlog_reset_file (struct zlog *zl)
618{
619 if (zl == NULL)
620 zl = zlog_default;
621
718e3744 622 if (zl->fp)
623 fclose (zl->fp);
624 zl->fp = NULL;
c4c7d0c4 625 logfile_fd = -1;
274a4a44 626 zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
718e3744 627
628 if (zl->filename)
629 free (zl->filename);
630 zl->filename = NULL;
631
632 return 1;
633}
634
635/* Reopen log file. */
636int
637zlog_rotate (struct zlog *zl)
638{
274a4a44 639 int level;
718e3744 640
641 if (zl == NULL)
642 zl = zlog_default;
643
644 if (zl->fp)
645 fclose (zl->fp);
646 zl->fp = NULL;
c4c7d0c4 647 logfile_fd = -1;
274a4a44 648 level = zl->maxlvl[ZLOG_DEST_FILE];
649 zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
718e3744 650
651 if (zl->filename)
652 {
aa593d5e 653 mode_t oldumask;
274a4a44 654 int save_errno;
aa593d5e 655
656 oldumask = umask (0777 & ~LOGFILE_MASK);
274a4a44 657 zl->fp = fopen (zl->filename, "a");
658 save_errno = errno;
659 umask(oldumask);
660 if (zl->fp == NULL)
aa593d5e 661 {
274a4a44 662 zlog_err("Log rotate failed: cannot open file %s for append: %s",
663 zl->filename, safe_strerror(save_errno));
aa593d5e 664 return -1;
665 }
c4c7d0c4 666 logfile_fd = fileno(zl->fp);
274a4a44 667 zl->maxlvl[ZLOG_DEST_FILE] = level;
718e3744 668 }
669
670 return 1;
671}
672\f
718e3744 673/* Message lookup function. */
8c328f11 674const char *
718e3744 675lookup (struct message *mes, int key)
676{
677 struct message *pnt;
678
679 for (pnt = mes; pnt->key != 0; pnt++)
680 if (pnt->key == key)
681 return pnt->str;
682
683 return "";
684}
685
686/* Very old hacky version of message lookup function. Still partly
b04c699e 687 used in bgpd and ospfd. FIXME Seems that it's not used any more. */
8c328f11 688const char *
718e3744 689mes_lookup (struct message *meslist, int max, int index)
690{
691 if (index < 0 || index >= max)
692 {
693 zlog_err ("message index out of bound: %d", max);
694 return NULL;
695 }
696 return meslist[index].str;
697}
ca359769 698
699/* Wrapper around strerror to handle case where it returns NULL. */
700const char *
701safe_strerror(int errnum)
702{
703 const char *s = strerror(errnum);
704 return (s != NULL) ? s : "Unknown error";
705}