]> git.proxmox.com Git - mirror_frr.git/blobdiff - vtysh/vtysh.c
*: conform with COMMUNITY.md formatting rules, via 'make indent'
[mirror_frr.git] / vtysh / vtysh.c
index 3f86f3c9294d3475b46669b2a0b09ae391c5040c..f0010700a866b16b6bbebad4b60ebfd103f6c266 100644 (file)
@@ -46,6 +46,9 @@
 
 DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CMD, "Vtysh cmd copy")
 
+/* Destination for vtysh output */
+FILE *outputfile;
+
 /* Struct VTY. */
 struct vty *vty;
 
@@ -74,6 +77,7 @@ struct vtysh_client vtysh_client[] = {
        {.fd = -1, .name = "nhrpd", .flag = VTYSH_NHRPD, .next = NULL},
        {.fd = -1, .name = "eigrpd", .flag = VTYSH_EIGRPD, .next = NULL},
        {.fd = -1, .name = "babeld", .flag = VTYSH_BABELD, .next = NULL},
+       {.fd = -1, .name = "sharpd", .flag = VTYSH_SHARPD, .next = NULL},
        {.fd = -1, .name = "watchfrr", .flag = VTYSH_WATCHFRR, .next = NULL},
 };
 
@@ -137,16 +141,25 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line,
 
                bufvalid += nread;
 
-               end = memmem(buf, bufvalid - buf, terminator,
-                            sizeof(terminator));
-               if (end + sizeof(terminator) + 1 > bufvalid)
+               if (bufvalid - buf >= 4)
+                       end = memmem(bufvalid - 4, 4, terminator,
+                                    sizeof(terminator));
+
+               if (end && end + sizeof(terminator) + 1 > bufvalid)
                        /* found \0\0\0 but return code hasn't been read yet */
                        end = NULL;
                if (end)
                        ret = end[sizeof(terminator)];
 
