]> git.proxmox.com Git - mirror_frr.git/blobdiff - lib/vty.c
Revert "lib: Fix handling of poll"
[mirror_frr.git] / lib / vty.c
index 90e1dadb5b6c8b8f99323349675e17fd60bb4882..ae3e595364edae9b68ecd1f5ce52cfda46ac336e 100644 (file)
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -25,7 +25,7 @@
 #include "linklist.h"
 #include "thread.h"
 #include "buffer.h"
-#include "version.h"
+#include <lib/version.h>
 #include "command.h"
 #include "sockunion.h"
 #include "memory.h"
@@ -35,6 +35,9 @@
 #include "filter.h"
 #include "vty.h"
 #include "privs.h"
+#include "network.h"
+
+#include <arpa/telnet.h>
 
 /* Vty events */
 enum event 
@@ -45,7 +48,8 @@ enum event
   VTY_TIMEOUT_RESET,
 #ifdef VTYSH
   VTYSH_SERV,
-  VTYSH_READ
+  VTYSH_READ,
+  VTYSH_WRITE
 #endif /* VTYSH */
 };
 
@@ -53,7 +57,7 @@ static void vty_event (enum event, int, struct vty *);
 
 /* Extern host structure from command.c */
 extern struct host host;
-\f
+
 /* Vector which store each vty structure. */
 static vector vtyvec;
 
@@ -67,7 +71,7 @@ static char *vty_accesslist_name = NULL;
 static char *vty_ipv6_accesslist_name = NULL;
 
 /* VTY server thread. */
-vector Vvty_serv_thread;
+static vector Vvty_serv_thread;
 
 /* Current directory. */
 char *vty_cwd = NULL;
@@ -78,10 +82,14 @@ static int vty_config;
 /* Login password check. */
 static int no_password_check = 0;
 
+/* Restrict unauthenticated logins? */
+static const u_char restricted_mode_default = 0;
+static u_char restricted_mode = 0;
+
 /* Integrated configuration file path */
 char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
 
-\f
+
 /* VTY standard output function. */
 int
 vty_out (struct vty *vty, const char *format, ...)
@@ -91,15 +99,19 @@ vty_out (struct vty *vty, const char *format, ...)
   int size = 1024;
   char buf[1024];
   char *p = NULL;
-  
-  va_start (args, format);
 
   if (vty_shell (vty))
-    vprintf (format, args);
+    {
+      va_start (args, format);
+      vprintf (format, args);
+      va_end (args);
+    }
   else
     {
       /* Try to write to initial buffer.  */
+      va_start (args, format);
       len = vsnprintf (buf, sizeof buf, format, args);
+      va_end (args);
 
       /* Initial buffer is not enough.  */
       if (len < 0 || len >= size)
@@ -115,7 +127,9 @@ vty_out (struct vty *vty, const char *format, ...)
              if (! p)
                return -1;
 
+             va_start (args, format);
              len = vsnprintf (p, size, format, args);
+             va_end (args);
 
              if (len > -1 && len < size)
                break;
@@ -127,59 +141,78 @@ vty_out (struct vty *vty, const char *format, ...)
        p = buf;
 
       /* Pointer p must point out buffer. */
-      if (vty_shell_serv (vty))
-       write (vty->fd, (u_char *) p, len);
-      else
-       buffer_write (vty->obuf, (u_char *) p, len);
+      buffer_put (vty->obuf, (u_char *) p, len);
 
       /* If p is not different with buf, it is allocated buffer.  */
       if (p != buf)
        XFREE (MTYPE_VTY_OUT_BUF, p);
     }
 
-  va_end (args);
-
   return len;
 }
 
-int
-vty_log_out (struct vty *vty, const char *proto_str, const char *format,
-            va_list va)
+static int
+vty_log_out (struct vty *vty, const char *level, const char *proto_str,
+            const char *format, struct timestamp_control *ctl, va_list va)
 {
+  int ret;
   int len;
   char buf[1024];
 
-  snprintf (buf, sizeof buf, "%s: ", proto_str);
-  write (vty->fd, buf, strlen (proto_str) + 2);
+  if (!ctl->already_rendered)
+    {
+      ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
+      ctl->already_rendered = 1;
+    }
+  if (ctl->len+1 >= sizeof(buf))
+    return -1;
+  memcpy(buf, ctl->buf, len = ctl->len);
+  buf[len++] = ' ';
+  buf[len] = '\0';
 
-  len = vsnprintf (buf, sizeof buf, format, va);
-  if (len < 0)
+  if (level)
+    ret = snprintf(buf+len, sizeof(buf)-len, "%s: %s: ", level, proto_str);
+  else
+    ret = snprintf(buf+len, sizeof(buf)-len, "%s: ", proto_str);
+  if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf)))
     return -1;
-  write (vty->fd, (u_char *)buf, len);
 
-  snprintf (buf, sizeof buf, "\r\n");
-  write (vty->fd, buf, 2);
+  if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) ||
+      ((size_t)((len += ret)+2) > sizeof(buf)))
+    return -1;
 
-  return len;
+  buf[len++] = '\r';
+  buf[len++] = '\n';
+
+  if (write(vty->fd, buf, len) < 0)
+    {
+      if (ERRNO_IO_RETRY(errno))
+       /* Kernel buffer is full, probably too much debugging output, so just
+          drop the data and ignore. */
+       return -1;
+      /* Fatal I/O error. */
+      vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
+      zlog_warn("%s: write failed to vty client fd %d, closing: %s",
+               __func__, vty->fd, safe_strerror(errno));
+      buffer_reset(vty->obuf);
+      /* cannot call vty_close, because a parent routine may still try
+         to access the vty struct */
+      vty->status = VTY_CLOSE;
+      shutdown(vty->fd, SHUT_RDWR);
+      return -1;
+    }
+  return 0;
 }
 
 /* Output current time to the vty. */
 void
 vty_time_print (struct vty *vty, int cr)
 {
-  time_t clock;
-  struct tm *tm;
-#define TIME_BUF 25
-  char buf [TIME_BUF];
-  int ret;
+  char buf [25];
   
-  time (&clock);
-  tm = localtime (&clock);
-
-  ret = strftime (buf, TIME_BUF, "%Y/%m/%d %H:%M:%S", tm);
-  if (ret == 0)
+  if (quagga_timestamp(0, buf, sizeof(buf)) == 0)
     {
-      zlog (NULL, LOG_INFO, "strftime error");
+      zlog (NULL, LOG_INFO, "quagga_timestamp error");
       return;
     }
   if (cr)
@@ -194,8 +227,30 @@ vty_time_print (struct vty *vty, int cr)
 void
 vty_hello (struct vty *vty)
 {
-  if (host.motd)
-    vty_out (vty, host.motd);
+  if (host.motdfile)
+    {
+      FILE *f;
+      char buf[4096];
+
+      f = fopen (host.motdfile, "r");
+      if (f)
+       {
+         while (fgets (buf, sizeof (buf), f))
+           {
+             char *s;
+             /* work backwards to ignore trailling isspace() */
+             for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
+                  s--);
+             *s = '\0';
+             vty_out (vty, "%s%s", buf, VTY_NEWLINE);
+           }
+         fclose (f);
+       }
+      else
+       vty_out (vty, "MOTD file not found%s", VTY_NEWLINE);
+    }
+  else if (host.motd)
+    vty_out (vty, "%s", host.motd);
 }
 
 /* Put out prompt and wait input from user. */
@@ -218,10 +273,10 @@ vty_prompt (struct vty *vty)
 }
 
 /* Send WILL TELOPT_ECHO to remote server. */
-void
+static void
 vty_will_echo (struct vty *vty)
 {
-  char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
+  unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
   vty_out (vty, "%s", cmd);
 }
 
