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