2004-12-07 Andrew J. Schorr <ajschorr@alumni.princeton.edu>

	* bgp_main.c: (main) The 2nd argument to openzlog has been removed.
	* isis_main.c: (main) The 2nd argument to openzlog has been removed.
	* ospf6_main.c: (main) The 2nd argument to openzlog has been removed.
	  Note that stdout logging will no longer be enabled by default when
	  not running as a daemon.
	* ospf_main.c: (main) The 2nd argument to openzlog has been removed.
	* rip_main.c: (main) The 2nd argument to openzlog has been removed.
	* ripng_main.c: (main) The 2nd argument to openzlog has been removed.
	* main.c: (main) The 2nd argument to openzlog has been removed.
	  So stdout logging will no longer be enabled by default.
	* irdp_main.c: (irdp_finish) Reduce severity of shutdown message
	  from LOG_WARNING to LOG_INFO.
	* vtysh.c: Make several functions static instead of global.
	  Added several commands to support destination-specific logging levels.
	  (vtysh_completion) This function is unused, so comment it out.
	* basic.texi: Document new logging features.  Separate basic config
	  commands from basic VTY commands.
	* log.h: Replace struct zlog flags and maskpri fields with maxlvl
	  array to support individual logging levels for each destination.
	  Remove the 2nd argument to openzlog since the default logging config
	  should be standardized inside the library.  Replaced the
	  zlog_set_flag and zlog_reset_flag functions with zlog_set_level.
	  And zlog_set_file now requires an additional log_level argument.
	  Declare zlog_proto_names for use inside command.c in the
	  "show logging" command.  Added defines useful for command
	  construction.
	* log.c: (vzlog) Decide where to send the message based on the
	  individual logging levels configured for each destination.
	  Remove support for ZLOG_STDERR since it was never actually used.
	  Support record-priority for terminal monitors.
	  (zlog_signal,zlog_backtrace_sigsafe) Support destination-specific
	  logging levels.  Remove stderr support (was never used).  Added
	  support for terminal monitor logging.
	  (_zlog_assert_failed) Increase message severity to LOG_EMERG.
	  (openzlog) Remove 2nd argument since default config should be
	  standardized in library.  By default, terminal monitoring
	  is set to debug, and all other logging is disabled.
	  (zlog_set_flag,zlog_reset_flag) Removed.
	  (zlog_set_level) New function to replace zlog_set_flag and
	  zlog_reset_flag.  Supports destination-specific logging levels.
	  (zlog_set_file,zlog_reset_file) Support file-specific logging level.
	  (zlog_rotate) Log an error message if fopen fails, and support
	  new file-specific logging level.
	* command.h: Change DEFUN_CMD_FUNC_DECL and DEFUN_CMD_FUNC_TEXT so that
	  command functions will be static instead of global.  Remove
	  declarations for config_exit and config_help.  Define new macros
	  DEFUNSH_ATTR, DEFUNSH_HIDDEN, and DEFUNSH_DEPRECATED so we can
	  have deprecated commands in vtysh.  Similarly, for completeness,
	  define macros ALIAS_SH, ALIAS_SH_HIDDEN, and ALIAS_SH_DEPRECATED.
	  Also, fix bug in ALIAS_ATTR macro (didn't matter because it
	  was never used).
	* command.c: Make many functions static instead of global.
	  (facility_name,facility_match,level_match) New functions
	  to support enhanced destination-specific logging levels.
	  (config_write_host) Support new destination-specific logging levels.
	  (config_logmsg) Added new "logmsg" command to help test logging
	  system.
	  (show_logging) Added "show logging" command to show the current
	  configuration of the logging system.
	  (config_log_stdout_level) Support explicit stdout logging level.
	  (no_config_log_stdout) Now takes optional LEVEL arg.
	  (config_log_monitor,config_log_monitor_level,no_config_log_monitor)
	  New commands creating new "log monitor" commands to set terminal
	  monitoring log level.
	  (config_log_file_level) Support explicit file logging level.
	  (config_log_syslog_level) Support explicit syslog logging level.
	  (config_log_facility,no_config_log_facility) Implement new
	  "log facility" command.
	  (cmd_init) Add hooks for new commands: "show logging", "logmsg",
	  "log stdout <level>", "log monitor", "log monitor <level>",
	  "no log monitor", "log file <filename> <level>",
	  "no log file <filename> <level>", "log syslog <level>",
	  "log facility", and "no log facility".
	* vty.h: Added a "level" argument to vty_log so it can support
	  "log record-priority".  Declare new function vty_log_fixed for
	  use in signal handlers.
	* vty.c: (vty_log,vty_log_out) Added a "level" argument to support
	  "log record-priority" for vty terminal monitors.
	  (vty_down_level) Use config_exit_cmd.func instead of calling
	  config_exit directly (since command functions will now be static
	  instead of global).
	  (vty_log_fixed) New function to send terminal monitor messages
	  from inside a signal handler.