@@ -229,7 +284,7 @@ vty_will_echo (struct vty *vty)
 static void
 vty_will_suppress_go_ahead (struct vty *vty)
 {
-  char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
+  unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
   vty_out (vty, "%s", cmd);
 }
 
@@ -237,7 +292,7 @@ vty_will_suppress_go_ahead (struct vty *vty)
 static void
 vty_dont_linemode (struct vty *vty)
 {
-  char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
+  unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
   vty_out (vty, "%s", cmd);
 }
 
@@ -245,7 +300,7 @@ vty_dont_linemode (struct vty *vty)
 static void
 vty_do_window_size (struct vty *vty)
 {
-  char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
+  unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
   vty_out (vty, "%s", cmd);
 }
 
@@ -254,7 +309,7 @@ vty_do_window_size (struct vty *vty)
 static void
 vty_dont_lflow_ahead (struct vty *vty)
 {
-  char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
+  unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
   vty_out (vty, "%s", cmd);
 }
 #endif /* 0 */
@@ -265,10 +320,10 @@ vty_new ()
 {
   struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty));
 
-  new->obuf = (struct buffer *) buffer_new (100);
+  new->obuf = buffer_new(0);   /* Use default buffer size. */
   new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
+  new->error_buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
   new->max = VTY_BUFSIZ;
-  new->sb_buffer = NULL;
 
   return new;
 }
@@ -333,18 +388,19 @@ vty_auth (struct vty *vty, char *buf)
              /* AUTH_ENABLE_NODE */
              vty->fail = 0;
              vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE);
-             vty->node = VIEW_NODE;
+             vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
            }
        }
     }
 }
 
 /* Command execution over the vty interface. */
-int
+static int
 vty_command (struct vty *vty, char *buf)
 {
   int ret;
   vector vline;
+  const char *protocolname;
 
   /* Split readline string up into the vector */
   vline = cmd_make_strvec (buf);
@@ -352,7 +408,32 @@ vty_command (struct vty *vty, char *buf)
   if (vline == NULL)
     return CMD_SUCCESS;
 
-  ret = cmd_execute_command (vline, vty, NULL);
+#ifdef CONSUMED_TIME_CHECK
+  {
+    RUSAGE_T before;
+    RUSAGE_T after;
+    unsigned long realtime, cputime;
+
+    GETRUSAGE(&before);
+#endif /* CONSUMED_TIME_CHECK */
+
+  ret = cmd_execute_command (vline, vty, NULL, 0);
+
+  /* Get the name of the protocol if any */
+  if (zlog_default)
+      protocolname = zlog_proto_names[zlog_default->protocol];
+  else
+      protocolname = zlog_proto_names[ZLOG_NONE];
+                                                                           
+#ifdef CONSUMED_TIME_CHECK
+    GETRUSAGE(&after);
+    if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
+       CONSUMED_TIME_CHECK)
+      /* Warn about CPU hog that must be fixed. */
+      zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s",
+               realtime/1000, cputime/1000, buf);
+  }
+#endif /* CONSUMED_TIME_CHECK */
 
   if (ret != CMD_SUCCESS)
     switch (ret)
@@ -365,7 +446,7 @@ vty_command (struct vty *vty, char *buf)
        vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
        break;
       case CMD_ERR_NO_MATCH:
-       vty_out (vty, "%% Unknown command.%s", VTY_NEWLINE);
+       vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE);
        break;
       case CMD_ERR_INCOMPLETE:
        vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
@@ -375,19 +456,19 @@ vty_command (struct vty *vty, char *buf)
 
   return ret;
 }
-\f
-char telnet_backward_char = 0x08;
-char telnet_space_char = ' ';
+
+static const char telnet_backward_char = 0x08;
+static const char telnet_space_char = ' ';
 
 /* Basic function to write buffer to vty. */
 static void
-vty_write (struct vty *vty, char *buf, size_t nbytes)
+vty_write (struct vty *vty, const char *buf, size_t nbytes)
 {
   if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
     return;
 
   /* Should we do buffering here ?  And make vty_flush (vty) ? */
-  buffer_write (vty->obuf, (u_char *)buf, nbytes);
+  buffer_put (vty->obuf, buf, nbytes);
 }
 
 /* Ensure length of input buffer.  Is buffer is short, double it. */
@@ -398,6 +479,7 @@ vty_ensure (struct vty *vty, int length)
     {
       vty->max *= 2;
       vty->buf = XREALLOC (MTYPE_VTY, vty->buf, vty->max);
+      vty->error_buf = XREALLOC (MTYPE_VTY, vty->error_buf, vty->max);
     }
 }
 
@@ -508,7 +590,7 @@ vty_history_print (struct vty *vty)
 }
 
 /* Show next command line history. */
-void
+static void
 vty_next_line (struct vty *vty)
 {
   int try_index;
@@ -533,7 +615,7 @@ vty_next_line (struct vty *vty)
 }
 
 /* Show previous command line history. */
