diff options
Diffstat (limited to 'lib/vty.c')
-rw-r--r-- | lib/vty.c | 996 |
1 files changed, 768 insertions, 228 deletions
@@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. + * 02111-1307, USA. */ #include <zebra.h> @@ -38,9 +38,37 @@ #include "network.h" #include <arpa/telnet.h> +#include "qpthreads.h" +#include "qpnexus.h" + +/* Needs to be qpthread safe */ +qpt_mutex_t* vty_mutex = NULL; +#ifdef NDEBUG +#define LOCK qpt_mutex_lock(vty_mutex); +#define UNLOCK qpt_mutex_unlock(vty_mutex); +#else +int vty_lock_count = 0; +#define LOCK qpt_mutex_lock(vty_mutex);++vty_lock_count; +#define UNLOCK --vty_lock_count;qpt_mutex_unlock(vty_mutex); +#endif + +/* + * To make vty qpthread safe we use a single mutex. In general external + * routines have explicit locks, static routines assume that they are being + * called with the mutex already locked. There are a few exceptions, e.g. + * callbacks where static routines are being invoked from outside the module. + * + * As we are not using recursive mutexes so there are a few cases where + * both external and static versions of a routine exist. The former for use + * outside, the latter for use inside the module (and lock). In these cases + * the internal static versions starts uty_. + * + * vty and log recurse through each other, so the same mutex is used + * for both, i.e. they are treated as being part of the same monitor. + */ /* Vty events */ -enum event +enum event { VTY_SERV, VTY_READ, @@ -53,11 +81,27 @@ enum event #endif /* VTYSH */ }; +/* Prototypes */ +static int uty_out (struct vty *vty, const char *format, ...); +static int uty_vout(struct vty *vty, const char *format, va_list args); static void vty_event (enum event, int, struct vty *); +static void uty_hello (struct vty *vty); +static void uty_close (struct vty *vty); +static int uty_config_unlock (struct vty *vty); +static int uty_shell (struct vty *vty); +static int uty_read (struct vty *vty, int vty_sock); +static int uty_flush (struct vty *vty, int vty_sock); +static void vty_event_t (enum event event, int sock, struct vty *vty); +static void vty_event_r (enum event event, int sock, struct vty *vty); +static int uty_accept (int accept_sock); +static int uty_timeout (struct vty *vty); +static void vty_timeout_r (qtimer qtr, void* timer_info, qtime_t when); +static void vty_read_r (qps_file qf, void* file_info); +static void vty_flush_r (qps_file qf, void* file_info); /* Extern host structure from command.c */ extern struct host host; - + /* Vector which store each vty structure. */ static vector vtyvec; @@ -71,10 +115,10 @@ 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; +static char *vty_cwd = NULL; /* Configure lock. */ static int vty_config; @@ -89,29 +133,60 @@ static u_char restricted_mode = 0; /* Integrated configuration file path */ char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; - -/* VTY standard output function. */ +/* Master of the threads. */ +static struct thread_master *master = NULL; +static qpn_nexus master_nexus = NULL; + +/* VTY standard output function. vty == NULL or VTY_SHELL => stdout */ int vty_out (struct vty *vty, const char *format, ...) { + int result; + + LOCK va_list args; + va_start (args, format); + result = uty_vout(vty, format, args); + va_end (args); + UNLOCK + return result; +} + +/* internal VTY standard output function. vty == NULL or VTY_SHELL => stdout */ +static int +uty_out (struct vty *vty, const char *format, ...) +{ + int result; + assert(vty_lock_count); + va_list args; + va_start (args, format); + result = uty_vout(vty, format, args); + va_end (args); + return result; +} + +/* internal VTY standard output function. vty == NULL or VTY_SHELL => stdout */ +static int +uty_vout(struct vty *vty, const char *format, va_list args) +{ int len = 0; int size = 1024; char buf[1024]; char *p = NULL; + va_list ac; + + assert(vty_lock_count); - if (vty_shell (vty)) + if (uty_shell (vty)) { - 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); + va_copy(ac, args); + len = vsnprintf (buf, sizeof buf, format, ac); + va_end(ac); /* Initial buffer is not enough. */ if (len < 0 || len >= size) @@ -125,11 +200,11 @@ vty_out (struct vty *vty, const char *format, ...) p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size); if (! p) - return -1; + return -1; - va_start (args, format); - len = vsnprintf (p, size, format, args); - va_end (args); + va_copy(ac, args); + len = vsnprintf (p, size, format, ac); + va_end(ac); if (len > -1 && len < size) break; @@ -151,6 +226,27 @@ vty_out (struct vty *vty, const char *format, ...) return len; } +int +vty_puts(struct vty *vty, const char* str) +{ + return vty_out(vty, "%s", str) ; +} + +int +vty_out_newline(struct vty *vty) +{ + return vty_out(vty, "%s", VTY_NEWLINE) ; +} + +/* 123456789012345678901234 */ +const char* vty_spaces_string = " " ; + +int +vty_out_indent(struct vty *vty, int indent) +{ + return vty_puts(vty, VTY_SPACES(indent)) ; +} + 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) @@ -159,9 +255,11 @@ vty_log_out (struct vty *vty, const char *level, const char *proto_str, int len; char buf[1024]; + assert(vty_lock_count); + if (!ctl->already_rendered) { - ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf)); + ctl->len = uquagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf)); ctl->already_rendered = 1; } if (ctl->len+1 >= sizeof(buf)) @@ -192,7 +290,7 @@ vty_log_out (struct vty *vty, const char *level, const char *proto_str, 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", + uzlog(NULL, LOG_WARNING, "%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 @@ -209,7 +307,7 @@ void vty_time_print (struct vty *vty, int cr) { char buf [25]; - + if (quagga_timestamp(0, buf, sizeof(buf)) == 0) { zlog (NULL, LOG_INFO, "quagga_timestamp error"); @@ -227,6 +325,15 @@ vty_time_print (struct vty *vty, int cr) void vty_hello (struct vty *vty) { + LOCK + uty_hello(vty); + UNLOCK +} + +static void +uty_hello (struct vty *vty) +{ + assert(vty_lock_count); if (host.motdfile) { FILE *f; @@ -242,15 +349,15 @@ vty_hello (struct vty *vty) for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1)); s--); *s = '\0'; - vty_out (vty, "%s%s", buf, VTY_NEWLINE); + uty_out (vty, "%s%s", buf, VTY_NEWLINE); } fclose (f); } else - vty_out (vty, "MOTD file not found%s", VTY_NEWLINE); + uty_out (vty, "MOTD file not found%s", VTY_NEWLINE); } else if (host.motd) - vty_out (vty, host.motd); + uty_out (vty, "%s", host.motd); } /* Put out prompt and wait input from user. */ @@ -260,6 +367,8 @@ vty_prompt (struct vty *vty) struct utsname names; const char*hostname; + assert(vty_lock_count); + if (vty->type == VTY_TERM) { hostname = host.name; @@ -268,7 +377,7 @@ vty_prompt (struct vty *vty) uname (&names); hostname = names.nodename; } - vty_out (vty, cmd_prompt (vty->node), hostname); + uty_out (vty, cmd_prompt (vty->node), hostname); } } @@ -277,7 +386,8 @@ static void vty_will_echo (struct vty *vty) { unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; - vty_out (vty, "%s", cmd); + assert(vty_lock_count); + uty_out (vty, "%s", cmd); } /* Make suppress Go-Ahead telnet option. */ @@ -285,7 +395,8 @@ static void vty_will_suppress_go_ahead (struct vty *vty) { unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' }; - vty_out (vty, "%s", cmd); + assert(vty_lock_count); + uty_out (vty, "%s", cmd); } /* Make don't use linemode over telnet. */ @@ -293,7 +404,8 @@ static void vty_dont_linemode (struct vty *vty) { unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; - vty_out (vty, "%s", cmd); + assert(vty_lock_count); + uty_out (vty, "%s", cmd); } /* Use window size. */ @@ -301,7 +413,8 @@ static void vty_do_window_size (struct vty *vty) { unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' }; - vty_out (vty, "%s", cmd); + assert(vty_lock_count); + uty_out (vty, "%s", cmd); } #if 0 /* Currently not used. */ @@ -310,21 +423,30 @@ static void vty_dont_lflow_ahead (struct vty *vty) { unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' }; - vty_out (vty, "%s", cmd); + assert(vty_lock_count); + uty_out (vty, "%s", cmd); } #endif /* 0 */ /* Allocate new vty struct. */ struct vty * -vty_new () +vty_new (int fd) { - struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty)); + struct vty *vty = XCALLOC (MTYPE_VTY, sizeof (struct vty)); - new->obuf = buffer_new(0); /* Use default buffer size. */ - new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ); - new->max = VTY_BUFSIZ; + vty->obuf = buffer_new(0); /* Use default buffer size. */ + vty->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ); + vty->max = VTY_BUFSIZ; + vty->fd = fd; - return new; + if (master_nexus) + { + vty->qf = qps_file_init_new(vty->qf, NULL); + qps_add_file(master_nexus->selection, vty->qf, vty->fd, vty); + vty->qtr = qtimer_init_new(vty->qtr, master_nexus->pile, vty_timeout_r, vty); + } + + return vty; } /* Authentication of vty */ @@ -336,6 +458,8 @@ vty_auth (struct vty *vty, char *buf) int fail; char *crypt (const char *, const char *); + assert(vty_lock_count); + switch (vty->node) { case AUTH_NODE: @@ -379,14 +503,14 @@ vty_auth (struct vty *vty, char *buf) { if (vty->node == AUTH_NODE) { - vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); + uty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); vty->status = VTY_CLOSE; } - else + else { /* AUTH_ENABLE_NODE */ vty->fail = 0; - vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); + uty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE; } } @@ -401,6 +525,8 @@ vty_command (struct vty *vty, char *buf) vector vline; const char *protocolname; + assert(vty_lock_count); + /* Split readline string up into the vector */ vline = cmd_make_strvec (buf); @@ -416,20 +542,19 @@ vty_command (struct vty *vty, char *buf) GETRUSAGE(&before); #endif /* CONSUMED_TIME_CHECK */ + UNLOCK ret = cmd_execute_command (vline, vty, NULL, 0); + LOCK /* 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]; - + protocolname = uzlog_get_proto_name(NULL); + #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", + uzlog(NULL, LOG_WARNING, "SLOW COMMAND: command took %lums (cpu time %lums): %s", realtime/1000, cputime/1000, buf); } #endif /* CONSUMED_TIME_CHECK */ @@ -439,23 +564,23 @@ vty_command (struct vty *vty, char *buf) { case CMD_WARNING: if (vty->type == VTY_FILE) - vty_out (vty, "Warning...%s", VTY_NEWLINE); + uty_out (vty, "Warning...%s", VTY_NEWLINE); break; case CMD_ERR_AMBIGUOUS: - vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); break; case CMD_ERR_NO_MATCH: - vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); + uty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); break; case CMD_ERR_INCOMPLETE: - vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); + uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); break; } cmd_free_strvec (vline); return ret; } - + static const char telnet_backward_char = 0x08; static const char telnet_space_char = ' '; @@ -463,6 +588,7 @@ static const char telnet_space_char = ' '; static void vty_write (struct vty *vty, const char *buf, size_t nbytes) { + assert(vty_lock_count); if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) return; @@ -474,6 +600,7 @@ vty_write (struct vty *vty, const char *buf, size_t nbytes) static void vty_ensure (struct vty *vty, int length) { + assert(vty_lock_count); if (vty->max <= length) { vty->max *= 2; @@ -488,6 +615,8 @@ vty_self_insert (struct vty *vty, char c) int i; int length; + assert(vty_lock_count); + vty_ensure (vty, vty->length + 1); length = vty->length - vty->cp; memmove (&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length); @@ -505,6 +634,7 @@ vty_self_insert (struct vty *vty, char c) static void vty_self_insert_overwrite (struct vty *vty, char c) { + assert(vty_lock_count); vty_ensure (vty, vty->length + 1); vty->buf[vty->cp++] = c; @@ -521,7 +651,11 @@ vty_self_insert_overwrite (struct vty *vty, char c) static void vty_insert_word_overwrite (struct vty *vty, char *str) { + int len = strlen (str); + + assert(vty_lock_count); + vty_write (vty, str, len); strcpy (&vty->buf[vty->cp], str); vty->cp += len; @@ -532,6 +666,7 @@ vty_insert_word_overwrite (struct vty *vty, char *str) static void vty_forward_char (struct vty *vty) { + assert(vty_lock_count); if (vty->cp < vty->length) { vty_write (vty, &vty->buf[vty->cp], 1); @@ -543,6 +678,7 @@ vty_forward_char (struct vty *vty) static void vty_backward_char (struct vty *vty) { + assert(vty_lock_count); if (vty->cp > 0) { vty->cp--; @@ -554,6 +690,7 @@ vty_backward_char (struct vty *vty) static void vty_beginning_of_line (struct vty *vty) { + assert(vty_lock_count); while (vty->cp) vty_backward_char (vty); } @@ -562,6 +699,7 @@ vty_beginning_of_line (struct vty *vty) static void vty_end_of_line (struct vty *vty) { + assert(vty_lock_count); while (vty->cp < vty->length) vty_forward_char (vty); } @@ -576,6 +714,8 @@ vty_history_print (struct vty *vty) { int length; + assert(vty_lock_count); + vty_kill_line_from_beginning (vty); /* Get previous line from history buffer */ @@ -593,6 +733,8 @@ vty_next_line (struct vty *vty) { int try_index; + assert(vty_lock_count); + if (vty->hp == vty->hindex) return; @@ -618,6 +760,8 @@ vty_previous_line (struct vty *vty) { int try_index; + assert(vty_lock_count); + try_index = vty->hp; if (try_index == 0) try_index = VTY_MAXHIST - 1; @@ -636,6 +780,7 @@ vty_previous_line (struct vty *vty) static void vty_redraw_line (struct vty *vty) { + assert(vty_lock_count); vty_write (vty, vty->buf, vty->length); vty->cp = vty->length; } @@ -644,9 +789,10 @@ vty_redraw_line (struct vty *vty) static void vty_forward_word (struct vty *vty) { + assert(vty_lock_count); while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') vty_forward_char (vty); - + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') vty_forward_char (vty); } @@ -655,6 +801,7 @@ vty_forward_word (struct vty *vty) static void vty_backward_pure_word (struct vty *vty) { + assert(vty_lock_count); while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') vty_backward_char (vty); } @@ -663,6 +810,7 @@ vty_backward_pure_word (struct vty *vty) static void vty_backward_word (struct vty *vty) { + assert(vty_lock_count); while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') vty_backward_char (vty); @@ -675,7 +823,8 @@ vty_backward_word (struct vty *vty) static void vty_down_level (struct vty *vty) { - vty_out (vty, "%s", VTY_NEWLINE); + assert(vty_lock_count); + uty_out (vty, "%s", VTY_NEWLINE); (*config_exit_cmd.func)(NULL, vty, 0, NULL); vty_prompt (vty); vty->cp = 0; @@ -685,7 +834,8 @@ vty_down_level (struct vty *vty) static void vty_end_config (struct vty *vty) { - vty_out (vty, "%s", VTY_NEWLINE); + assert(vty_lock_count); + uty_out (vty, "%s", VTY_NEWLINE); switch (vty->node) { @@ -713,7 +863,7 @@ vty_end_config (struct vty *vty) case KEYCHAIN_KEY_NODE: case MASC_NODE: case VTY_NODE: - vty_config_unlock (vty); + uty_config_unlock (vty); vty->node = ENABLE_NODE; break; default: @@ -732,6 +882,8 @@ vty_delete_char (struct vty *vty) int i; int size; + assert(vty_lock_count); + if (vty->length == 0) { vty_down_level (vty); @@ -746,7 +898,7 @@ 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; @@ -761,6 +913,7 @@ vty_delete_char (struct vty *vty) static void vty_delete_backward_char (struct vty *vty) { + assert(vty_lock_count); if (vty->cp == 0) return; @@ -775,8 +928,10 @@ vty_kill_line (struct vty *vty) int i; int size; + assert(vty_lock_count); + size = vty->length - vty->cp; - + if (size == 0) return; @@ -793,6 +948,7 @@ vty_kill_line (struct vty *vty) static void vty_kill_line_from_beginning (struct vty *vty) { + assert(vty_lock_count); vty_beginning_of_line (vty); vty_kill_line (vty); } @@ -801,6 +957,7 @@ vty_kill_line_from_beginning (struct vty *vty) static void vty_forward_kill_word (struct vty *vty) { + assert(vty_lock_count); while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') vty_delete_char (vty); while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') @@ -811,6 +968,7 @@ vty_forward_kill_word (struct vty *vty) static void vty_backward_kill_word (struct vty *vty) { + assert(vty_lock_count); while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') vty_delete_backward_char (vty); while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') @@ -823,6 +981,8 @@ vty_transpose_chars (struct vty *vty) { char c1, c2; + assert(vty_lock_count); + /* If length is short or point is near by the beginning of line then return. */ if (vty->length < 2 || vty->cp < 1) @@ -854,11 +1014,14 @@ vty_transpose_chars (struct vty *vty) static void vty_complete_command (struct vty *vty) { + int i; int ret; char **matched = NULL; vector vline; + assert(vty_lock_count); + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) return; @@ -871,14 +1034,14 @@ vty_complete_command (struct vty *vty) vector_set (vline, '\0'); matched = cmd_complete_command (vline, vty, &ret); - + cmd_free_strvec (vline); - vty_out (vty, "%s", VTY_NEWLINE); + uty_out (vty, "%s", VTY_NEWLINE); switch (ret) { case CMD_ERR_AMBIGUOUS: - vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); vty_prompt (vty); vty_redraw_line (vty); break; @@ -908,11 +1071,11 @@ vty_complete_command (struct vty *vty) for (i = 0; matched[i] != NULL; i++) { if (i != 0 && ((i % 6) == 0)) - vty_out (vty, "%s", VTY_NEWLINE); - vty_out (vty, "%-10s ", matched[i]); + uty_out (vty, "%s", VTY_NEWLINE); + uty_out (vty, "%-10s ", matched[i]); XFREE (MTYPE_TMP, matched[i]); } - vty_out (vty, "%s", VTY_NEWLINE); + uty_out (vty, "%s", VTY_NEWLINE); vty_prompt (vty); vty_redraw_line (vty); @@ -936,11 +1099,13 @@ vty_describe_fold (struct vty *vty, int cmd_width, const char *cmd, *p; int pos; + assert(vty_lock_count); + cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; if (desc_width <= 0) { - vty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE); + uty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE); return; } @@ -957,12 +1122,12 @@ vty_describe_fold (struct vty *vty, int cmd_width, strncpy (buf, p, pos); buf[pos] = '\0'; - vty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE); + uty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE); cmd = ""; } - vty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE); + uty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE); XFREE (MTYPE_TMP, buf); } @@ -971,12 +1136,14 @@ vty_describe_fold (struct vty *vty, int cmd_width, static void vty_describe_command (struct vty *vty) { - int ret; + int ret; vector vline; vector describe; unsigned int i, width, desc_width; struct desc *desc, *desc_cr = NULL; + assert(vty_lock_count); + vline = cmd_make_strvec (vty->buf); /* In case of '> ?'. */ @@ -985,26 +1152,26 @@ vty_describe_command (struct vty *vty) vline = vector_init (1); vector_set (vline, '\0'); } - else + else if (isspace ((int) vty->buf[vty->length - 1])) vector_set (vline, '\0'); describe = cmd_describe_command (vline, vty, &ret); - vty_out (vty, "%s", VTY_NEWLINE); + uty_out (vty, "%s", VTY_NEWLINE); /* Ambiguous error. */ switch (ret) { case CMD_ERR_AMBIGUOUS: - vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); goto out; break; case CMD_ERR_NO_MATCH: - vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); + uty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); goto out; break; - } + } /* Get width of command string. */ width = 0; @@ -1033,7 +1200,7 @@ vty_describe_command (struct vty *vty) { if (desc->cmd[0] == '\0') continue; - + if (strcmp (desc->cmd, command_cr) == 0) { desc_cr = desc; @@ -1041,18 +1208,18 @@ vty_describe_command (struct vty *vty) } if (!desc->str) - vty_out (vty, " %-s%s", + uty_out (vty, " %-s%s", desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, VTY_NEWLINE); else if (desc_width >= strlen (desc->str)) - vty_out (vty, " %-*s %s%s", width, + uty_out (vty, " %-*s %s%s", width, desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str, VTY_NEWLINE); else vty_describe_fold (vty, width, desc_width, desc); #if 0 - vty_out (vty, " %-*s %s%s", width + uty_out (vty, " %-*s %s%s", width desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str ? desc->str : "", VTY_NEWLINE); #endif /* 0 */ @@ -1061,11 +1228,11 @@ vty_describe_command (struct vty *vty) if ((desc = desc_cr)) { if (!desc->str) - vty_out (vty, " %-s%s", + uty_out (vty, " %-s%s", desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, VTY_NEWLINE); else if (desc_width >= strlen (desc->str)) - vty_out (vty, " %-*s %s%s", width, + uty_out (vty, " %-*s %s%s", width, desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str, VTY_NEWLINE); else @@ -1084,6 +1251,7 @@ out: static void vty_clear_buf (struct vty *vty) { + assert(vty_lock_count); memset (vty->buf, 0, vty->max); } @@ -1091,9 +1259,10 @@ vty_clear_buf (struct vty *vty) static void vty_stop_input (struct vty *vty) { + assert(vty_lock_count); vty->cp = vty->length = 0; vty_clear_buf (vty); - vty_out (vty, "%s", VTY_NEWLINE); + uty_out (vty, "%s", VTY_NEWLINE); switch (vty->node) { @@ -1116,7 +1285,7 @@ vty_stop_input (struct vty *vty) case KEYCHAIN_KEY_NODE: case MASC_NODE: case VTY_NODE: - vty_config_unlock (vty); + uty_config_unlock (vty); vty->node = ENABLE_NODE; break; default: @@ -1135,6 +1304,8 @@ vty_hist_add (struct vty *vty) { int index; + assert(vty_lock_count); + if (vty->length == 0) return; @@ -1170,46 +1341,48 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) #ifdef TELNET_OPTION_DEBUG int i; + assert(vty_lock_count); + for (i = 0; i < nbytes; i++) { switch (buf[i]) { case IAC: - vty_out (vty, "IAC "); + uty_out (vty, "IAC "); break; case WILL: - vty_out (vty, "WILL "); + uty_out (vty, "WILL "); break; case WONT: - vty_out (vty, "WONT "); + uty_out (vty, "WONT "); break; case DO: - vty_out (vty, "DO "); + uty_out (vty, "DO "); break; case DONT: - vty_out (vty, "DONT "); + uty_out (vty, "DONT "); break; case SB: - vty_out (vty, "SB "); + uty_out (vty, "SB "); break; case SE: - vty_out (vty, "SE "); + uty_out (vty, "SE "); break; case TELOPT_ECHO: - vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); + uty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); break; case TELOPT_SGA: - vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); + uty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); break; case TELOPT_NAWS: - vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); + uty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); break; default: - vty_out (vty, "%x ", buf[i]); + uty_out (vty, "%x ", buf[i]); break; } } - vty_out (vty, "%s", VTY_NEWLINE); + uty_out (vty, "%s", VTY_NEWLINE); #endif /* TELNET_OPTION_DEBUG */ @@ -1220,7 +1393,7 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) vty->iac_sb_in_progress = 1; return 0; break; - case SE: + case SE: { if (!vty->iac_sb_in_progress) return 0; @@ -1234,11 +1407,11 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) { case TELOPT_NAWS: if (vty->sb_len != TELNET_NAWS_SB_LEN) - zlog_warn("RFC 1073 violation detected: telnet NAWS option " + uzlog(NULL, LOG_WARNING, "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, " + uzlog(NULL, LOG_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 @@ -1246,7 +1419,7 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) 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: " + uty_out(vty, "TELNET NAWS window size negotiation completed: " "width %d, height %d%s", vty->width, vty->height, VTY_NEWLINE); #endif @@ -1303,6 +1476,7 @@ vty_execute (struct vty *vty) static void vty_escape_map (unsigned char c, struct vty *vty) { + assert(vty_lock_count); switch (c) { case ('A'): @@ -1329,22 +1503,51 @@ vty_escape_map (unsigned char c, struct vty *vty) static void vty_buffer_reset (struct vty *vty) { + assert(vty_lock_count); buffer_reset (vty->obuf); vty_prompt (vty); vty_redraw_line (vty); } -/* Read data via vty socket. */ +/* Callback: qpthreads., Read data via vty socket. */ +static void +vty_read_r (qps_file qf, void* file_info) +{ + int vty_sock = qf->fd; + struct vty *vty = (struct vty *)file_info; + + LOCK + + /* is this necessary? */ + qps_disable_modes(qf, qps_read_mbit); + uty_read(vty, vty_sock); + + UNLOCK +} + +/* Callback: threads. Read data via vty socket. */ static int vty_read (struct thread *thread) { - int i; - int nbytes; - unsigned char buf[VTY_READ_BUFSIZ]; - int vty_sock = THREAD_FD (thread); struct vty *vty = THREAD_ARG (thread); + int result ; + + LOCK + vty->t_read = NULL; + result = uty_read(vty, vty_sock); + + UNLOCK + return result; +} + +static int +uty_read (struct vty *vty, int vty_sock) +{ + int i; + int nbytes; + unsigned char buf[VTY_READ_BUFSIZ]; /* Read raw data from socket */ if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) @@ -1357,14 +1560,14 @@ vty_read (struct thread *thread) return 0; } vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("%s: read error on vty client fd %d, closing: %s", + uzlog(NULL, LOG_WARNING, "%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++) + for (i = 0; i < nbytes; i++) { if (buf[i] == IAC) { @@ -1378,7 +1581,7 @@ vty_read (struct thread *thread) vty->iac = 0; } } - + if (vty->iac_sb_in_progress && !vty->iac) { if (vty->sb_len < sizeof(vty->sb_buf)) @@ -1396,7 +1599,7 @@ vty_read (struct thread *thread) i += ret; continue; } - + if (vty->status == VTY_MORE) { @@ -1505,7 +1708,7 @@ vty_read (struct thread *thread) break; case '\n': case '\r': - vty_out (vty, "%s", VTY_NEWLINE); + uty_out (vty, "%s", VTY_NEWLINE); vty_execute (vty); break; case '\t': @@ -1535,32 +1738,66 @@ vty_read (struct thread *thread) /* Check status. */ if (vty->status == VTY_CLOSE) - vty_close (vty); + uty_close (vty); else { vty_event (VTY_WRITE, vty_sock, vty); vty_event (VTY_READ, vty_sock, vty); } + return 0; } -/* Flush buffer to the vty. */ +/* Callback: qpthreads. Flush buffer to the vty. */ +static void +vty_flush_r (qps_file qf, void* file_info) +{ + int vty_sock = qf->fd; + struct vty *vty = (struct vty *)file_info; + + LOCK + + qps_disable_modes(qf, qps_write_mbit); + + /* Temporary disable read thread. */ + if ((vty->lines == 0)) + { + qps_disable_modes(qf, qps_read_mbit); + } + + uty_flush(vty, vty_sock); + + UNLOCK +} + +/* Callback: threads. Flush buffer to the vty. */ static int vty_flush (struct thread *thread) { - int erase; - buffer_status_t flushrc; int vty_sock = THREAD_FD (thread); struct vty *vty = THREAD_ARG (thread); + int result; + LOCK vty->t_write = NULL; - /* Tempolary disable read thread. */ + /* Temporary disable read thread. */ if ((vty->lines == 0) && vty->t_read) { thread_cancel (vty->t_read); vty->t_read = NULL; } + result = uty_flush(vty, vty_sock); + + UNLOCK + return result; +} + +static int +uty_flush (struct vty *vty, int vty_sock) +{ + int erase; + buffer_status_t flushrc; /* Function execution continue. */ erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE)); @@ -1580,14 +1817,14 @@ vty_flush (struct thread *thread) { case BUFFER_ERROR: vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("buffer_flush failed on vty client fd %d, closing", + uzlog(NULL, LOG_WARNING, "buffer_flush failed on vty client fd %d, closing", vty->fd); buffer_reset(vty->obuf); - vty_close(vty); - return 0; + uty_close(vty); + break; case BUFFER_EMPTY: if (vty->status == VTY_CLOSE) - vty_close (vty); + uty_close (vty); else { vty->status = VTY_NORMAL; @@ -1612,9 +1849,10 @@ vty_create (int vty_sock, union sockunion *su) { struct vty *vty; + assert(vty_lock_count); + /* Allocate new vty structure and set up default values. */ - vty = vty_new (); - vty->fd = vty_sock; + vty = vty_new (vty_sock); vty->type = VTY_TERM; vty->address = sockunion_su2str (su); if (no_password_check) @@ -1651,17 +1889,17 @@ vty_create (int vty_sock, union sockunion *su) /* Vty is not available if password isn't set. */ if (host.password == NULL && host.password_encrypt == NULL) { - vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); + uty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); vty->status = VTY_CLOSE; - vty_close (vty); + uty_close (vty); return NULL; } } /* Say hello to the world. */ - vty_hello (vty); + uty_hello (vty); if (! no_password_check) - vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); /* Setting up terminal. */ vty_will_echo (vty); @@ -1680,22 +1918,45 @@ vty_create (int vty_sock, union sockunion *su) return vty; } -/* Accept connection from the network. */ +/* Callback: qpthreads. Accept connection from the network. */ +static void +vty_accept_r (qps_file qf, void* file_info) +{ + LOCK + + int accept_sock = qf->fd; + uty_accept(accept_sock); + + UNLOCK +} + +/* Callback: threads. Accept connection from the network. */ static int vty_accept (struct thread *thread) { + int result; + + LOCK + + int accept_sock = THREAD_FD (thread); + result = uty_accept(accept_sock); + + UNLOCK + return result; +} + +static int +uty_accept (int accept_sock) +{ 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 *bufp; - accept_sock = THREAD_FD (thread); - /* We continue hearing vty socket. */ vty_event (VTY_SERV, accept_sock, NULL); @@ -1705,7 +1966,7 @@ vty_accept (struct thread *thread) vty_sock = sockunion_accept (accept_sock, &su); if (vty_sock < 0) { - zlog_warn ("can't accept vty socket : %s", safe_strerror (errno)); + uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s", safe_strerror (errno)); return -1; } set_nonblocking(vty_sock); @@ -1719,16 +1980,15 @@ vty_accept (struct thread *thread) (access_list_apply (acl, p) == FILTER_DENY)) { char *buf; - zlog (NULL, LOG_INFO, "Vty connection refused from %s", + uzlog (NULL, LOG_INFO, "Vty connection refused from %s", (buf = sockunion_su2str (&su))); free (buf); close (vty_sock); - + /* continue accepting connections */ vty_event (VTY_SERV, accept_sock, NULL); - - prefix_free (p); + prefix_free (p); return 0; } } @@ -1741,28 +2001,27 @@ vty_accept (struct thread *thread) (access_list_apply (acl, p) == FILTER_DENY)) { char *buf; - zlog (NULL, LOG_INFO, "Vty connection refused from %s", + uzlog (NULL, LOG_INFO, "Vty connection refused from %s", (buf = sockunion_su2str (&su))); free (buf); close (vty_sock); - + /* continue accepting connections */ vty_event (VTY_SERV, accept_sock, NULL); - - prefix_free (p); + prefix_free (p); return 0; } } #endif /* HAVE_IPV6 */ - + prefix_free (p); on = 1; - ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, + ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof (on)); if (ret < 0) - zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", + uzlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", safe_strerror (errno)); zlog (NULL, LOG_INFO, "Vty connection from %s", @@ -1786,6 +2045,8 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port) int sock; char port_str[BUFSIZ]; + assert(vty_lock_count); + memset (&req, 0, sizeof (struct addrinfo)); req.ai_flags = AI_PASSIVE; req.ai_family = AF_UNSPEC; @@ -1827,7 +2088,7 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port) } ret = listen (sock, 3); - if (ret < 0) + if (ret < 0) { close (sock); /* Avoid sd leak. */ continue; @@ -1841,6 +2102,7 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port) } #endif /* HAVE_IPV6 && ! NRL */ +#if defined(HAVE_IPV6) && defined(NRL) || !defined(HAVE_IPV6) /* Make vty server socket. */ static void vty_serv_sock_family (const char* addr, unsigned short port, int family) @@ -1850,6 +2112,8 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family) int accept_sock; void* naddr=NULL; + assert(vty_lock_count); + memset (&su, 0, sizeof (union sockunion)); su.sa.sa_family = family; if(addr) @@ -1860,18 +2124,18 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family) #ifdef HAVE_IPV6 case AF_INET6: naddr=&su.sin6.sin6_addr; -#endif +#endif } if(naddr) switch(inet_pton(family,addr,naddr)) { case -1: - zlog_err("bad address %s",addr); + uzlog(NULL, LOG_ERR, "bad address %s",addr); naddr=NULL; break; case 0: - zlog_err("error translating address %s: %s",addr,safe_strerror(errno)); + uzlog(NULL, LOG_ERR, "error translating address %s: %s",addr,safe_strerror(errno)); naddr=NULL; } @@ -1888,14 +2152,14 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family) ret = sockunion_bind (accept_sock, &su, port, naddr); if (ret < 0) { - zlog_warn("can't bind socket"); + uzlog(NULL, LOG_WARNING, "can't bind socket"); close (accept_sock); /* Avoid sd leak. */ return; } /* Listen socket under queue 3. */ ret = listen (accept_sock, 3); - if (ret < 0) + if (ret < 0) { zlog (NULL, LOG_WARNING, "can't listen socket"); close (accept_sock); /* Avoid sd leak. */ @@ -1905,6 +2169,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family) /* Add vty server event. */ vty_event (VTY_SERV, accept_sock, NULL); } +#endif /* defined(HAVE_IPV6) && defined(NRL) || !defined(HAVE_IPV6) */ #ifdef VTYSH /* For sockaddr_un. */ @@ -1919,7 +2184,9 @@ vty_serv_un (const char *path) struct sockaddr_un serv; mode_t old_mask; struct zprivs_ids_t ids; - + + assert(vty_lock_count); + /* First of all, unlink existing socket */ unlink (path); @@ -1930,7 +2197,7 @@ vty_serv_un (const char *path) sock = socket (AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { - zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno)); + uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s", safe_strerror(errno)); return; } @@ -1947,7 +2214,7 @@ vty_serv_un (const char *path) ret = bind (sock, (struct sockaddr *) &serv, len); if (ret < 0) { - zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno)); + ulog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, safe_strerror(errno)); close (sock); /* Avoid sd leak. */ return; } @@ -1955,7 +2222,7 @@ vty_serv_un (const char *path) ret = listen (sock, 5); if (ret < 0) { - zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno)); + uzlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock, safe_strerror(errno)); close (sock); /* Avoid sd leak. */ return; } @@ -1963,13 +2230,13 @@ vty_serv_un (const char *path) umask (old_mask); zprivs_get_ids(&ids); - + if (ids.gid_vty > 0) { /* set group of socket */ if ( chown (path, -1, ids.gid_vty) ) { - zlog_err ("vty_serv_un: could chown socket, %s", + uzlog (NULL, LOG_ERR, "vty_serv_un: could chown socket, %s", safe_strerror (errno) ); } } @@ -1979,16 +2246,36 @@ vty_serv_un (const char *path) /* #define VTYSH_DEBUG 1 */ +/* Callback: qpthreads. Accept connection */ +void int +vtysh_accept_r (qps_file qf, void* file_info) +{ + int accept_sock = qf->fd; + LOCK + utysh_accept (accept_sock); + UNLOCK +} + +/* Callback: threads. Accept connection */ static int vtysh_accept (struct thread *thread) { - int accept_sock; + int accept_sock = THREAD_FD (thread); + LOCK + result = utysh_accept (accept_sock); + UNLOCK + return result; +} + +static int +utysh_accept (int accept_sock) +{ int sock; int client_len; struct sockaddr_un client; struct vty *vty; - - accept_sock = THREAD_FD (thread); + + assert(vty_lock_count); vty_event (VTYSH_SERV, accept_sock, NULL); @@ -2000,18 +2287,18 @@ vtysh_accept (struct thread *thread) if (sock < 0) { - zlog_warn ("can't accept vty socket : %s", safe_strerror (errno)); + uzlog (NULL, LOG_WARNING, "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," + uzlog (NULL, LOG_WARNING, "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 */ @@ -2029,6 +2316,8 @@ vtysh_accept (struct thread *thread) static int vtysh_flush(struct vty *vty) { + assert(vty_lock_count); + switch (buffer_flush_available(vty->obuf, vty->fd)) { case BUFFER_PENDING: @@ -2036,9 +2325,9 @@ vtysh_flush(struct vty *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); + uzlog(NULL, LOG_WARNING, "%s: write error to fd %d, closing", __func__, vty->fd); buffer_reset(vty->obuf); - vty_close(vty); + uty_close(vty); return -1; break; case BUFFER_EMPTY: @@ -2047,21 +2336,48 @@ vtysh_flush(struct vty *vty) return 0; } +/* Callback: qpthreads., Read data via vty socket. */ +static void +vtysh_read_r (qps_file qf, void* file_info) +{ + int vty_sock = qf->fd; + struct vty *vty = (struct vty *)file_info; + + LOCK + + /* is this necessary? */ + qps_disable_modes(qf, qps_read_mbit); + utysh_read(vty, vty_soc); + + UNLOCK +} + +/* Callback: threads. Read data via vty socket. */ static int vtysh_read (struct thread *thread) { + int vty_sock = THREAD_FD (thread); + struct vty *vty = THREAD_ARG (thread); + int result; + + LOCK + + vty->t_read = NULL; + result = uty_read(vty, vty_soc); + + UNLOCK + return result; +} + +static int +utysh_read (struct vty *vty, int sock) +{ int ret; - int sock; 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; - if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0) { if (nbytes < 0) @@ -2072,11 +2388,11 @@ vtysh_read (struct thread *thread) return 0; } vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("%s: read failed on vtysh client fd %d, closing: %s", + uzlog(NULL, LOG_WARNING, "%s: read failed on vtysh client fd %d, closing: %s", __func__, sock, safe_strerror(errno)); } buffer_reset(vty->obuf); - vty_close (vty); + uty_close (vty); #ifdef VTYSH_DEBUG printf ("close vtysh\n"); #endif /* VTYSH_DEBUG */ @@ -2118,13 +2434,32 @@ vtysh_read (struct thread *thread) return 0; } +/* Callback: qpthraeds. Write */ +static void +vtysh_write_r (qps_file qf, void* file_info) +{ + struct vty *vty = (struct vty *)file_info; + + LOCK + + qps_disable_modes(qf, qps_write_mbit); + vtysh_flush(vty); + + UNLOCK +} + +//* Callback: thraeds. Write */ static int vtysh_write (struct thread *thread) { struct vty *vty = THREAD_ARG (thread); + LOCK + vty->t_write = NULL; vtysh_flush(vty); + + UNLOCK return 0; } @@ -2134,6 +2469,8 @@ vtysh_write (struct thread *thread) void vty_serv_sock (const char *addr, unsigned short port, const char *path) { + LOCK + /* If port is set to 0, do not listen on TCP/IP at all! */ if (port) { @@ -2153,6 +2490,8 @@ vty_serv_sock (const char *addr, unsigned short port, const char *path) #ifdef VTYSH vty_serv_un (path); #endif /* VTYSH */ + + UNLOCK } /* Close vty interface. Warning: call this only from functions that @@ -2162,8 +2501,18 @@ vty_serv_sock (const char *addr, unsigned short port, const char *path) void vty_close (struct vty *vty) { + LOCK + uty_close(vty); + UNLOCK +} + +static void +uty_close (struct vty *vty) +{ int i; + assert(vty_lock_count); + /* Cancel threads.*/ if (vty->t_read) thread_cancel (vty->t_read); @@ -2171,6 +2520,17 @@ vty_close (struct vty *vty) thread_cancel (vty->t_write); if (vty->t_timeout) thread_cancel (vty->t_timeout); + if (vty->qf) + { + qps_remove_file(vty->qf); + qps_file_free(vty->qf); + vty->qf = NULL; + } + if (vty->qtr) + { + qtimer_free(vty->qtr); + vty->qtr = NULL; + } /* Flush buffer. */ buffer_flush_all (vty->obuf, vty->fd); @@ -2196,29 +2556,48 @@ vty_close (struct vty *vty) XFREE (MTYPE_VTY, vty->buf); /* Check configure. */ - vty_config_unlock (vty); + uty_config_unlock (vty); /* OK free vty. */ XFREE (MTYPE_VTY, vty); } -/* When time out occur output message then close connection. */ +/* Callback: qpthreads. When time out occur output message then close connection. */ +static void +vty_timeout_r (qtimer qtr, void* timer_info, qtime_t when) +{ + struct vty *vty = (struct vty *)timer_info; + LOCK + qtimer_unset(qtr); + uty_timeout(vty); + UNLOCK +} + +/* Callback: threads. When time out occur output message then close connection. */ static int vty_timeout (struct thread *thread) { - struct vty *vty; - - vty = THREAD_ARG (thread); + int result; + struct vty *vty = THREAD_ARG (thread); + LOCK vty->t_timeout = NULL; + result = uty_timeout(vty); + UNLOCK + return result; +} + +static int +uty_timeout (struct vty *vty) +{ vty->v_timeout = 0; /* Clear buffer*/ buffer_reset (vty->obuf); - vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE); + uty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE); /* Close connection. */ vty->status = VTY_CLOSE; - vty_close (vty); + uty_close (vty); return 0; } @@ -2230,15 +2609,16 @@ vty_read_file (FILE *confp) int ret; struct vty *vty; - vty = vty_new (); - vty->fd = 0; /* stdout */ + vty = vty_new (0); /* stdout */ vty->type = VTY_TERM; vty->node = CONFIG_NODE; - + /* Execute configuration file */ ret = config_from_file (vty, confp); - if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) + LOCK + + if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) { switch (ret) { @@ -2249,13 +2629,14 @@ vty_read_file (FILE *confp) fprintf (stderr, "There is no such command.\n"); break; } - fprintf (stderr, "Error occured during reading below line.\n%s\n", + fprintf (stderr, "Error occured during reading below line.\n%s\n", vty->buf); - vty_close (vty); + uty_close (vty); exit (1); } - vty_close (vty); + uty_close (vty); + UNLOCK } static FILE * @@ -2267,7 +2648,7 @@ vty_use_backup_config (char *fullpath) int tmp, sav; int c; char buffer[512]; - + fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1); strcpy (fullpath_sav, fullpath); strcat (fullpath_sav, CONF_BACKUP_EXT); @@ -2279,7 +2660,7 @@ vty_use_backup_config (char *fullpath) fullpath_tmp = malloc (strlen (fullpath) + 8); sprintf (fullpath_tmp, "%s.XXXXXX", fullpath); - + /* Open file to configuration write. */ tmp = mkstemp (fullpath_tmp); if (tmp < 0) @@ -2297,13 +2678,13 @@ vty_use_backup_config (char *fullpath) free (fullpath_tmp); return NULL; } - + while((c = read (sav, buffer, 512)) > 0) write (tmp, buffer, c); - + close (sav); close (tmp); - + if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0) { unlink (fullpath_tmp); @@ -2311,12 +2692,12 @@ vty_use_backup_config (char *fullpath) free (fullpath_tmp); return NULL; } - + if (link (fullpath_tmp, fullpath) == 0) ret = fopen (fullpath, "r"); unlink (fullpath_tmp); - + free (fullpath_sav); free (fullpath_tmp); return ret; @@ -2338,7 +2719,7 @@ vty_read_config (char *config_file, if (! IS_DIRECTORY_SEP (config_file[0])) { getcwd (cwd, MAXPATHLEN); - tmp = XMALLOC (MTYPE_TMP, + tmp = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (config_file) + 2); sprintf (tmp, "%s/%s", cwd, config_file); fullpath = tmp; @@ -2352,13 +2733,13 @@ vty_read_config (char *config_file, { 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", + fprintf (stderr, "can't open configuration file [%s]\n", config_file); exit(1); } @@ -2388,7 +2769,7 @@ vty_read_config (char *config_file, { ret = stat (integrate_default, &conf_stat); if (ret >= 0) - return; + return; } #endif /* VTYSH */ @@ -2397,7 +2778,7 @@ vty_read_config (char *config_file, { 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) { @@ -2410,7 +2791,7 @@ vty_read_config (char *config_file, config_default_dir); exit (1); } - } + } else fullpath = config_default_dir; } @@ -2420,7 +2801,7 @@ vty_read_config (char *config_file, fclose (confp); host_config_set (fullpath); - + if (tmp) XFREE (MTYPE_TMP, fullpath); } @@ -2432,19 +2813,20 @@ vty_log (const char *level, const char *proto_str, { unsigned int i; struct vty *vty; - + + assert(vty_lock_count); + if (!vtyvec) return; - for (i = 0; i < vector_active (vtyvec); i++) - if ((vty = vector_slot (vtyvec, i)) != NULL) - if (vty->monitor) - { - va_list ac; - va_copy(ac, va); - vty_log_out (vty, level, proto_str, format, ctl, ac); - va_end(ac); - } + for (i = 0; i < vector_active (vtyvec); i++) + if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor) + { + 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. */ @@ -2457,7 +2839,7 @@ vty_log_fixed (const char *buf, size_t len) /* vty may not have been initialised */ if (!vtyvec) return; - + iov[0].iov_base = (void *)buf; iov[0].iov_len = len; iov[1].iov_base = (void *)"\r\n"; @@ -2476,17 +2858,32 @@ vty_log_fixed (const char *buf, size_t len) int vty_config_lock (struct vty *vty) { + int result; + LOCK if (vty_config == 0) { vty->config = 1; vty_config = 1; } - return vty->config; + result = vty->config; + UNLOCK + return result; } int vty_config_unlock (struct vty *vty) { + int result; + LOCK + result = uty_config_unlock(vty); + UNLOCK + return result; +} + +static int +uty_config_unlock (struct vty *vty) +{ + assert(vty_lock_count); if (vty_config == 1 && vty->config == 1) { vty->config = 0; @@ -2494,15 +2891,24 @@ vty_config_unlock (struct vty *vty) } return vty->config; } - -/* Master of the threads. */ -static struct thread_master *master; static void vty_event (enum event event, int sock, struct vty *vty) { + if (master_nexus) + vty_event_r(event, sock, vty); + else + vty_event_t(event, sock, vty); +} + +/* thread event setter */ +static void +vty_event_t (enum event event, int sock, struct vty *vty) + { struct thread *vty_serv_thread; + assert(vty_lock_count); + switch (event) { case VTY_SERV: @@ -2528,14 +2934,14 @@ vty_event (enum event event, int sock, struct vty *vty) { if (vty->t_timeout) thread_cancel (vty->t_timeout); - vty->t_timeout = + vty->t_timeout = thread_add_timer (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); - break; + break; case VTY_TIMEOUT_RESET: if (vty->t_timeout) { @@ -2544,13 +2950,77 @@ vty_event (enum event event, int sock, struct vty *vty) } if (vty->v_timeout) { - vty->t_timeout = + vty->t_timeout = thread_add_timer (master, vty_timeout, vty, vty->v_timeout); } break; } } - + +/* qpthreads event setter */ +static void +vty_event_r (enum event event, int sock, struct vty *vty) + { + + qps_file accept_file = NULL; + + assert(vty_lock_count); + + switch (event) + { + case VTY_SERV: + accept_file = vector_get_item(Vvty_serv_thread, sock); + if (accept_file == NULL) + { + accept_file = qps_file_init_new(accept_file, NULL); + qps_add_file(master_nexus->selection, accept_file, sock, NULL); + vector_set_index(Vvty_serv_thread, sock, accept_file); + } + qps_enable_mode(accept_file, qps_read_mnum, vty_accept_r) ; + break; +#ifdef VTYSH + case VTYSH_SERV: + accept_file = vector_get_item(Vvty_serv_thread, sock); + if (accept_file == NULL) + { + accept_file = qps_file_init_new(accept_file, NULL); + qps_add_file(master, accept_file, sock, NULL); + vector_set_index(Vvty_serv_thread, sock, accept_file); + } + qps_enable_mode(accept_file, qps_read_mnum, vtysh_accept_r) ; + break; + case VTYSH_READ: + qps_enable_mode(vty->file, qps_read_mnum, vtysh_read_r) ; + break; + case VTYSH_WRITE: + qps_enable_mode(vty->file, qps_write_mnum, vtysh_write_r) ; + break; +#endif /* VTYSH */ + case VTY_READ: + qps_enable_mode(vty->qf, qps_read_mnum, vty_read_r) ; + + /* Time out treatment. */ + if (vty->v_timeout) + { + qtimer_set(vty->qtr, qt_add_monotonic(QTIME(vty->v_timeout)), NULL) ; + } + break; + case VTY_WRITE: + qps_enable_mode(vty->qf, qps_write_mnum, vty_flush_r) ; + break; + case VTY_TIMEOUT_RESET: + if (vty->v_timeout) + { + qtimer_set(vty->qtr, qt_add_monotonic(QTIME(vty->v_timeout)), NULL) ; + } + else + { + qtimer_unset(vty->qtr); + } + break; + } +} + DEFUN (config_who, config_who_cmd, "who", @@ -2584,6 +3054,8 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str) { unsigned long timeout = 0; + LOCK + /* min_str and sec_str are already checked by parser. So it must be all digit string. */ if (min_str) @@ -2598,7 +3070,7 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str) vty->v_timeout = timeout; vty_event (VTY_TIMEOUT_RESET, 0, vty); - + UNLOCK return CMD_SUCCESS; } @@ -2839,14 +3311,14 @@ vty_config_write (struct vty *vty) /* exec-timeout */ if (vty_timeout_val != VTY_TIMEOUT_DEFAULT) - vty_out (vty, " exec-timeout %ld %ld%s", + vty_out (vty, " exec-timeout %ld %ld%s", vty_timeout_val / 60, vty_timeout_val % 60, VTY_NEWLINE); /* login */ if (no_password_check) vty_out (vty, " no login%s", VTY_NEWLINE); - + if (restricted_mode != restricted_mode_default) { if (restricted_mode_default) @@ -2854,7 +3326,7 @@ vty_config_write (struct vty *vty) else vty_out (vty, " anonymous restricted%s", VTY_NEWLINE); } - + vty_out (vty, "!%s", VTY_NEWLINE); return CMD_SUCCESS; @@ -2874,22 +3346,40 @@ vty_reset () unsigned int i; struct vty *vty; struct thread *vty_serv_thread; + qps_file qf; + + LOCK for (i = 0; i < vector_active (vtyvec); i++) if ((vty = vector_slot (vtyvec, i)) != NULL) { buffer_reset (vty->obuf); vty->status = VTY_CLOSE; - vty_close (vty); + uty_close (vty); } - 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); - vector_slot (Vvty_serv_thread, i) = NULL; - close (i); - } + if (master_nexus) + { + for (i = 0; i < vector_active (Vvty_serv_thread); i++) + if ((qf = vector_slot (Vvty_serv_thread, i)) != NULL) + { + qps_remove_file(qf); + qps_file_free(qf); + vector_slot (Vvty_serv_thread, i) = NULL; + close (i); + } + } + else + { + assert(master); + 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); + vector_slot (Vvty_serv_thread, i) = NULL; + close (i); + } + } vty_timeout_val = VTY_TIMEOUT_DEFAULT; @@ -2904,6 +3394,7 @@ vty_reset () XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); vty_ipv6_accesslist_name = NULL; } + UNLOCK } static void @@ -2933,34 +3424,69 @@ vty_get_cwd () int vty_shell (struct vty *vty) { - return vty->type == VTY_SHELL ? 1 : 0; + LOCK + int result; + result = uty_shell (vty); + UNLOCK + return result; +} + +static int +uty_shell (struct vty *vty) +{ + return ((vty == NULL) || (vty->type == VTY_SHELL)) ? 1 : 0; } int vty_shell_serv (struct vty *vty) { - return vty->type == VTY_SHELL_SERV ? 1 : 0; + LOCK + int result; + result = ((vty->type == VTY_SHELL_SERV) ? 1 : 0); + UNLOCK + return result; } void vty_init_vtysh () { - vtyvec = vector_init (VECTOR_MIN_SIZE); + LOCK + vtyvec = vector_init (0); + UNLOCK +} + +/* qpthreads: Install vty's own commands like `who' command. */ +void +vty_init_r (void) +{ + master_nexus = qpn_init_new(master_nexus); + vty_mutex = qpt_mutex_init(vty_mutex, qpt_mutex_quagga); + vty_init(NULL); } -/* Install vty's own commands like `who' command. */ +/* create and execute our thread */ +void +vty_exec_r(void) +{ + if (master_nexus) + qpn_exec(master_nexus); +} + +/* threads: Install vty's own commands like `who' command. */ void vty_init (struct thread_master *master_thread) { + LOCK + /* For further configuration read, preserve current directory. */ vty_save_cwd (); - vtyvec = vector_init (VECTOR_MIN_SIZE); + vtyvec = vector_init (0); master = master_thread; /* Initilize server thread vector. */ - Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE); + Vvty_serv_thread = vector_init (0); /* Install bgp top node. */ install_node (&vty_node, vty_config_write); @@ -2993,11 +3519,18 @@ vty_init (struct thread_master *master_thread) install_element (VTY_NODE, &vty_ipv6_access_class_cmd); install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd); #endif /* HAVE_IPV6 */ + + UNLOCK } void vty_terminate (void) { + LOCK + + if (master_nexus) + master_nexus->terminate = 1; + if (vty_cwd) XFREE (MTYPE_TMP, vty_cwd); @@ -3007,4 +3540,11 @@ vty_terminate (void) vector_free (vtyvec); vector_free (Vvty_serv_thread); } + UNLOCK + + if (vty_mutex) + vty_mutex = qpt_mutex_destroy(vty_mutex, 1); } + +#undef LOCK +#undef UNLOCK |