diff --git a/lib/log.c b/lib/log.c
index d3106cb..b68896c 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -1,4 +1,7 @@
-/* Logging of zebra
+/*
+ * $Id: log.c,v 1.17 2004/12/07 15:39:32 ajs Exp $
+ *
+ * Logging of zebra
  * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro
  *
  * This file is part of GNU Zebra.
@@ -104,12 +107,8 @@
       return;
     }
 
-  /* only log this information if it has not been masked out */
-  if ( priority > zl->maskpri )
-    return ;
-		
   /* Syslog output */
-  if (zl->flags & ZLOG_SYSLOG)
+  if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
     {
       va_list ac;
       va_copy(ac, args);
@@ -118,7 +117,7 @@
     }
 
   /* File output. */
-  if (zl->flags & ZLOG_FILE)
+  if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
     {
       va_list ac;
       time_print (zl->fp);
@@ -133,7 +132,7 @@
     }
 
   /* stdout output. */
-  if (zl->flags & ZLOG_STDOUT)
+  if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
     {
       va_list ac;
       time_print (stdout);
@@ -147,23 +146,10 @@
       fflush (stdout);
     }
 
-  /* stderr output. */
-  if (zl->flags & ZLOG_STDERR)
-    {
-      va_list ac;
-      time_print (stderr);
-      if (zl->record_priority)
-	fprintf (stderr, "%s: ", zlog_priority[priority]);
-      fprintf (stderr, "%s: ", zlog_proto_names[zl->protocol]);
-      va_copy(ac, args);
-      vfprintf (stderr, format, ac);
-      va_end(ac);
-      fprintf (stderr, "\n");
-      fflush (stderr);
-    }
-
   /* Terminal monitor. */
-  vty_log (zlog_proto_names[zl->protocol], format, args);
+  if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
+    vty_log ((zl->record_priority ? zlog_priority[priority] : NULL),
+	     zlog_proto_names[zl->protocol], format, args);
 }
 
 static char *
@@ -297,26 +283,29 @@
   if (s < buf+sizeof(buf))
     *s++ = '\n';
 
+  /* N.B. implicit priority is most severe */
+#define PRI LOG_EMERG
+
 #define DUMP(FP) write(fileno(FP),buf,s-buf);
   if (!zlog_default)
     DUMP(stderr)
   else
     {
-      if ((zlog_default->flags & ZLOG_FILE) && zlog_default->fp)
+      if ((PRI <= zlog_default->maxlvl[ZLOG_DEST_FILE]) && zlog_default->fp)
         DUMP(zlog_default->fp)
-      if (zlog_default->flags & ZLOG_STDOUT)
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
         DUMP(stdout)
-      if (zlog_default->flags & ZLOG_STDERR)
-        DUMP(stderr)
-      if (zlog_default->flags & ZLOG_SYSLOG)
-        {
-	  *--s = '\0';
-	  syslog_sigsafe(LOG_ERR|zlog_default->facility,msgstart,s-msgstart);
-	}
+      /* Remove trailing '\n' for monitor and syslog */
+      *--s = '\0';
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
+        vty_log_fixed(buf,s-buf);
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
+	syslog_sigsafe(PRI|zlog_default->facility,msgstart,s-msgstart);
     }
 #undef DUMP
 
-  zlog_backtrace_sigsafe(LOG_ERR);
+  zlog_backtrace_sigsafe(PRI);
+#undef PRI
 #undef LOC
 }
 
@@ -332,10 +321,6 @@
   char *s;
 #define LOC s,buf+sizeof(buf)-s
 
-  /* only log this information if it has not been masked out */
-  if (zlog_default && (priority > zlog_default->maskpri))
-    return;
-
   if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) ||
       ((size_t)size > sizeof(array)/sizeof(array[0])))
     return;