-void
+static void
 vty_previous_line (struct vty *vty)
 {
   int try_index;
@@ -596,13 +678,13 @@ static void
 vty_down_level (struct vty *vty)
 {
   vty_out (vty, "%s", VTY_NEWLINE);
-  config_exit (NULL, vty, 0, NULL);
+  (*config_exit_cmd.func)(NULL, vty, 0, NULL);
   vty_prompt (vty);
   vty->cp = 0;
 }
 
 /* When '^Z' is received from vty, move down to the enable mode. */
-void
+static void
 vty_end_config (struct vty *vty)
 {
   vty_out (vty, "%s", VTY_NEWLINE);
@@ -611,6 +693,7 @@ vty_end_config (struct vty *vty)
     {
     case VIEW_NODE:
     case ENABLE_NODE:
+    case RESTRICTED_NODE:
       /* Nothing to do. */
       break;
     case CONFIG_NODE:
@@ -623,9 +706,11 @@ vty_end_config (struct vty *vty)
     case BGP_IPV4_NODE:
     case BGP_IPV4M_NODE:
     case BGP_IPV6_NODE:
+    case BGP_IPV6M_NODE:
     case RMAP_NODE:
     case OSPF_NODE:
     case OSPF6_NODE:
+    case ISIS_NODE:
     case KEYCHAIN_NODE:
     case KEYCHAIN_KEY_NODE:
     case MASC_NODE:
@@ -649,9 +734,6 @@ vty_delete_char (struct vty *vty)
   int i;
   int size;
 
-  if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
-    return;
-
   if (vty->length == 0)
     {
       vty_down_level (vty);
@@ -666,6 +748,9 @@ vty_delete_char (struct vty *vty)
   vty->length--;
   memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
   vty->buf[vty->length] = '\0';
+  
+  if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+    return;
 
   vty_write (vty, &vty->buf[vty->cp], size - 1);
   vty_write (vty, &telnet_space_char, 1);
@@ -845,24 +930,25 @@ vty_complete_command (struct vty *vty)
     vector_only_index_free (matched);
 }
 
-void
+static void
 vty_describe_fold (struct vty *vty, int cmd_width,
-                 int desc_width, struct desc *desc)
+                  unsigned int desc_width, struct cmd_token *token)
 {
-  char *buf, *cmd, *p;
+  char *buf;
+  const char *cmd, *p;
   int pos;
 
-  cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
+  cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd;
 
   if (desc_width <= 0)
     {
-      vty_out (vty, "  %-*s  %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE);
+      vty_out (vty, "  %-*s  %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE);
       return;
     }
 
-  buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
+  buf = XCALLOC (MTYPE_TMP, strlen (token->desc) + 1);
 
-  for (p = desc->str; strlen (p) > desc_width; p += pos + 1)
+  for (p = token->desc; strlen (p) > desc_width; p += pos + 1)
     {
       for (pos = desc_width; pos > 0; pos--)
       if (*(p + pos) == ' ')
@@ -890,8 +976,8 @@ vty_describe_command (struct vty *vty)
   int ret;
   vector vline;
   vector describe;
-  int i, width, desc_width;
-  struct desc *desc, *desc_cr = NULL;
+  unsigned int i, width, desc_width;
+  struct cmd_token *token, *token_cr = NULL;
 
   vline = cmd_make_strvec (vty->buf);
 
@@ -913,33 +999,27 @@ vty_describe_command (struct vty *vty)
   switch (ret)
     {
     case CMD_ERR_AMBIGUOUS:
-      cmd_free_strvec (vline);
       vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
-      vty_prompt (vty);
-      vty_redraw_line (vty);
-      return;
+      goto out;
       break;
     case CMD_ERR_NO_MATCH:
-      cmd_free_strvec (vline);
       vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);
-      vty_prompt (vty);
-      vty_redraw_line (vty);
-      return;
+      goto out;
       break;
     }  
 
   /* Get width of command string. */
   width = 0;
-  for (i = 0; i < vector_max (describe); i++)
-    if ((desc = vector_slot (describe, i)) != NULL)
+  for (i = 0; i < vector_active (describe); i++)
+    if ((token = vector_slot (describe, i)) != NULL)
       {
-       int len;
+       unsigned int len;
 
-       if (desc->cmd[0] == '\0')
+       if (token->cmd[0] == '\0')
          continue;
 
-       len = strlen (desc->cmd);
-       if (desc->cmd[0] == '.')
+       len = strlen (token->cmd);
+       if (token->cmd[0] == '.')
          len--;
 
        if (width < len)
@@ -950,28 +1030,28 @@ vty_describe_command (struct vty *vty)
   desc_width = vty->width - (width + 6);
 
   /* Print out description. */
-  for (i = 0; i < vector_max (describe); i++)
-    if ((desc = vector_slot (describe, i)) != NULL)
+  for (i = 0; i < vector_active (describe); i++)
+    if ((token = vector_slot (describe, i)) != NULL)
       {
-       if (desc->cmd[0] == '\0')
+       if (token->cmd[0] == '\0')
          continue;
        
-       if (strcmp (desc->cmd, "<cr>") == 0)
+       if (strcmp (token->cmd, command_cr) == 0)
          {
-           desc_cr = desc;
+           token_cr = token;
            continue;
          }
 
-       if (!desc->str)
+       if (!token->desc)
          vty_out (vty, "  %-s%s",
-                  desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+                  token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
                   VTY_NEWLINE);
-       else if (desc_width >= strlen (desc->str))
+       else if (desc_width >= strlen (token->desc))
          vty_out (vty, "  %-*s  %s%s", width,
-                  desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
-                  desc->str, VTY_NEWLINE);
+                  token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
+                  token->desc, VTY_NEWLINE);
        else
-         vty_describe_fold (vty, width, desc_width, desc);
+         vty_describe_fold (vty, width, desc_width, token);
 
 #if 0
        vty_out (vty, "  %-*s %s%s", width
@@ -980,28 +1060,30 @@ vty_describe_command (struct vty *vty)
 #endif /* 0 */
       }
 
-  if ((desc = desc_cr))
+  if ((token = token_cr))
     {
-      if (!desc->str)
+      if (!token->desc)
        vty_out (vty, "  %-s%s",
-                desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+                token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
                 VTY_NEWLINE);
-      else if (desc_width >= strlen (desc->str))
+      else if (desc_width >= strlen (token->desc))
        vty_out (vty, "  %-*s  %s%s", width,
-                desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
-                desc->str, VTY_NEWLINE);
+                token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
+                token->desc, VTY_NEWLINE);
       else
-       vty_describe_fold (vty, width, desc_width, desc);
+       vty_describe_fold (vty, width, desc_width, token);
     }
 
+out:
   cmd_free_strvec (vline);
-  vector_free (describe);
+  if (describe)
+    vector_free (describe);
 
   vty_prompt (vty);
   vty_redraw_line (vty);
 }
 
-void
+static void
 vty_clear_buf (struct vty *vty)
 {
   memset (vty->buf, 0, vty->max);
@@ -1019,6 +1101,7 @@ vty_stop_input (struct vty *vty)
     {
     case VIEW_NODE:
     case ENABLE_NODE:
+    case RESTRICTED_NODE:
       /* Nothing to do. */
       break;
     case CONFIG_NODE:
@@ -1030,6 +1113,7 @@ vty_stop_input (struct vty *vty)
     case RMAP_NODE:
     case OSPF_NODE:
     case OSPF6_NODE:
+    case ISIS_NODE:
     case KEYCHAIN_NODE:
     case KEYCHAIN_KEY_NODE:
     case MASC_NODE:
@@ -1134,33 +1218,41 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
   switch (buf[0])
     {
     case SB:
-      buffer_reset(vty->sb_buffer);
+      vty->sb_len = 0;
       vty->iac_sb_in_progress = 1;
       return 0;
       break;
     case SE: 
       {
-       char *buffer = (char *)vty->sb_buffer->head->data;
-       int length = vty->sb_buffer->length;
-
-       if (buffer == NULL)
-         return 0;
-
        if (!vty->iac_sb_in_progress)
          return 0;
 
-       if (buffer[0] == '\0')
+       if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
          {
            vty->iac_sb_in_progress = 0;
            return 0;
          }
-       switch (buffer[0])
+       switch (vty->sb_buf[0])
          {
          case TELOPT_NAWS:
-           if (length < 5)
-             break;
-           vty->width = buffer[2];
-           vty->height = vty->lines >= 0 ? vty->lines : buffer[4];
+           if (vty->sb_len != TELNET_NAWS_SB_LEN)
+             zlog_warn("RFC 1073 violation detected: telnet NAWS option "
+                       "should send %d characters, but we received %lu",
+                       TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
+           else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
+             zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, "
+                      "too small to handle the telnet NAWS option",
+                      (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
+           else
+             {
+               vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
+               vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
+#ifdef TELNET_OPTION_DEBUG
+               vty_out(vty, "TELNET NAWS window size negotiation completed: "
+                             "width %d, height %d%s",
+                       vty->width, vty->height, VTY_NEWLINE);
+#endif
+             }
            break;
          }
        vty->iac_sb_in_progress = 0;
@@ -1198,9 +1290,7 @@ vty_execute (struct vty *vty)
   vty->cp = vty->length = 0;
   vty_clear_buf (vty);
 
-  if (vty->status != VTY_CLOSE 
-      && vty->status != VTY_START
-      && vty->status != VTY_CONTINUE)
+  if (vty->status != VTY_CLOSE )
     vty_prompt (vty);
 
   return ret;
@@ -1251,7 +1341,6 @@ static int
 vty_read (struct thread *thread)
 {
   int i;
-  int ret;
   int nbytes;
   unsigned char buf[VTY_READ_BUFSIZ];
 
@@ -1260,9 +1349,22 @@ vty_read (struct thread *thread)
   vty->t_read = NULL;
 
   /* Read raw data from socket */
-  nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ);
-  if (nbytes <= 0)
-    vty->status = VTY_CLOSE;
+  if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
+    {
+      if (nbytes < 0)
+       {
+         if (ERRNO_IO_RETRY(errno))
+           {
+             vty_event (VTY_READ, vty_sock, vty);
+             return 0;
+           }
+         vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
+         zlog_warn("%s: read error on vty client fd %d, closing: %s",
+                   __func__, vty->fd, safe_strerror(errno));
+       }
+      buffer_reset(vty->obuf);
+      vty->status = VTY_CLOSE;
+    }
 
   for (i = 0; i < nbytes; i++) 
     {
@@ -1281,18 +1383,22 @@ vty_read (struct thread *thread)
       
       if (vty->iac_sb_in_progress && !vty->iac)
        {
-           buffer_putc(vty->sb_buffer, buf[i]);
+           if (vty->sb_len < sizeof(vty->sb_buf))
+             vty->sb_buf[vty->sb_len] = buf[i];
+           vty->sb_len++;
            continue;
        }
 
       if (vty->iac)
        {
          /* In case of telnet command */
+         int ret = 0;
          ret = vty_telnet_option (vty, buf + i, nbytes - i);
          vty->iac = 0;
          i += ret;
          continue;
        }
+               
 
       if (vty->status == VTY_MORE)
        {
@@ -1301,8 +1407,6 @@ vty_read (struct thread *thread)
            case CONTROL('C'):
            case 'q':
            case 'Q':
-             if (vty->output_func)
-               (*vty->output_func) (vty, 1);
              vty_buffer_reset (vty);
              break;
 #if 0 /* More line does not work for "show ip bgp".  */
@@ -1312,8 +1416,6 @@ vty_read (struct thread *thread)
              break;
 #endif
            default:
-             if (vty->output_func)
-               (*vty->output_func) (vty, 0);
              break;
            }
          continue;
@@ -1449,121 +1551,82 @@ static int
 vty_flush (struct thread *thread)
 {
   int erase;
-  int dont_more;
+  buffer_status_t flushrc;
   int vty_sock = THREAD_FD (thread);
   struct vty *vty = THREAD_ARG (thread);
+
   vty->t_write = NULL;
 
   /* Tempolary disable read thread. */
-  if (vty->lines == 0)
-    if (vty->t_read)
-      {
-       thread_cancel (vty->t_read);
-       vty->t_read = NULL;
-      }
-
-  /* Function execution continue. */
-  if (vty->status == VTY_START || vty->status == VTY_CONTINUE)
+  if ((vty->lines == 0) && vty->t_read)
     {
-      if (vty->status == VTY_CONTINUE)
-       erase = 1;
-      else
-       erase = 0;
-
-      if (vty->output_func == NULL)
-       dont_more = 1;
-      else
-       dont_more = 0;
-
-      if (vty->lines == 0)
-       {
-         erase = 0;
-         dont_more = 1;
-       }
-
-      buffer_flush_vty_all (vty->obuf, vty->fd, erase, dont_more);
+      thread_cancel (vty->t_read);
+      vty->t_read = NULL;
+    }
 
+  /* Function execution continue. */
+  erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
+
+  /* N.B. if width is 0, that means we don't know the window size. */
+  if ((vty->lines == 0) || (vty->width == 0))
+    flushrc = buffer_flush_available(vty->obuf, vty->fd);
+  else if (vty->status == VTY_MORELINE)
+    flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width,
+                                 1, erase, 0);
+  else
+    flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width,
+                                 vty->lines >= 0 ? vty->lines :
+                                                   vty->height,
+                                 erase, 0);
+  switch (flushrc)
+    {
+    case BUFFER_ERROR:
+      vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
+      zlog_warn("buffer_flush failed on vty client fd %d, closing",
+               vty->fd);
+      buffer_reset(vty->obuf);
+      vty_close(vty);
+      return 0;
+    case BUFFER_EMPTY:
       if (vty->status == VTY_CLOSE)
-       {
-         vty_close (vty);
-         return 0;
-       }
-
-      if (vty->output_func == NULL)
-       {
-         vty->status = VTY_NORMAL;
-         vty_prompt (vty);
-         vty_event (VTY_WRITE, vty_sock, vty);
-       }
+       vty_close (vty);
       else
-       vty->status = VTY_MORE;
-
-      if (vty->lines == 0)
        {
-         if (vty->output_func == NULL)
+         vty->status = VTY_NORMAL;
+         if (vty->lines == 0)
            vty_event (VTY_READ, vty_sock, vty);
-         else
-           {
-             if (vty->output_func)
-               (*vty->output_func) (vty, 0);
-             vty_event (VTY_WRITE, vty_sock, vty);
-           }
        }
-    }
-  else
-    {
-      if (vty->status == VTY_MORE || vty->status == VTY_MORELINE)
-       erase = 1;
-      else
-       erase = 0;
-
+      break;
+    case BUFFER_PENDING:
+      /* There is more data waiting to be written. */
+      vty->status = VTY_MORE;
       if (vty->lines == 0)
-       buffer_flush_window (vty->obuf, vty->fd, vty->width, 25, 0, 1);
-      else if (vty->status == VTY_MORELINE)
-       buffer_flush_window (vty->obuf, vty->fd, vty->width, 1, erase, 0);
-      else
-       buffer_flush_window (vty->obuf, vty->fd, vty->width,
-                            vty->lines >= 0 ? vty->lines : vty->height,
-                            erase, 0);
-  
-      if (buffer_empty (vty->obuf))
-       {
-         if (vty->status == VTY_CLOSE)
-           vty_close (vty);
-         else
-           {
-             vty->status = VTY_NORMAL;
-         
-             if (vty->lines == 0)
-               vty_event (VTY_READ, vty_sock, vty);
-           }
-       }
-      else
-       {
-         vty->status = VTY_MORE;
-
-         if (vty->lines == 0)
-           vty_event (VTY_WRITE, vty_sock, vty);
-       }
+       vty_event (VTY_WRITE, vty_sock, vty);
+      break;
     }
 
   return 0;
 }
 
 /* Create new vty structure. */
-struct vty *
+static struct vty *
 vty_create (int vty_sock, union sockunion *su)
 {
+  char buf[SU_ADDRSTRLEN];
   struct vty *vty;
 
+  sockunion2str(su, buf, SU_ADDRSTRLEN);
+
   /* Allocate new vty structure and set up default values. */
   vty = vty_new ();
   vty->fd = vty_sock;
   vty->type = VTY_TERM;
-  vty->address = sockunion_su2str (su);
+  strcpy (vty->address, buf);
   if (no_password_check)
     {
-      if (host.advanced)
+      if (restricted_mode)
+        vty->node = RESTRICTED_NODE;
+      else if (host.advanced)
        vty->node = ENABLE_NODE;
       else
        vty->node = VIEW_NODE;
@@ -1586,7 +1649,7 @@ vty_create (int vty_sock, union sockunion *su)
     vty->lines = -1;
   vty->iac = 0;
   vty->iac_sb_in_progress = 0;
-  vty->sb_buffer = buffer_new (1024);
+  vty->sb_len = 0;
 
   if (! no_password_check)
     {
@@ -1627,13 +1690,13 @@ static int
 vty_accept (struct thread *thread)
 {
   int vty_sock;
-  struct vty *vty;
   union sockunion su;
   int ret;
   unsigned int on;
   int accept_sock;
   struct prefix *p = NULL;
   struct access_list *acl = NULL;
+  char buf[SU_ADDRSTRLEN];
 
   accept_sock = THREAD_FD (thread);
 
@@ -1646,9 +1709,10 @@ vty_accept (struct thread *thread)
   vty_sock = sockunion_accept (accept_sock, &su);
   if (vty_sock < 0)
     {
-      zlog_warn ("can't accept vty socket : %s", strerror (errno));
+      zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
       return -1;
     }
+  set_nonblocking(vty_sock);
 
   p = sockunion2hostprefix (&su);
 
@@ -1658,10 +1722,8 @@ vty_accept (struct thread *thread)
       if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
          (access_list_apply (acl, p) == FILTER_DENY))
        {
-         char *buf;
          zlog (NULL, LOG_INFO, "Vty connection refused from %s",
-               (buf = sockunion_su2str (&su)));
-         free (buf);
+               sockunion2str (&su, buf, SU_ADDRSTRLEN));
          close (vty_sock);
          
          /* continue accepting connections */
@@ -1680,10 +1742,8 @@ vty_accept (struct thread *thread)
       if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
          (access_list_apply (acl, p) == FILTER_DENY))
        {
-         char *buf;
          zlog (NULL, LOG_INFO, "Vty connection refused from %s",
-               (buf = sockunion_su2str (&su)));
-         free (buf);
+               sockunion2str (&su, buf, SU_ADDRSTRLEN));
          close (vty_sock);
          
          /* continue accepting connections */
@@ -1703,15 +1763,18 @@ vty_accept (struct thread *thread)
                    (char *) &on, sizeof (on));
   if (ret < 0)
     zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", 
-         strerror (errno));
+         safe_strerror (errno));
+
+  zlog (NULL, LOG_INFO, "Vty connection from %s",
+       sockunion2str (&su, buf, SU_ADDRSTRLEN));
 
-  vty = vty_create (vty_sock, &su);
+  vty_create (vty_sock, &su);
 
   return 0;
 }
 
 #if defined(HAVE_IPV6) && !defined(NRL)
-void
+static void
 vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
 {
   int ret;
@@ -1751,6 +1814,7 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
       if (sock < 0)
        continue;
 
+      sockopt_v6only (ainfo->ai_family, sock);
       sockopt_reuseaddr (sock);
       sockopt_reuseport (sock);
 
@@ -1774,10 +1838,10 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
 
   freeaddrinfo (ainfo_save);
 }
-#endif /* HAVE_IPV6 && ! NRL */
+#else /* HAVE_IPV6 && ! NRL */
 
 /* Make vty server socket. */
-void
+static void
 vty_serv_sock_family (const char* addr, unsigned short port, int family)
 {
   int ret;
@@ -1792,9 +1856,11 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
     {
       case AF_INET:
         naddr=&su.sin.sin_addr;
+        break;
 #ifdef HAVE_IPV6
       case AF_INET6:
         naddr=&su.sin6.sin6_addr;
+        break;
 #endif 
     }
 
@@ -1806,7 +1872,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
        naddr=NULL;
        break;
       case 0:
-        zlog_err("error translating address %s: %s",addr,strerror(errno));
+        zlog_err("error translating address %s: %s",addr,safe_strerror(errno));
        naddr=NULL;
     }
 
@@ -1840,14 +1906,15 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
   /* Add vty server event. */
   vty_event (VTY_SERV, accept_sock, NULL);
 }
+#endif /* HAVE_IPV6 && ! NRL */
 
 #ifdef VTYSH
 /* For sockaddr_un. */
 #include <sys/un.h>
 
 /* VTY shell UNIX domain socket. */
-void
-vty_serv_un (char *path)
+static void
+vty_serv_un (const char *path)
 {
   int ret;
   int sock, len;
@@ -1865,7 +1932,7 @@ vty_serv_un (char *path)
   sock = socket (AF_UNIX, SOCK_STREAM, 0);
   if (sock < 0)
     {
-      perror ("sock");
+      zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno));
       return;
     }
 
@@ -1873,16 +1940,16 @@ vty_serv_un (char *path)
   memset (&serv, 0, sizeof (struct sockaddr_un));
   serv.sun_family = AF_UNIX;
   strncpy (serv.sun_path, path, strlen (path));
-#ifdef HAVE_SUN_LEN
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
   len = serv.sun_len = SUN_LEN(&serv);
 #else
   len = sizeof (serv.sun_family) + strlen (serv.sun_path);
-#endif /* HAVE_SUN_LEN */
+#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
 
   ret = bind (sock, (struct sockaddr *) &serv, len);
   if (ret < 0)
     {
-      perror ("bind");
+      zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno));
       close (sock);    /* Avoid sd leak. */
       return;
     }
@@ -1890,7 +1957,7 @@ vty_serv_un (char *path)
   ret = listen (sock, 5);
   if (ret < 0)
     {
-      perror ("listen");
+      zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno));
       close (sock);    /* Avoid sd leak. */
       return;
     }
@@ -1905,7 +1972,7 @@ vty_serv_un (char *path)
       if ( chown (path, -1, ids.gid_vty) )
         {
           zlog_err ("vty_serv_un: could chown socket, %s",
-                     strerror (errno) );
+                     safe_strerror (errno) );
         }
     }
 