-               while (bufvalid > buf && (end > buf || !end)) {
-                       size_t textlen = (end ? end : bufvalid) - buf;
+               /*
+                * calculate # bytes we have, up to & not including the
+                * terminator if present
+                */
+               size_t textlen = (end ? end : bufvalid) - buf;
+
+               /* feed line processing callback if present */
+               while (callback && bufvalid > buf && (end > buf || !end)) {
+                       textlen = (end ? end : bufvalid) - buf;
                        char *eol = memchr(buf, '\n', textlen);
                        if (eol)
                                /* line break */
@@ -164,8 +177,7 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line,
                                /* continue reading */
                                break;
 
-                       /* eol is at a line end now, either \n => \0 or \0\0\0
-                        */
+                       /* eol is at line end now, either \n => \0 or \0\0\0 */
                        assert(eol && eol <= bufvalid);
 
                        if (fp) {
@@ -185,6 +197,14 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line,
                                end -= eol - buf;
                }
 
+               /* else if no callback, dump raw */
+               if (!callback) {
+                       if (fp)
+                               fwrite(buf, 1, textlen, fp);
+                       memmove(buf, buf + textlen, bufvalid - buf - textlen);
+                       bufvalid -= textlen;
+               }
+
                if (bufvalid == buf + bufsz) {
                        char *new;
                        bufsz *= 2;
@@ -226,7 +246,8 @@ static int vtysh_client_run_all(struct vtysh_client *head_client,
                        wrong_instance++;
                        continue;
                }
-               correct_instance++;
+               if (client->fd > 0)
+                       correct_instance++;
                if (rc != CMD_SUCCESS) {
                        if (!continue_on_err)
                                return rc;
@@ -373,21 +394,21 @@ static int vtysh_execute_func(const char *line, int pager)
                fprintf(stdout, "%% Command incomplete.\n");
                break;
        case CMD_SUCCESS_DAEMON: {
-               /* FIXME: Don't open pager for exit commands. popen() causes
-                * problems
-                * if exited from vtysh at all. This hack shouldn't cause any
-                * problem
-                * but is really ugly. */
-               if (pager && vtysh_pager_name
+               /*
+                * FIXME: Don't open pager for exit commands. popen() causes
+                * problems if exited from vtysh at all. This hack shouldn't
+                * cause any problem but is really ugly.
+                */
+               fp = outputfile;
+               if (pager && vtysh_pager_name && outputfile == stdout
                    && (strncmp(line, "exit", 4) != 0)) {
                        fp = popen(vtysh_pager_name, "w");
                        if (fp == NULL) {
                                perror("popen failed for pager");
-                               fp = stdout;
+                               fp = outputfile;
                        } else
                                closepager = 1;
-               } else
-                       fp = stdout;
+               }
 
                if (!strcmp(cmd->string, "configure terminal")) {
                        for (i = 0; i < array_size(vtysh_client); i++) {
@@ -403,7 +424,7 @@ static int vtysh_execute_func(const char *line, int pager)
 
                                if (vline == NULL) {
                                        if (pager && vtysh_pager_name && fp
-                                           && closepager) {
+                                           && fp != outputfile && closepager) {
                                                if (pclose(fp) == -1) {
                                                        perror("pclose failed for pager");
                                                }
@@ -423,13 +444,22 @@ static int vtysh_execute_func(const char *line, int pager)
                }
 
                cmd_stat = CMD_SUCCESS;
+               struct vtysh_client *vc;
                for (i = 0; i < array_size(vtysh_client); i++) {
                        if (cmd->daemon & vtysh_client[i].flag) {
                                if (vtysh_client[i].fd < 0
                                    && (cmd->daemon == vtysh_client[i].flag)) {
-                                       fprintf(stderr, "%s is not running\n",
-                                               vtysh_client[i].name);
-                                       continue;
+                                       bool any_inst = false;
+                                       for (vc = &vtysh_client[i]; vc;
+                                            vc = vc->next)
+                                               any_inst = any_inst
+                                                          || (vc->fd > 0);
+                                       if (!any_inst) {
+                                               fprintf(stderr,
+                                                       "%s is not running\n",
+                                                       vtysh_client[i].name);
+                                               continue;
+                                       }
                                }
                                cmd_stat = vtysh_client_execute(
                                        &vtysh_client[i], line, fp);
@@ -444,7 +474,7 @@ static int vtysh_execute_func(const char *line, int pager)
                        (*cmd->func)(cmd, vty, 0, NULL);
        }
        }
-       if (pager && vtysh_pager_name && fp && closepager) {
+       if (pager && vtysh_pager_name && fp && closepager && fp != outputfile) {
                if (pclose(fp) == -1) {
                        perror("pclose failed for pager");
                }
@@ -474,11 +504,11 @@ static char *trim(char *s)
                return s;
 
        end = s + size - 1;
-       while (end >= s && isspace(*end))
+       while (end >= s && isspace((int)*end))
                end--;
        *(end + 1) = '\0';
 
-       while (*s && isspace(*s))
+       while (*s && isspace((int)*s))
                s++;
 
        return s;
@@ -509,7 +539,7 @@ int vtysh_mark_file(const char *filename)
        }
 
        vty = vty_new();
-       vty->fd = 0; /* stdout */
+       vty->wfd = STDERR_FILENO;
        vty->type = VTY_TERM;
        vty->node = CONFIG_NODE;
 
@@ -526,19 +556,19 @@ int vtysh_mark_file(const char *filename)
                switch (vty->node) {
                case LDP_IPV4_IFACE_NODE:
                        if (strncmp(vty_buf_copy, "   ", 3)) {
-                               fprintf(stdout, "  end\n");
+                               fprintf(outputfile, "  end\n");
                                vty->node = LDP_IPV4_NODE;
                        }
                        break;
                case LDP_IPV6_IFACE_NODE:
                        if (strncmp(vty_buf_copy, "   ", 3)) {
-                               fprintf(stdout, "  end\n");
+                               fprintf(outputfile, "  end\n");
                                vty->node = LDP_IPV6_NODE;
                        }
                        break;
                case LDP_PSEUDOWIRE_NODE:
                        if (strncmp(vty_buf_copy, "  ", 2)) {
-                               fprintf(stdout, " end\n");
+                               fprintf(outputfile, " end\n");
                                vty->node = LDP_L2VPN_NODE;
                        }
                        break;
@@ -547,7 +577,7 @@ int vtysh_mark_file(const char *filename)
                }
 
                if (vty_buf_trimmed[0] == '!' || vty_buf_trimmed[0] == '#') {
-                       fprintf(stdout, "%s", vty->buf);
+                       fprintf(outputfile, "%s", vty->buf);
                        continue;
                }
 
@@ -555,7 +585,7 @@ int vtysh_mark_file(const char *filename)
                vline = cmd_make_strvec(vty->buf);
 
                if (vline == NULL) {
-                       fprintf(stdout, "%s", vty->buf);
+                       fprintf(outputfile, "%s", vty->buf);
                        continue;
                }
 
@@ -563,6 +593,7 @@ int vtysh_mark_file(const char *filename)
                 * appropriate */
                if (strlen(vty_buf_trimmed) == 3
                    && strncmp("end", vty_buf_trimmed, 3) == 0) {
+                       cmd_free_strvec(vline);
                        continue;
                }
 
@@ -597,15 +628,15 @@ int vtysh_mark_file(const char *filename)
                             || prev_node == BGP_IPV6M_NODE
                             || prev_node == BGP_EVPN_NODE)
                            && (tried == 1)) {
-                               fprintf(stdout, "exit-address-family\n");
+                               fprintf(outputfile, "exit-address-family\n");
                        } else if ((prev_node == BGP_EVPN_VNI_NODE)
                                   && (tried == 1)) {
-                               fprintf(stdout, "exit-vni\n");
+                               fprintf(outputfile, "exit-vni\n");
                        } else if ((prev_node == KEYCHAIN_KEY_NODE)
                                   && (tried == 1)) {
-                               fprintf(stdout, "exit\n");
+                               fprintf(outputfile, "exit\n");
                        } else if (tried) {
-                               fprintf(stdout, "end\n");
+                               fprintf(outputfile, "end\n");
                        }
                }
                /* If command didn't succeed in any node, continue with return
@@ -655,12 +686,12 @@ int vtysh_mark_file(const char *filename)
                        u_int i;
                        int cmd_stat = CMD_SUCCESS;
 
-                       fprintf(stdout, "%s", vty->buf);
+                       fprintf(outputfile, "%s", vty->buf);
                        for (i = 0; i < array_size(vtysh_client); i++) {
                                if (cmd->daemon & vtysh_client[i].flag) {
                                        cmd_stat = vtysh_client_execute(
                                                &vtysh_client[i], vty->buf,
-                                               stdout);
+                                               outputfile);
                                        if (cmd_stat != CMD_SUCCESS)
                                                break;
                                }
@@ -674,7 +705,7 @@ int vtysh_mark_file(const char *filename)
                }
        }
        /* This is the end */
-       fprintf(stdout, "\nend\n");
+       fprintf(outputfile, "\nend\n");
        vty_close(vty);
        XFREE(MTYPE_VTYSH_CMD, vty_buf_copy);
 
@@ -717,9 +748,8 @@ int vtysh_config_from_file(struct vty *vty, FILE *fp)
                case CMD_ERR_NO_MATCH:
                        fprintf(stderr, "line %d: %% Unknown command[%d]: %s",
                                lineno, vty->node, vty->buf);
-                       retcode =
-                               CMD_ERR_NO_MATCH; /* once we have an error, we
-                                                    remember & return that */
+                       retcode = CMD_ERR_NO_MATCH; /* once we have an error, we
+                                                      remember & return that */
                        break;
                case CMD_ERR_INCOMPLETE:
                        fprintf(stderr,
@@ -737,7 +767,7 @@ int vtysh_config_from_file(struct vty *vty, FILE *fp)
                                if (cmd->daemon & vtysh_client[i].flag) {
                                        cmd_stat = vtysh_client_execute(
                                                &vtysh_client[i], vty->buf,
-                                               stdout);
+                                               outputfile);
                                        /*
                                         * CMD_WARNING - Can mean that the
                                         * command was
@@ -794,20 +824,23 @@ static int vtysh_rl_describe(void)
        } else if (rl_end && isspace((int)rl_line_buffer[rl_end - 1]))
                vector_set(vline, NULL);
 
-       describe = cmd_describe_command(vline, vty, &ret);
-
        fprintf(stdout, "\n");
 
+       describe = cmd_describe_command(vline, vty, &ret);
+
        /* Ambiguous and no match error. */
        switch (ret) {
        case CMD_ERR_AMBIGUOUS:
                cmd_free_strvec(vline);
+               vector_free(describe);
                fprintf(stdout, "%% Ambiguous command.\n");
                rl_on_new_line();
                return 0;
                break;
        case CMD_ERR_NO_MATCH:
                cmd_free_strvec(vline);
+               if (describe)
+                       vector_free(describe);
                fprintf(stdout, "%% There is no matched command.\n");
                rl_on_new_line();
                return 0;
@@ -941,8 +974,8 @@ static struct cmd_node pw_node = {
        PW_NODE, "%s(config-pw)# ",
 };
 
-static struct cmd_node ns_node = {
-       NS_NODE, "%s(config-logical-router)# ",
+static struct cmd_node logicalrouter_node = {
+       LOGICALROUTER_NODE, "%s(config-logical-router)# ",
 };
 
 static struct cmd_node vrf_node = {
@@ -1031,6 +1064,10 @@ struct cmd_node link_params_node = {
        LINK_PARAMS_NODE, "%s(config-link-params)# ",
 };
 
+#if defined(HAVE_RPKI)
+static struct cmd_node rpki_node = {RPKI_NODE, "%s(config-rpki)# ", 1};
+#endif
+
 /* Defined in lib/vty.c */
 extern struct cmd_node vty_node;
 
@@ -1167,6 +1204,28 @@ DEFUNSH(VTYSH_BGPD, address_family_ipv6_labeled_unicast,
        return CMD_SUCCESS;
 }
 
+#if defined(HAVE_RPKI)
+DEFUNSH(VTYSH_BGPD, rpki, rpki_cmd, "rpki",
+       "Enable rpki and enter rpki configuration mode\n")
+{
+       vty->node = RPKI_NODE;
+       return CMD_SUCCESS;
+}
+
+DEFUNSH(VTYSH_BGPD, rpki_exit, rpki_exit_cmd, "exit",
+       "Exit current mode and down to previous mode\n")
+{
+       vty->node = CONFIG_NODE;
+       return CMD_SUCCESS;
+}
+
+DEFUNSH(VTYSH_BGPD, rpki_quit, rpki_quit_cmd, "quit",
+       "Exit current mode and down to previous mode\n")
+{
+       return rpki_exit(self, vty, argc, argv);
+}
+#endif
+
 DEFUNSH(VTYSH_BGPD, address_family_evpn, address_family_evpn_cmd,
        "address-family <l2vpn evpn>",
        "Enter Address Family command mode\n"
@@ -1263,10 +1322,11 @@ DEFUNSH(VTYSH_RIPNGD, router_ripng, router_ripng_cmd, "router ripng",
        return CMD_SUCCESS;
 }
 
-DEFUNSH(VTYSH_OSPFD, router_ospf, router_ospf_cmd, "router ospf [(1-65535)]",
+DEFUNSH(VTYSH_OSPFD, router_ospf, router_ospf_cmd,
+       "router ospf [(1-65535)] [vrf NAME]",
        "Enable a routing process\n"
        "Start OSPF configuration\n"
-       "Instance ID\n")
+       "Instance ID\n" VRF_CMD_HELP_STR)
 {
        vty->node = OSPF_NODE;
        return CMD_SUCCESS;
@@ -1375,7 +1435,7 @@ DEFUNSH(VTYSH_LDPD, ldp_member_pseudowire_ifname,
 DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, "router isis WORD",
        ROUTER_STR
        "ISO IS-IS\n"
-       "ISO Routing area tag")
+       "ISO Routing area tag\n")
 {
        vty->node = ISIS_NODE;
        return CMD_SUCCESS;
@@ -1437,7 +1497,7 @@ static int vtysh_exit(struct vty *vty)
                break;
        case INTERFACE_NODE:
        case PW_NODE:
-       case NS_NODE:
+       case LOGICALROUTER_NODE:
        case VRF_NODE:
        case ZEBRA_NODE:
        case BGP_NODE:
@@ -1711,16 +1771,24 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_interface_vrf_cmd, "no interface IFNAME vrf NAME",
       "Delete a pseudo interface's configuration\n"
       "Interface's name\n" VRF_CMD_HELP_STR)
 
-DEFUNSH(VTYSH_NS, vtysh_ns, vtysh_ns_cmd, "logical-router (1-65535) ns NAME",
+DEFUNSH(VTYSH_ZEBRA, vtysh_logicalrouter, vtysh_logicalrouter_cmd,
+       "logical-router (1-65535) ns NAME",
        "Enable a logical-router\n"
        "Specify the logical-router indentifier\n"
        "The Name Space\n"
        "The file name in " NS_RUN_DIR ", or a full pathname\n")
 {
-       vty->node = NS_NODE;
+       vty->node = LOGICALROUTER_NODE;
        return CMD_SUCCESS;
 }
 
+DEFSH(VTYSH_ZEBRA, vtysh_no_logicalrouter_cmd,
+      "no logical-router (1-65535) ns NAME", NO_STR
+      "Enable a Logical-Router\n"
+      "Specify the Logical-Router identifier\n"
+      "The Name Space\n"
+      "The file name in " NS_RUN_DIR ", or a full pathname\n")
+
 DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME",
        "Select a VRF to configure\n"
        "VRF's name\n")
@@ -1733,16 +1801,16 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_vrf_cmd, "no vrf NAME", NO_STR
       "Delete a pseudo vrf's configuration\n"
       "VRF's name\n")
 
-DEFUNSH(VTYSH_NS, vtysh_exit_ns, vtysh_exit_ns_cmd, "exit",
-       "Exit current mode and down to previous mode\n")
+DEFUNSH(VTYSH_NS, vtysh_exit_logicalrouter, vtysh_exit_logicalrouter_cmd,
+       "exit", "Exit current mode and down to previous mode\n")
 {
        return vtysh_exit(vty);
 }
 
-DEFUNSH(VTYSH_NS, vtysh_quit_ns, vtysh_quit_ns_cmd, "quit",
-       "Exit current mode and down to previous mode\n")
+DEFUNSH(VTYSH_NS, vtysh_quit_logicalrouter, vtysh_quit_logicalrouter_cmd,
+       "quit", "Exit current mode and down to previous mode\n")
 {
-       return vtysh_exit_ns(self, vty, argc, argv);
+       return vtysh_exit_logicalrouter(self, vty, argc, argv);
 }
 
 DEFUNSH(VTYSH_VRF, vtysh_exit_vrf, vtysh_exit_vrf_cmd, "exit",
@@ -1802,7 +1870,7 @@ DEFUN (vtysh_show_thread,
                        fprintf(stdout, "Thread statistics for %s:\n",
                                vtysh_client[i].name);
                        ret = vtysh_client_execute(&vtysh_client[i], line,
-                                                  stdout);
+                                                  outputfile);
                        fprintf(stdout, "\n");
                }
        return ret;
@@ -1823,7 +1891,7 @@ DEFUN (vtysh_show_work_queues,
                        fprintf(stdout, "Work queue statistics for %s:\n",
                                vtysh_client[i].name);
                        ret = vtysh_client_execute(&vtysh_client[i], line,
-                                                  stdout);
+                                                  outputfile);
                        fprintf(stdout, "\n");
                }
 
@@ -1853,7 +1921,7 @@ DEFUN (vtysh_show_work_queues_daemon,
        }
 
        ret = vtysh_client_execute(&vtysh_client[i], "show work-queues\n",
-                                  stdout);
+                                  outputfile);
 
        return ret;
 }
@@ -1880,9 +1948,9 @@ static int show_per_daemon(const char *line, const char *headline)
 
        for (i = 0; i < array_size(vtysh_client); i++)
                if (vtysh_client[i].fd >= 0) {
-                       fprintf(stdout, headline, vtysh_client[i].name);
+                       fprintf(outputfile, headline, vtysh_client[i].name);
                        ret = vtysh_client_execute(&vtysh_client[i], line,
-                                                  stdout);
+                                                  outputfile);
                        fprintf(stdout, "\n");
                }
 
@@ -1895,8 +1963,7 @@ DEFUN (vtysh_show_debugging,
        SHOW_STR
        DEBUG_STR)
 {
-       return show_per_daemon("do show debugging\n",
-                              "");
+       return show_per_daemon("do show debugging\n", "");
 }
 
 DEFUN (vtysh_show_debugging_hashtable,
@@ -1929,8 +1996,7 @@ DEFUN (vtysh_show_memory,
        SHOW_STR
        "Memory statistics\n")
 {
-       return show_per_daemon("show memory\n",
-                              "Memory statistics for %s:\n");
+       return show_per_daemon("show memory\n", "Memory statistics for %s:\n");
 }
 
 DEFUN (vtysh_show_modules,
@@ -2174,20 +2240,19 @@ DEFUN (vtysh_write_terminal,
 {
        u_int i;
        char line[] = "do write terminal\n";
-       FILE *fp = NULL;
+       FILE *fp = outputfile;
 
-       if (vtysh_pager_name) {
+       if (fp == stdout && vtysh_pager_name) {
                fp = popen(vtysh_pager_name, "w");
                if (fp == NULL) {
                        perror("popen");
                        exit(1);
                }
-       } else
-               fp = stdout;
+       }
 
-       vty_out(vty, "Building configuration...\n");
-       vty_out(vty, "\nCurrent configuration:\n");
-       vty_out(vty, "!\n");
+       fprintf(outputfile, "Building configuration...\n");
+       fprintf(outputfile, "\nCurrent configuration:\n");
+       fprintf(outputfile, "!\n");
 
        for (i = 0; i < array_size(vtysh_client); i++)
                if ((argc < 3)
@@ -2199,7 +2264,7 @@ DEFUN (vtysh_write_terminal,
 
        vtysh_config_dump(fp);
 
-       if (vtysh_pager_name && fp) {
+       if (vtysh_pager_name && fp && fp != outputfile) {
                fflush(fp);
                if (pclose(fp) == -1) {
                        perror("pclose");
@@ -2208,7 +2273,7 @@ DEFUN (vtysh_write_terminal,
                fp = NULL;
        }
 
-       vty_out(vty, "end\n");
+       fprintf(outputfile, "end\n");
        return CMD_SUCCESS;
 }
 
@@ -2377,7 +2442,7 @@ DEFUN (vtysh_write_memory,
        char line[] = "do write memory\n";
        u_int i;
 
-       fprintf(stdout,
+       fprintf(outputfile,
                "Note: this version of vtysh never writes vtysh.conf\n");
 
        /* If integrated frr.conf explicitely set. */
@@ -2389,7 +2454,7 @@ DEFUN (vtysh_write_memory,
                if (i < array_size(vtysh_client) && vtysh_client[i].fd != -1)
                        ret = vtysh_client_execute(&vtysh_client[i],
                                                   "do write integrated",
-                                                  stdout);
+                                                  outputfile);
 
                if (ret != CMD_SUCCESS) {
                        printf("\nWarning: attempting direct configuration write without "
@@ -2400,10 +2465,10 @@ DEFUN (vtysh_write_memory,
                return ret;
        }
 
-       fprintf(stdout, "Building Configuration...\n");
+       fprintf(outputfile, "Building Configuration...\n");
 
        for (i = 0; i < array_size(vtysh_client); i++)
-               ret = vtysh_client_execute(&vtysh_client[i], line, stdout);
+               ret = vtysh_client_execute(&vtysh_client[i], line, outputfile);
 
        return ret;
 }
@@ -2432,7 +2497,7 @@ DEFUN (vtysh_terminal_length,
 
        lines = strtol(argv[idx_number]->arg, &endptr, 10);
        if (lines < 0 || lines > 512 || *endptr != '\0') {
-               vty_out(vty, "length is malformed\n");
+               fprintf(outputfile, "length is malformed\n");
                return CMD_WARNING;
        }
 
@@ -2475,15 +2540,15 @@ DEFUN (vtysh_show_daemons,
 
        for (i = 0; i < array_size(vtysh_client); i++)
                if (vtysh_client[i].fd >= 0)
-                       vty_out(vty, " %s", vtysh_client[i].name);
-       vty_out(vty, "\n");
+                       fprintf(outputfile, " %s", vtysh_client[i].name);
+       fprintf(outputfile, "\n");
 
        return CMD_SUCCESS;
 }
 
 /* Execute command in child process. */
-static void execute_command(const char *command, int argc,
-                           const char *arg1, const char *arg2)
+static void execute_command(const char *command, int argc, const char *arg1,
+                           const char *arg2)
 {
        pid_t pid;
        int status;
@@ -2558,6 +2623,19 @@ ALIAS(vtysh_traceroute, vtysh_traceroute_ip_cmd, "traceroute ip WORD",
       "IP trace\n"
       "Trace route to destination address or hostname\n")
 
+DEFUN (vtysh_mtrace,
+       vtysh_mtrace_cmd,
+       "mtrace WORD",
+       "Multicast trace route to multicast source\n"
+       "Multicast trace route to multicast source address\n")
+{
+       int idx = 1;
+
+       argv_find(argv, argc, "WORD", &idx);
+       execute_command("mtracebis", 1, argv[idx]->arg, NULL);
+       return CMD_SUCCESS;
+}
+
 DEFUN (vtysh_ping6,
        vtysh_ping6_cmd,
        "ping ipv6 WORD",
@@ -2651,6 +2729,38 @@ DEFUN (config_list,
        return cmd_list_cmds(vty, argc == 2);
 }
 
+DEFUN (vtysh_output_file,
+       vtysh_output_file_cmd,
+       "output file FILE",
+       "Direct vtysh output to file\n"
+       "Direct vtysh output to file\n"
+       "Path to dump output to\n")
+{
+       const char *path = argv[argc - 1]->arg;
+       outputfile = fopen(path, "a");
+       if (!outputfile) {
+               fprintf(stdout, "Failed to open file '%s': %s\n", path,
+                       safe_strerror(errno));
+               outputfile = stdout;
+       }
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_vtysh_output_file,
+       no_vtysh_output_file_cmd,
+       "no output file [FILE]",
+       NO_STR
+       "Direct vtysh output to file\n"
+       "Direct vtysh output to file\n"
+       "Path to dump output to\n")
+{
+       if (outputfile != stdout) {
+               fclose(outputfile);
+               outputfile = stdout;
+       }
+       return CMD_SUCCESS;
+}
+
 DEFUN(find,
       find_cmd,
       "find COMMAND...",
@@ -2684,6 +2794,8 @@ static void vtysh_install_default(enum node_type node)
 {
        install_element(node, &config_list_cmd);
        install_element(node, &find_cmd);
+       install_element(node, &vtysh_output_file_cmd);
+       install_element(node, &no_vtysh_output_file_cmd);
 }
 
 /* Making connection to protocol daemon. */
@@ -2912,6 +3024,12 @@ static const struct cmd_variable_handler vtysh_var_handler[] = {
         .completions = vtysh_autocomplete},
        {.completions = NULL}};
 
+void vtysh_uninit()
+{
+       if (outputfile != stdout)
+               fclose(outputfile);
+}
+
 void vtysh_init_vty(void)
 {
        /* Make vty structure. */
@@ -2919,6 +3037,9 @@ void vtysh_init_vty(void)
        vty->type = VTY_SHELL;
        vty->node = VIEW_NODE;
 
+       /* set default output */
+       outputfile = stdout;
+
        /* Initialize commands. */
        cmd_init(0);
        cmd_variable_handler_register(vtysh_var_handler);
@@ -2929,7 +3050,7 @@ void vtysh_init_vty(void)
        install_node(&interface_node, NULL);
        install_node(&pw_node, NULL);
        install_node(&link_params_node, NULL);
-       install_node(&ns_node, NULL);
+       install_node(&logicalrouter_node, NULL);
        install_node(&vrf_node, NULL);
        install_node(&rmap_node, NULL);
        install_node(&zebra_node, NULL);
@@ -2963,6 +3084,9 @@ void vtysh_init_vty(void)
        install_node(&keychain_key_node, NULL);
        install_node(&isis_node, NULL);
        install_node(&vty_node, NULL);
+#if defined(HAVE_RPKI)
+       install_node(&rpki_node, NULL);
+#endif
 
        struct cmd_node *node;
        for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
@@ -3104,11 +3228,12 @@ void vtysh_init_vty(void)
        install_element(PW_NODE, &vtysh_exit_interface_cmd);
        install_element(PW_NODE, &vtysh_quit_interface_cmd);
 
-       install_element(NS_NODE, &vtysh_end_all_cmd);
+       install_element(LOGICALROUTER_NODE, &vtysh_end_all_cmd);
 
-       install_element(CONFIG_NODE, &vtysh_ns_cmd);
-       install_element(NS_NODE, &vtysh_exit_ns_cmd);
-       install_element(NS_NODE, &vtysh_quit_ns_cmd);
+       install_element(CONFIG_NODE, &vtysh_logicalrouter_cmd);
+       install_element(CONFIG_NODE, &vtysh_no_logicalrouter_cmd);
+       install_element(LOGICALROUTER_NODE, &vtysh_exit_logicalrouter_cmd);
+       install_element(LOGICALROUTER_NODE, &vtysh_quit_logicalrouter_cmd);
 
        install_element(VRF_NODE, &vtysh_end_all_cmd);
        install_element(VRF_NODE, &vtysh_exit_vrf_cmd);
@@ -3161,6 +3286,13 @@ void vtysh_init_vty(void)
        install_element(BGP_EVPN_NODE, &exit_address_family_cmd);
        install_element(BGP_IPV6L_NODE, &exit_address_family_cmd);
 
+#if defined(HAVE_RPKI)
+       install_element(CONFIG_NODE, &rpki_cmd);
+       install_element(RPKI_NODE, &rpki_exit_cmd);
+       install_element(RPKI_NODE, &rpki_quit_cmd);
+       install_element(RPKI_NODE, &vtysh_end_all_cmd);
+#endif
+
        /* EVPN commands */
        install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd);
        install_element(BGP_EVPN_VNI_NODE, &exit_vni_cmd);
@@ -3204,6 +3336,7 @@ void vtysh_init_vty(void)
        install_element(VIEW_NODE, &vtysh_ping_ip_cmd);
        install_element(VIEW_NODE, &vtysh_traceroute_cmd);
        install_element(VIEW_NODE, &vtysh_traceroute_ip_cmd);
+       install_element(VIEW_NODE, &vtysh_mtrace_cmd);
        install_element(VIEW_NODE, &vtysh_ping6_cmd);
        install_element(VIEW_NODE, &vtysh_traceroute6_cmd);
 #if defined(HAVE_SHELL_ACCESS)