@@ -353,29 +338,34 @@
     DUMP(stderr)
   else
     {
-      if ((zlog_default->flags & ZLOG_FILE) && zlog_default->fp)
+      if ((priority <= zlog_default->maxlvl[ZLOG_DEST_FILE]) &&
+	  zlog_default->fp)
 	DUMP(zlog_default->fp)
-      if (zlog_default->flags & ZLOG_STDOUT)
+      if (priority <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
 	DUMP(stdout)
-      if (zlog_default->flags & ZLOG_STDERR)
-	DUMP(stderr)
-      if (zlog_default->flags & ZLOG_SYSLOG)
-        {
-	  int i;
-	  *--s = '\0';
-	  syslog_sigsafe(priority|zlog_default->facility,buf,s-buf);
-	  /* Just print the function addresses. */
-	  for (i = 0; i < size; i++)
-	    {
-	      s = buf;
-	      s = str_append(LOC,"[bt ");
-	      s = num_append(LOC,i);
-	      s = str_append(LOC,"] 0x");
-	      s = hex_append(LOC,(u_long)(array[i]));
-	      *s = '\0';
+      /* Remove trailing '\n' for monitor and syslog */
+      *--s = '\0';
+      if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
+	vty_log_fixed(buf,s-buf);
+      if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
+	syslog_sigsafe(priority|zlog_default->facility,buf,s-buf);
+      {
+	int i;
+	/* Just print the function addresses. */
+	for (i = 0; i < size; i++)
+	  {
+	    s = buf;
+	    s = str_append(LOC,"[bt ");
+	    s = num_append(LOC,i);
+	    s = str_append(LOC,"] 0x");
+	    s = hex_append(LOC,(u_long)(array[i]));
+	    *s = '\0';
+	    if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
+	      vty_log_fixed(buf,s-buf);
+	    if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
 	      syslog_sigsafe(priority|zlog_default->facility,buf,s-buf);
-	    }
-        }
+	  }
+      }
     }
 #undef DUMP
 #undef LOC
@@ -476,29 +466,32 @@
 {
   zlog_err("Assertion `%s' failed in file %s, line %u, function %s",
 	   assertion,file,line,(function ? function : "?"));
-  zlog_backtrace(LOG_ERR);
+  zlog_backtrace(LOG_EMERG);
   abort();
 }
 
 
 /* Open log stream */
 struct zlog *
-openzlog (const char *progname, int flags, zlog_proto_t protocol,
+openzlog (const char *progname, zlog_proto_t protocol,
 	  int syslog_flags, int syslog_facility)
 {
   struct zlog *zl;
+  u_int i;
 
-  zl = XMALLOC(MTYPE_ZLOG, sizeof (struct zlog));
-  memset (zl, 0, sizeof (struct zlog));
+  zl = XCALLOC(MTYPE_ZLOG, sizeof (struct zlog));
 
   zl->ident = progname;
-  zl->flags = flags;
   zl->protocol = protocol;
   zl->facility = syslog_facility;
-  zl->maskpri = LOG_DEBUG;
-  zl->record_priority = 0;
   zl->syslog_options = syslog_flags;
 
+  /* Set default logging levels. */
+  for (i = 0; i < sizeof(zl->maxlvl)/sizeof(zl->maxlvl[0]); i++)
+    zl->maxlvl[i] = ZLOG_DISABLED;
+  zl->maxlvl[ZLOG_DEST_MONITOR] = LOG_DEBUG;
+  zl->default_lvl = LOG_DEBUG;
+
   openlog (progname, syslog_flags, zl->facility);
   
   return zl;
@@ -515,25 +508,16 @@
 
 /* Called from command.c. */
 void
-zlog_set_flag (struct zlog *zl, int flags)
+zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level)
 {
   if (zl == NULL)
     zl = zlog_default;
 
-  zl->flags |= flags;
-}
-
-void
-zlog_reset_flag (struct zlog *zl, int flags)
-{
-  if (zl == NULL)
-    zl = zlog_default;
-
-  zl->flags &= ~flags;
+  zl->maxlvl[dest] = log_level;
 }
 
 int
-zlog_set_file (struct zlog *zl, const char *filename)
+zlog_set_file (struct zlog *zl, const char *filename, int log_level)
 {
   FILE *fp;
   mode_t oldumask;
@@ -548,16 +532,13 @@
   /* Open file. */
   oldumask = umask (0777 & ~LOGFILE_MASK);
   fp = fopen (filename, "a");
-  if (fp == NULL)
-    {
-      umask(oldumask);
-      return 0;
-    }
   umask(oldumask);
+  if (fp == NULL)
+    return 0;
 
   /* Set flags. */
   zl->filename = strdup (filename);
-  zl->flags |= ZLOG_FILE;
+  zl->maxlvl[ZLOG_DEST_FILE] = log_level;
   zl->fp = fp;
 
   return 1;
@@ -570,11 +551,10 @@
   if (zl == NULL)
     zl = zlog_default;
 
-  zl->flags &= ~ZLOG_FILE;
-
   if (zl->fp)
     fclose (zl->fp);
   zl->fp = NULL;
+  zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
 
   if (zl->filename)
     free (zl->filename);
@@ -587,7 +567,7 @@
 int
 zlog_rotate (struct zlog *zl)
 {
-  FILE *fp;
+  int level;
 
   if (zl == NULL)
     zl = zlog_default;
@@ -595,20 +575,25 @@
   if (zl->fp)
     fclose (zl->fp);
   zl->fp = NULL;
+  level = zl->maxlvl[ZLOG_DEST_FILE];
+  zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
 
   if (zl->filename)
     {
       mode_t oldumask;
+      int save_errno;
 
       oldumask = umask (0777 & ~LOGFILE_MASK);
-      fp = fopen (zl->filename, "a");
-      if (fp == NULL)
+      zl->fp = fopen (zl->filename, "a");
+      save_errno = errno;
+      umask(oldumask);
+      if (zl->fp == NULL)
         {
-	  umask(oldumask);
+	  zlog_err("Log rotate failed: cannot open file %s for append: %s",
+	  	   zl->filename, safe_strerror(save_errno));
 	  return -1;
         }	
-      umask(oldumask);
-      zl->fp = fp;
+      zl->maxlvl[ZLOG_DEST_FILE] = level;
     }
 
   return 1;