@@ -1930,14 +1997,23 @@ vtysh_accept (struct thread *thread)
   memset (&client, 0, sizeof (struct sockaddr_un));
   client_len = sizeof (struct sockaddr_un);
 
-  sock = accept (accept_sock, (struct sockaddr *) &client, &client_len);
+  sock = accept (accept_sock, (struct sockaddr *) &client,
+                (socklen_t *) &client_len);
 
   if (sock < 0)
     {
-      zlog_warn ("can't accept vty socket : %s", strerror (errno));
+      zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
       return -1;
     }
 
+  if (set_nonblocking(sock) < 0)
+    {
+      zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking,"
+                 " %s, closing", sock, safe_strerror (errno));
+      close (sock);
+      return -1;
+    }
+  
 #ifdef VTYSH_DEBUG
   printf ("VTY shell accept\n");
 #endif /* VTYSH_DEBUG */
@@ -1952,6 +2028,27 @@ vtysh_accept (struct thread *thread)
   return 0;
 }
 
+static int
+vtysh_flush(struct vty *vty)
+{
+  switch (buffer_flush_available(vty->obuf, vty->fd))
+    {
+    case BUFFER_PENDING:
+      vty_event(VTYSH_WRITE, vty->fd, vty);
+      break;
+    case BUFFER_ERROR:
+      vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
+      zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd);
+      buffer_reset(vty->obuf);
+      vty_close(vty);
+      return -1;
+      break;
+    case BUFFER_EMPTY:
+      break;
+    }
+  return 0;
+}
+
 static int
 vtysh_read (struct thread *thread)
 {
@@ -1960,15 +2057,27 @@ vtysh_read (struct thread *thread)
   int nbytes;
   struct vty *vty;
   unsigned char buf[VTY_READ_BUFSIZ];
+  unsigned char *p;
   u_char header[4] = {0, 0, 0, 0};
 
   sock = THREAD_FD (thread);
   vty = THREAD_ARG (thread);
   vty->t_read = NULL;
 
-  nbytes = read (sock, buf, VTY_READ_BUFSIZ);
-  if (nbytes <= 0)
+  if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)
     {
+      if (nbytes < 0)
+       {
+         if (ERRNO_IO_RETRY(errno))
+           {
+             vty_event (VTYSH_READ, sock, vty);
+             return 0;
+           }
+         vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
+         zlog_warn("%s: read failed on vtysh client fd %d, closing: %s",
+                   __func__, sock, safe_strerror(errno));
+       }
+      buffer_reset(vty->obuf);
       vty_close (vty);
 #ifdef VTYSH_DEBUG
       printf ("close vtysh\n");
@@ -1977,35 +2086,55 @@ vtysh_read (struct thread *thread)
     }
 
 #ifdef VTYSH_DEBUG
-  printf ("line: %s\n", buf);
+  printf ("line: %.*s\n", nbytes, buf);
 #endif /* VTYSH_DEBUG */
 
-  vty_ensure (vty, nbytes);
-  memcpy (vty->buf, buf, nbytes);
-  
-  /* Pass this line to parser. */
-  ret = vty_execute (vty);
-
-  vty_clear_buf (vty);
+  for (p = buf; p < buf+nbytes; p++)
+    {
+      vty_ensure(vty, vty->length+1);
+      vty->buf[vty->length++] = *p;
+      if (*p == '\0')
+       {
+         /* Pass this line to parser. */
+         ret = vty_execute (vty);
+         /* Note that vty_execute clears the command buffer and resets
+            vty->length to 0. */
 
-  /* Return result. */
+         /* Return result. */
 #ifdef VTYSH_DEBUG
-  printf ("result: %d\n", ret);
-  printf ("vtysh node: %d\n", vty->node);
+         printf ("result: %d\n", ret);
+         printf ("vtysh node: %d\n", vty->node);
 #endif /* VTYSH_DEBUG */
 
-  header[3] = ret;
-  write (vty->fd, header, 4);
+         header[3] = ret;
+         buffer_put(vty->obuf, header, 4);
+
+         if (!vty->t_write && (vtysh_flush(vty) < 0))
+           /* Try to flush results; exit if a write error occurs. */
+           return 0;
+       }
+    }
 
   vty_event (VTYSH_READ, sock, vty);
 
   return 0;
 }
+
+static int
+vtysh_write (struct thread *thread)
+{
+  struct vty *vty = THREAD_ARG (thread);
+
+  vty->t_write = NULL;
+  vtysh_flush(vty);
+  return 0;
+}
+
 #endif /* VTYSH */
 
 /* Determine address family to bind. */
 void
-vty_serv_sock (const char *addr, unsigned short port, char *path)
+vty_serv_sock (const char *addr, unsigned short port, const char *path)
 {
   /* If port is set to 0, do not listen on TCP/IP at all! */
   if (port)
@@ -2028,7 +2157,10 @@ vty_serv_sock (const char *addr, unsigned short port, char *path)
 #endif /* VTYSH */
 }
 
-/* Close vty interface. */
+/* Close vty interface.  Warning: call this only from functions that
+   will be careful not to access the vty afterwards (since it has
+   now been freed).  This is safest from top-level functions (called
+   directly by the thread dispatcher). */
 void
 vty_close (struct vty *vty)
 {
@@ -2041,20 +2173,13 @@ vty_close (struct vty *vty)
     thread_cancel (vty->t_write);
   if (vty->t_timeout)
     thread_cancel (vty->t_timeout);
-  if (vty->t_output)
-    thread_cancel (vty->t_output);
 
   /* Flush buffer. */
-  if (! buffer_empty (vty->obuf))
-    buffer_flush_all (vty->obuf, vty->fd);
+  buffer_flush_all (vty->obuf, vty->fd);
 
   /* Free input buffer. */
   buffer_free (vty->obuf);
 
-  /* Free SB buffer. */
-  if (vty->sb_buffer)
-    buffer_free (vty->sb_buffer);
-
   /* Free command history. */
   for (i = 0; i < VTY_MAXHIST; i++)
     if (vty->hist[i])
@@ -2067,11 +2192,12 @@ vty_close (struct vty *vty)
   if (vty->fd > 0)
     close (vty->fd);
 
-  if (vty->address)
-    XFREE (0, vty->address);
   if (vty->buf)
     XFREE (MTYPE_VTY, vty->buf);
 
+  if (vty->error_buf)
+    XFREE (MTYPE_VTY, vty->error_buf);
+
   /* Check configure. */
   vty_config_unlock (vty);
 
@@ -2106,36 +2232,43 @@ vty_read_file (FILE *confp)
 {
   int ret;
   struct vty *vty;
+  unsigned int line_num = 0;
 
   vty = vty_new ();
-  vty->fd = 0;                 /* stdout */
-  vty->type = VTY_TERM;
+  vty->fd = dup(STDERR_FILENO); /* vty_close() will close this */
+  if (vty->fd < 0)
+  {
+    /* Fine, we couldn't make a new fd. vty_close doesn't close stdout. */
+    vty->fd = STDOUT_FILENO;
+  }
+  vty->type = VTY_FILE;
   vty->node = CONFIG_NODE;
   
   /* Execute configuration file */
-  ret = config_from_file (vty, confp);
+  ret = config_from_file (vty, confp, &line_num);
+
+  /* Flush any previous errors before printing messages below */
+  buffer_flush_all (vty->obuf, vty->fd);
 
   if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) 
     {
       switch (ret)
        {
          case CMD_ERR_AMBIGUOUS:
-           fprintf (stderr, "Ambiguous command.\n");
+           fprintf (stderr, "*** Error reading config: Ambiguous command.\n");
            break;
          case CMD_ERR_NO_MATCH:
-           fprintf (stderr, "There is no such command.\n");
+           fprintf (stderr, "*** Error reading config: There is no such command.\n");
            break;
        }
-      fprintf (stderr, "Error occured during reading below line.\n%s\n", 
-              vty->buf);
-      vty_close (vty);
-      exit (1);
+      fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n",
+                      line_num, vty->error_buf);
     }
 
   vty_close (vty);
 }
 
-FILE *
+static FILE *
 vty_use_backup_config (char *fullpath)
 {
   char *fullpath_sav, *fullpath_tmp;
@@ -2169,18 +2302,34 @@ vty_use_backup_config (char *fullpath)
   sav = open (fullpath_sav, O_RDONLY);
   if (sav < 0)
     {
+      unlink (fullpath_tmp);
       free (fullpath_sav);
       free (fullpath_tmp);
-      unlink (fullpath_tmp);
       return NULL;
     }
   
   while((c = read (sav, buffer, 512)) > 0)
-    write (tmp, buffer, c);
-  
+    {
+      if (write (tmp, buffer, c) <= 0)
+       {
+         free (fullpath_sav);
+         free (fullpath_tmp);
+         close (sav);
+         close (tmp);
+         return NULL;
+       }
+    }
   close (sav);
   close (tmp);
   
+  if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)
+    {
+      unlink (fullpath_tmp);
+      free (fullpath_sav);
+      free (fullpath_tmp);
+      return NULL;
+    }
+  
   if (link (fullpath_tmp, fullpath) == 0)
     ret = fopen (fullpath, "r");
 
@@ -2188,136 +2337,170 @@ vty_use_backup_config (char *fullpath)
   
   free (fullpath_sav);
   free (fullpath_tmp);
-  return fopen (fullpath, "r");
+  return ret;
 }
 
 /* Read up configuration file from file_name. */
 void
 vty_read_config (char *config_file,
-                char *config_current_dir,
-                char *config_default_dir)
+                 char *config_default_dir)
 {
-  char *cwd;
+  char cwd[MAXPATHLEN];
   FILE *confp = NULL;
   char *fullpath;
+  char *tmp = NULL;
 
   /* If -f flag specified. */
   if (config_file != NULL)
     {
       if (! IS_DIRECTORY_SEP (config_file[0]))
-       {
-         cwd = getcwd (NULL, MAXPATHLEN);
-         fullpath = XMALLOC (MTYPE_TMP, 
-                             strlen (cwd) + strlen (config_file) + 2);
-         sprintf (fullpath, "%s/%s", cwd, config_file);
-       }
+        {
+          if (getcwd (cwd, MAXPATHLEN) == NULL)
+           {
+             fprintf (stderr, "Failure to determine Current Working Directory %d!\n", errno);
+             exit (1);
+           }
+          tmp = XMALLOC (MTYPE_TMP, 
+                             strlen (cwd) + strlen (config_file) + 2);
+          sprintf (tmp, "%s/%s", cwd, config_file);
+          fullpath = tmp;
+        }
       else
-       fullpath = config_file;
+        fullpath = config_file;
 
       confp = fopen (fullpath, "r");
 
       if (confp == NULL)
-       {
-         confp = vty_use_backup_config (fullpath);
-         if (confp)
-           fprintf (stderr, "WARNING: using backup configuration file!\n");
-         else
-           {
-             fprintf (stderr, "can't open configuration file [%s]\n", 
-                      config_file);
-             exit(1);
-           }
-       }
+        {
+          fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
+                   __func__, fullpath, safe_strerror (errno));
+          
+          confp = vty_use_backup_config (fullpath);
+          if (confp)
+            fprintf (stderr, "WARNING: using backup configuration file!\n");
+          else
+            {
+              fprintf (stderr, "can't open configuration file [%s]\n", 
+                      config_file);
+              exit(1);
+            }
+        }
     }
   else
     {
-      /* Relative path configuration file open. */
-      if (config_current_dir)
-       {
-         confp = fopen (config_current_dir, "r");
-         if (confp == NULL)
-           {
-             confp = vty_use_backup_config (config_current_dir);
-             if (confp)
-               fprintf (stderr, "WARNING: using backup configuration file!\n");
-           }
-       }
 
-      /* If there is no relative path exists, open system default file. */
-      if (confp == NULL)
-       {
+      host_config_set (config_default_dir);
+
 #ifdef VTYSH
-         int ret;
-         struct stat conf_stat;
-
-         /* !!!!PLEASE LEAVE!!!!
-            This is NEEDED for use with vtysh -b, or else you can get
-            a real configuration food fight with a lot garbage in the
-            merged configuration file it creates coming from the per
-            daemon configuration files.  This also allows the daemons
-            to start if there default configuration file is not
-            present or ignore them, as needed when using vtysh -b to
-            configure the daemons at boot - MAG */
-
-         /* Stat for vtysh Zebra.conf, if found startup and wait for
-            boot configuration */
-
-         if ( strstr(config_default_dir, "vtysh") == NULL)
-           {
-             ret = stat (integrate_default, &conf_stat);
-             if (ret >= 0)
-               {
-                 return;
-               }
-           }
+      int ret;
+      struct stat conf_stat;
+
+      /* !!!!PLEASE LEAVE!!!!
+       * This is NEEDED for use with vtysh -b, or else you can get
+       * a real configuration food fight with a lot garbage in the
+       * merged configuration file it creates coming from the per
+       * daemon configuration files.  This also allows the daemons
+       * to start if there default configuration file is not
+       * present or ignore them, as needed when using vtysh -b to
+       * configure the daemons at boot - MAG
+       */
+
+      /* Stat for vtysh Zebra.conf, if found startup and wait for
+       * boot configuration
+       */
+
+      if ( strstr(config_default_dir, "vtysh") == NULL)
+        {
+          ret = stat (integrate_default, &conf_stat);
+          if (ret >= 0)
+           goto tmp_free_and_out;
+        }
 #endif /* VTYSH */
-
-         confp = fopen (config_default_dir, "r");
-         if (confp == NULL)
-           {
-             confp = vty_use_backup_config (config_default_dir);
-             if (confp)
-               {
-                 fprintf (stderr, "WARNING: using backup configuration file!\n");
-                 fullpath = config_default_dir;
-               }
-             else
-               {
-                 fprintf (stderr, "can't open configuration file [%s]\n",
-                          config_default_dir);
-                 exit (1);
-               }
-           }      
-         else
-           fullpath = config_default_dir;
-       }
+      confp = fopen (config_default_dir, "r");
+      if (confp == NULL)
+        {
+          fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
+                   __func__, config_default_dir, safe_strerror (errno));
+          
+          confp = vty_use_backup_config (config_default_dir);
+          if (confp)
+            {
+              fprintf (stderr, "WARNING: using backup configuration file!\n");
+              fullpath = config_default_dir;
+            }
+          else
+            {
+              fprintf (stderr, "can't open configuration file [%s]\n",
+                      config_default_dir);
+             goto tmp_free_and_out;
+            }
+        }      
       else
-       {
-         /* Rleative path configuration file. */
-         cwd = getcwd (NULL, MAXPATHLEN);
-         fullpath = XMALLOC (MTYPE_TMP, 
-                             strlen (cwd) + strlen (config_current_dir) + 2);
-         sprintf (fullpath, "%s/%s", cwd, config_current_dir);
-       }  
-    }  
+        fullpath = config_default_dir;
+    }
+
   vty_read_file (confp);
 
   fclose (confp);
 
   host_config_set (fullpath);
+
+tmp_free_and_out:
+  if (tmp)
+    XFREE (MTYPE_TMP, fullpath);
 }
 
 /* Small utility function which output log to the VTY. */
 void
-vty_log (const char *proto_str, const char *format, va_list va)
+vty_log (const char *level, const char *proto_str,
+        const char *format, struct timestamp_control *ctl, va_list va)
 {
-  int i;
+  unsigned int i;
   struct vty *vty;
+  
+  if (!vtyvec)
+    return;
 
-  for (i = 0; i < vector_max (vtyvec); i++)
+  for (i = 0; i < vector_active (vtyvec); i++)
     if ((vty = vector_slot (vtyvec, i)) != NULL)
       if (vty->monitor)
-       vty_log_out (vty, proto_str, format, va);
+       {
+         va_list ac;
+         va_copy(ac, va);
+         vty_log_out (vty, level, proto_str, format, ctl, ac);
+         va_end(ac);
+       }
+}
+
+/* Async-signal-safe version of vty_log for fixed strings. */
+void
+vty_log_fixed (char *buf, size_t len)
+{
+  unsigned int i;
+  struct iovec iov[2];
+  char crlf[4] = "\r\n";
+
+  /* vty may not have been initialised */
+  if (!vtyvec)
+    return;
+  
+  iov[0].iov_base = (void *)buf;
+  iov[0].iov_len = len;
+  iov[1].iov_base = crlf;
+  iov[1].iov_len = 2;
+
+  for (i = 0; i < vector_active (vtyvec); i++)
+    {
+      struct vty *vty;
+      if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
+       /* N.B. We don't care about the return code, since process is
+          most likely just about to die anyway. */
+       if (writev(vty->fd, iov, 2) == -1)
+         {
+           fprintf(stderr, "Failure to writev: %d\n", errno);
+           exit(-1);
+         }
+    }
 }
 
 int
@@ -2341,9 +2524,9 @@ vty_config_unlock (struct vty *vty)
     }
   return vty->config;
 }
-\f
+
 /* Master of the threads. */
-static struct thread_master *master;
+static struct thread_master *vty_master;
 
 static void
 vty_event (enum event event, int sock, struct vty *vty)
@@ -2353,19 +2536,23 @@ vty_event (enum event event, int sock, struct vty *vty)
   switch (event)
     {
     case VTY_SERV:
-      vty_serv_thread = thread_add_read (master, vty_accept, vty, sock);
+      vty_serv_thread = thread_add_read (vty_master, vty_accept, vty, sock);
       vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
       break;
 #ifdef VTYSH
     case VTYSH_SERV:
-      thread_add_read (master, vtysh_accept, vty, sock);
+      vty_serv_thread = thread_add_read (vty_master, vtysh_accept, vty, sock);
+      vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
       break;
     case VTYSH_READ:
-      thread_add_read (master, vtysh_read, vty, sock);
+      vty->t_read = thread_add_read (vty_master, vtysh_read, vty, sock);
+      break;
+    case VTYSH_WRITE:
+      vty->t_write = thread_add_write (vty_master, vtysh_write, vty, sock);
       break;
 #endif /* VTYSH */
     case VTY_READ:
-      vty->t_read = thread_add_read (master, vty_read, vty, sock);
+      vty->t_read = thread_add_read (vty_master, vty_read, vty, sock);
 
       /* Time out treatment. */
       if (vty->v_timeout)
@@ -2373,12 +2560,12 @@ vty_event (enum event event, int sock, struct vty *vty)
          if (vty->t_timeout)
            thread_cancel (vty->t_timeout);
          vty->t_timeout = 
-           thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
+           thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
        }
       break;
     case VTY_WRITE:
       if (! vty->t_write)
-       vty->t_write = thread_add_write (master, vty_flush, vty, sock);
+       vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock);
       break;
     case VTY_TIMEOUT_RESET:
       if (vty->t_timeout)
@@ -2389,21 +2576,21 @@ vty_event (enum event event, int sock, struct vty *vty)
       if (vty->v_timeout)
        {
          vty->t_timeout = 
-           thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
+           thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
        }
       break;
     }
 }
-\f
+
 DEFUN (config_who,
        config_who_cmd,
        "who",
        "Display who is on vty\n")
 {
-  int i;
+  unsigned int i;
   struct vty *v;
 
-  for (i = 0; i < vector_max (vtyvec); i++)
+  for (i = 0; i < vector_active (vtyvec); i++)
     if ((v = vector_slot (vtyvec, i)) != NULL)
       vty_out (vty, "%svty[%d] connected from %s.%s",
               v->config ? "*" : " ",
@@ -2423,8 +2610,8 @@ DEFUN (line_vty,
 }
 
 /* Set time out value. */
-int
-exec_timeout (struct vty *vty, char *min_str, char *sec_str)
+static int
+exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
 {
   unsigned long timeout = 0;
 
@@ -2573,6 +2760,26 @@ DEFUN (no_vty_login,
   return CMD_SUCCESS;
 }
 
+/* initial mode. */
+DEFUN (vty_restricted_mode,
+       vty_restricted_mode_cmd,
+       "anonymous restricted",
+       "Restrict view commands available in anonymous, unauthenticated vty\n")
+{
+  restricted_mode = 1;
+  return CMD_SUCCESS;
+}
+
+DEFUN (vty_no_restricted_mode,
+       vty_no_restricted_mode_cmd,
+       "no anonymous restricted",
+       NO_STR
+       "Enable password checking\n")
+{
+  restricted_mode = 0;
+  return CMD_SUCCESS;
+}
+
 DEFUN (service_advanced_vty,
        service_advanced_vty_cmd,
        "service advanced-vty",
@@ -2615,6 +2822,13 @@ DEFUN (terminal_no_monitor,
   return CMD_SUCCESS;
 }
 
+ALIAS (terminal_no_monitor,
+       no_terminal_monitor_cmd,
+       "no terminal monitor",
+       NO_STR
+       "Set terminal line parameters\n"
+       "Copy debug output to the current terminal line\n")
+
 DEFUN (show_history,
        show_history_cmd,
        "show history",
@@ -2641,7 +2855,7 @@ DEFUN (show_history,
 }
 
 /* Display current configuration. */
-int
+static int
 vty_config_write (struct vty *vty)
 {
   vty_out (vty, "line vty%s", VTY_NEWLINE);
@@ -2663,7 +2877,15 @@ vty_config_write (struct vty *vty)
   /* login */
   if (no_password_check)
     vty_out (vty, " no login%s", VTY_NEWLINE);
-
+    
+  if (restricted_mode != restricted_mode_default)
+    {
+      if (restricted_mode_default)
+        vty_out (vty, " no anonymous restricted%s", VTY_NEWLINE);
+      else
+        vty_out (vty, " anonymous restricted%s", VTY_NEWLINE);
+    }
+  
   vty_out (vty, "!%s", VTY_NEWLINE);
 
   return CMD_SUCCESS;
@@ -2673,17 +2895,18 @@ struct cmd_node vty_node =
 {
   VTY_NODE,
   "%s(config-line)# ",
+  1,
 };
 
 /* Reset all VTY status. */
 void
 vty_reset ()
 {
-  int i;
+  unsigned int i;
   struct vty *vty;
   struct thread *vty_serv_thread;
 
-  for (i = 0; i < vector_max (vtyvec); i++)
+  for (i = 0; i < vector_active (vtyvec); i++)
     if ((vty = vector_slot (vtyvec, i)) != NULL)
       {
        buffer_reset (vty->obuf);
@@ -2691,7 +2914,7 @@ vty_reset ()
        vty_close (vty);
       }
 
-  for (i = 0; i < vector_max (Vvty_serv_thread); i++)
+  for (i = 0; i < vector_active (Vvty_serv_thread); i++)
     if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
       {
        thread_cancel (vty_serv_thread);
@@ -2714,52 +2937,32 @@ vty_reset ()
     }
 }
 
-/* for ospf6d easy temprary reload function */
-/* vty_reset + close accept socket */
-void
-vty_finish ()
+static void
+vty_save_cwd (void)
 {
-  int i;
-  struct vty *vty;
-  struct thread *vty_serv_thread;
+  char cwd[MAXPATHLEN];
+  char *c;
 
-  for (i = 0; i < vector_max (vtyvec); i++)
-    if ((vty = vector_slot (vtyvec, i)) != NULL)
-      {
-       buffer_reset (vty->obuf);
-       vty->status = VTY_CLOSE;
-       vty_close (vty);
-      }
+  c = getcwd (cwd, MAXPATHLEN);
 
-  for (i = 0; i < vector_max (Vvty_serv_thread); i++)
-    if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
-      {
-       thread_cancel (vty_serv_thread);
-       vector_slot (Vvty_serv_thread, i) = NULL;
-        close (i);
-      }
-
-  vty_timeout_val = VTY_TIMEOUT_DEFAULT;
-
-  if (vty_accesslist_name)
+  if (!c)
     {
-      XFREE(MTYPE_VTY, vty_accesslist_name);
-      vty_accesslist_name = NULL;
-    }
-
-  if (vty_ipv6_accesslist_name)
-    {
-      XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
-      vty_ipv6_accesslist_name = NULL;
+      /*
+       * At this point if these go wrong, more than likely
+       * the whole world is coming down around us
+       * Hence not worrying about it too much.
+       */
+      if (!chdir (SYSCONFDIR))
+       {
+         fprintf(stderr, "Failure to chdir to %s, errno: %d\n", SYSCONFDIR, errno);
+         exit(-1);
+       }
+      if (getcwd (cwd, MAXPATHLEN) == NULL)
+       {
+         fprintf(stderr, "Failure to getcwd, errno: %d\n", errno);
+         exit(-1);
+       }
     }
-}
-
-void
-vty_save_cwd ()
-{
-  char *cwd;
-
-  cwd = getcwd (NULL, MAXPATHLEN);
 
   vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
   strcpy (vty_cwd, cwd);
@@ -2798,7 +3001,7 @@ vty_init (struct thread_master *master_thread)
 
   vtyvec = vector_init (VECTOR_MIN_SIZE);
 
-  master = master_thread;
+  vty_master = master_thread;
 
   /* Initilize server thread vector. */
   Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE);
@@ -2806,6 +3009,8 @@ vty_init (struct thread_master *master_thread)
   /* Install bgp top node. */
   install_node (&vty_node, vty_config_write);
 
+  install_element (RESTRICTED_NODE, &config_who_cmd);
+  install_element (RESTRICTED_NODE, &show_history_cmd);
   install_element (VIEW_NODE, &config_who_cmd);
   install_element (VIEW_NODE, &show_history_cmd);
   install_element (ENABLE_NODE, &config_who_cmd);
@@ -2815,6 +3020,7 @@ vty_init (struct thread_master *master_thread)
   install_element (CONFIG_NODE, &show_history_cmd);
   install_element (ENABLE_NODE, &terminal_monitor_cmd);
   install_element (ENABLE_NODE, &terminal_no_monitor_cmd);
+  install_element (ENABLE_NODE, &no_terminal_monitor_cmd);
   install_element (ENABLE_NODE, &show_history_cmd);
 
   install_default (VTY_NODE);
@@ -2825,8 +3031,24 @@ vty_init (struct thread_master *master_thread)
   install_element (VTY_NODE, &no_vty_access_class_cmd);
   install_element (VTY_NODE, &vty_login_cmd);
   install_element (VTY_NODE, &no_vty_login_cmd);
+  install_element (VTY_NODE, &vty_restricted_mode_cmd);
+  install_element (VTY_NODE, &vty_no_restricted_mode_cmd);
 #ifdef HAVE_IPV6
   install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
   install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
 #endif /* HAVE_IPV6 */
 }
+
+void
+vty_terminate (void)
+{
+  if (vty_cwd)
+    XFREE (MTYPE_TMP, vty_cwd);
+
+  if (vtyvec && Vvty_serv_thread)
+    {
+      vty_reset ();
+      vector_free (vtyvec);
+      vector_free (Vvty_serv_thread);
+    }
+}