]> git.proxmox.com Git - mirror_frr.git/blobdiff - lib/vty.c
zebra, lib: fix the ZEBRA_INTERFACE_VRF_UPDATE zapi message
[mirror_frr.git] / lib / vty.c
index 748c14f675c35d5159d025fe128360e048ecfc22..085cbac742a004082a1594870d828994c671e815 100644 (file)
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -41,6 +41,7 @@
 #include "libfrr.h"
 #include "frrstr.h"
 #include "lib_errors.h"
+#include "northbound_cli.h"
 
 #include <arpa/telnet.h>
 #include <termios.h>
@@ -85,9 +86,8 @@ static vector Vvty_serv_thread;
 /* Current directory. */
 char *vty_cwd = NULL;
 
-/* Configure lock. */
-static int vty_config;
-static int vty_config_is_lockless = 0;
+/* Exclusive configuration lock. */
+struct vty *vty_exclusive_lock;
 
 /* Login password check. */
 static int no_password_check = 0;
@@ -314,8 +314,9 @@ static int vty_log_out(struct vty *vty, const char *level,
                /* 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));
+               flog_err(EC_LIB_SOCKET,
+                        "%s: write failed to vty client fd %d, closing: %s",
+                        __func__, vty->fd, safe_strerror(errno));
                buffer_reset(vty->obuf);
                buffer_reset(vty->lbuf);
                /* cannot call vty_close, because a parent routine may still try
@@ -534,7 +535,8 @@ static int vty_command(struct vty *vty, char *buf)
                if ((realtime = thread_consumed_time(&after, &before, &cputime))
                    > CONSUMED_TIME_CHECK)
                        /* Warn about CPU hog that must be fixed. */
-                       zlog_warn(
+                       flog_warn(
+                               EC_LIB_SLOW_THREAD,
                                "SLOW COMMAND: command took %lums (cpu time %lums): %s",
                                realtime / 1000, cputime / 1000, buf);
        }
@@ -811,13 +813,14 @@ static void vty_end_config(struct vty *vty)
        case LDP_L2VPN_NODE:
        case LDP_PSEUDOWIRE_NODE:
        case ISIS_NODE:
+       case OPENFABRIC_NODE:
        case KEYCHAIN_NODE:
        case KEYCHAIN_KEY_NODE:
        case VTY_NODE:
        case BGP_EVPN_VNI_NODE:
        case BFD_NODE:
        case BFD_PEER_NODE:
-               vty_config_unlock(vty);
+               vty_config_exit(vty);
                vty->node = ENABLE_NODE;
                break;
        default:
@@ -825,6 +828,8 @@ static void vty_end_config(struct vty *vty)
                break;
        }
 
+       vty->xpath_index = 0;
+
        vty_prompt(vty);
        vty->cp = 0;
 }
@@ -1210,12 +1215,13 @@ static void vty_stop_input(struct vty *vty)
        case LDP_L2VPN_NODE:
        case LDP_PSEUDOWIRE_NODE:
        case ISIS_NODE:
+       case OPENFABRIC_NODE:
        case KEYCHAIN_NODE:
        case KEYCHAIN_KEY_NODE:
        case VTY_NODE:
        case BFD_NODE:
        case BFD_PEER_NODE:
-               vty_config_unlock(vty);
+               vty_config_exit(vty);
                vty->node = ENABLE_NODE;
                break;
        default:
@@ -1324,14 +1330,15 @@ static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes)
                switch (vty->sb_buf[0]) {
                case TELOPT_NAWS:
                        if (vty->sb_len != TELNET_NAWS_SB_LEN)
-                               zlog_warn(
+                               flog_err(
+                                       EC_LIB_SYSTEM_CALL,
                                        "RFC 1073 violation detected: telnet NAWS option "
                                        "should send %d characters, but we received %lu",
                                        TELNET_NAWS_SB_LEN,
                                        (unsigned long)vty->sb_len);
                        else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
                                flog_err(
-                                       LIB_ERR_DEVELOPMENT,
+                                       EC_LIB_DEVELOPMENT,
                                        "Bug detected: sizeof(vty->sb_buf) %lu < %d, too small to handle the telnet NAWS option",
                                        (unsigned long)sizeof(vty->sb_buf),
                                        TELNET_NAWS_SB_LEN);
@@ -1446,7 +1453,8 @@ static int vty_read(struct thread *thread)
                        }
                        vty->monitor = 0; /* disable monitoring to avoid
                                             infinite recursion */
-                       zlog_warn(
+                       flog_err(
+                               EC_LIB_SOCKET,
                                "%s: read error on vty client fd %d, closing: %s",
                                __func__, vty->fd, safe_strerror(errno));
                        buffer_reset(vty->obuf);
@@ -1653,7 +1661,7 @@ static int 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",
+               zlog_info("buffer_flush failed on vty client fd %d, closing",
                          vty->fd);
                buffer_reset(vty->lbuf);
                buffer_reset(vty->obuf);
@@ -1689,7 +1697,6 @@ struct vty *vty_new()
        new->lbuf = buffer_new(0);
        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;
 
        return new;
@@ -1713,6 +1720,10 @@ static struct vty *vty_new_init(int vty_sock)
        memset(vty->hist, 0, sizeof(vty->hist));
        vty->hp = 0;
        vty->hindex = 0;
+       vty->xpath_index = 0;
+       memset(vty->xpath, 0, sizeof(vty->xpath));
+       vty->private_config = false;
+       vty->candidate_config = vty_shared_candidate_config;
        vector_set_index(vtyvec, vty_sock, vty);
        vty->status = VTY_NORMAL;
        vty->lines = -1;
@@ -1900,7 +1911,8 @@ static int vty_accept(struct thread *thread)
        /* We can handle IPv4 or IPv6 socket. */
        vty_sock = sockunion_accept(accept_sock, &su);
        if (vty_sock < 0) {
-               zlog_warn("can't accept vty socket : %s", safe_strerror(errno));
+               flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s",
+                        safe_strerror(errno));
                return -1;
        }
        set_nonblocking(vty_sock);
@@ -1973,7 +1985,7 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port)
        ret = getaddrinfo(hostname, port_str, &req, &ainfo);
 
        if (ret != 0) {
-               flog_err_sys(LIB_ERR_SYSTEM_CALL, "getaddrinfo failed: %s",
+               flog_err_sys(EC_LIB_SYSTEM_CALL, "getaddrinfo failed: %s",
                             gai_strerror(ret));
                exit(1);
        }
@@ -2034,7 +2046,7 @@ static void vty_serv_un(const char *path)
        /* Make UNIX domain socket. */
        sock = socket(AF_UNIX, SOCK_STREAM, 0);
        if (sock < 0) {
-               flog_err_sys(LIB_ERR_SOCKET,
+               flog_err_sys(EC_LIB_SOCKET,
                             "Cannot create unix stream socket: %s",
                             safe_strerror(errno));
                return;
@@ -2054,7 +2066,7 @@ static void vty_serv_un(const char *path)
 
        ret = bind(sock, (struct sockaddr *)&serv, len);
        if (ret < 0) {
-               flog_err_sys(LIB_ERR_SOCKET, "Cannot bind path %s: %s", path,
+               flog_err_sys(EC_LIB_SOCKET, "Cannot bind path %s: %s", path,
                             safe_strerror(errno));
                close(sock); /* Avoid sd leak. */
                return;
@@ -2062,7 +2074,7 @@ static void vty_serv_un(const char *path)
 
        ret = listen(sock, 5);
        if (ret < 0) {
-               flog_err_sys(LIB_ERR_SOCKET, "listen(fd %d) failed: %s", sock,
+               flog_err_sys(EC_LIB_SOCKET, "listen(fd %d) failed: %s", sock,
                             safe_strerror(errno));
                close(sock); /* Avoid sd leak. */
                return;
@@ -2078,7 +2090,7 @@ static void vty_serv_un(const char *path)
        if ((int)ids.gid_vty > 0) {
                /* set group of socket */
                if (chown(path, -1, ids.gid_vty)) {
-                       flog_err_sys(LIB_ERR_SYSTEM_CALL,
+                       flog_err_sys(EC_LIB_SYSTEM_CALL,
                                     "vty_serv_un: could chown socket, %s",
                                     safe_strerror(errno));
                }
@@ -2108,14 +2120,15 @@ static int vtysh_accept(struct thread *thread)
                      (socklen_t *)&client_len);
 
        if (sock < 0) {
-               zlog_warn("can't accept vty socket : %s", safe_strerror(errno));
+               flog_err(EC_LIB_SOCKET, "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",
+               flog_err(
+                       EC_LIB_SOCKET,
+                       "vtysh_accept: could not set vty socket %d to non-blocking, %s, closing",
                        sock, safe_strerror(errno));
                close(sock);
                return -1;
@@ -2146,8 +2159,8 @@ static int vtysh_flush(struct vty *vty)
        case BUFFER_ERROR:
                vty->monitor =
                        0; /* disable monitoring to avoid infinite recursion */
-               zlog_warn("%s: write error to fd %d, closing", __func__,
-                         vty->fd);
+               flog_err(EC_LIB_SOCKET, "%s: write error to fd %d, closing",
+                        __func__, vty->fd);
                buffer_reset(vty->lbuf);
                buffer_reset(vty->obuf);
                vty_close(vty);
@@ -2181,7 +2194,8 @@ static int vtysh_read(struct thread *thread)
                        }
                        vty->monitor = 0; /* disable monitoring to avoid
                                             infinite recursion */
-                       zlog_warn(
+                       flog_err(
+                               EC_LIB_SOCKET,
                                "%s: read failed on vtysh client fd %d, closing: %s",
                                __func__, sock, safe_strerror(errno));
                }
@@ -2269,6 +2283,13 @@ void vty_serv_sock(const char *addr, unsigned short port, const char *path)
 #endif /* VTYSH */
 }
 
+static void vty_error_delete(void *arg)
+{
+       struct vty_error *ve = arg;
+
+       XFREE(MTYPE_TMP, ve);
+}
+
 /* 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
@@ -2320,11 +2341,13 @@ void vty_close(struct vty *vty)
        if (vty->buf)
                XFREE(MTYPE_VTY, vty->buf);
 
-       if (vty->error_buf)
-               XFREE(MTYPE_VTY, vty->error_buf);
+       if (vty->error) {
+               vty->error->del = vty_error_delete;
+               list_delete(&vty->error);
+       }
 
        /* Check configure. */
-       vty_config_unlock(vty);
+       vty_config_exit(vty);
 
        /* OK free vty. */
        XFREE(MTYPE_VTY, vty);
@@ -2355,10 +2378,12 @@ static int vty_timeout(struct thread *thread)
 }
 
 /* Read up configuration file from file_name. */
-static void vty_read_file(FILE *confp)
+static void vty_read_file(struct nb_config *config, FILE *confp)
 {
        int ret;
        struct vty *vty;
+       struct vty_error *ve;
+       struct listnode *node;
        unsigned int line_num = 0;
 
        vty = vty_new();
@@ -2372,6 +2397,12 @@ static void vty_read_file(FILE *confp)
        vty->wfd = STDERR_FILENO;
        vty->type = VTY_FILE;
        vty->node = CONFIG_NODE;
+       if (config)
+               vty->candidate_config = config;
+       else {
+               vty->private_config = true;
+               vty->candidate_config = nb_config_new(NULL);
+       }
 
        /* Execute configuration file */
        ret = config_from_file(vty, confp, &line_num);
@@ -2408,12 +2439,27 @@ static void vty_read_file(FILE *confp)
                        break;
                }
 
-               nl = strchr(vty->error_buf, '\n');
-               if (nl)
-                       *nl = '\0';
-               flog_err(LIB_ERR_VTY,
-                         "ERROR: %s on config line %u: %s", message, line_num,
-                         vty->error_buf);
+               for (ALL_LIST_ELEMENTS_RO(vty->error, node, ve)) {
+                       nl = strchr(ve->error_buf, '\n');
+                       if (nl)
+                               *nl = '\0';
+                       flog_err(EC_LIB_VTY, "ERROR: %s on config line %u: %s",
+                                message, ve->line_num, ve->error_buf);
+               }
+       }
+
+       /*
+        * Automatically commit the candidate configuration after
+        * reading the configuration file.
+        */
+       if (config == NULL && vty->candidate_config
+           && frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) {
+               ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI,
+                                         true, "Read configuration file",
+                                         NULL);
+               if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
+                       zlog_err("%s: failed to read configuration file.",
+                                __func__);
        }
 
        vty_close(vty);
@@ -2474,7 +2520,8 @@ static FILE *vty_use_backup_config(const char *fullpath)
 }
 
 /* Read up configuration file from file_name. */
-bool vty_read_config(const char *config_file, char *config_default_dir)
+bool vty_read_config(struct nb_config *config, const char *config_file,
+                    char *config_default_dir)
 {
        char cwd[MAXPATHLEN];
        FILE *confp = NULL;
@@ -2487,10 +2534,10 @@ bool vty_read_config(const char *config_file, char *config_default_dir)
                if (!IS_DIRECTORY_SEP(config_file[0])) {
                        if (getcwd(cwd, MAXPATHLEN) == NULL) {
                                flog_err_sys(
-                                       LIB_ERR_SYSTEM_CALL,
-                                       "Failure to determine Current Working Directory %d!",
-                                       errno);
-                               exit(1);
+                                       EC_LIB_SYSTEM_CALL,
+                                       "%s: failure to determine Current Working Directory %d!",
+                                       __func__, errno);
+                               goto tmp_free_and_out;
                        }
                        tmp = XMALLOC(MTYPE_TMP,
                                      strlen(cwd) + strlen(config_file) + 2);
@@ -2502,18 +2549,22 @@ bool vty_read_config(const char *config_file, char *config_default_dir)
                confp = fopen(fullpath, "r");
 
                if (confp == NULL) {
-                       zlog_warn("%s: failed to open configuration file %s: %s, checking backup",
-                                __func__, fullpath, safe_strerror(errno));
+                       flog_warn(
+                               EC_LIB_BACKUP_CONFIG,
+                               "%s: failed to open configuration file %s: %s, checking backup",
+                               __func__, fullpath, safe_strerror(errno));
 
                        confp = vty_use_backup_config(fullpath);
                        if (confp)
-                               zlog_warn(
+                               flog_warn(
+                                       EC_LIB_BACKUP_CONFIG,
                                        "WARNING: using backup configuration file!");
                        else {
-                               flog_err(LIB_ERR_VTY,
-                                         "can't open configuration file [%s]",
-                                         config_file);
-                               exit(1);
+                               flog_err(
+                                       EC_LIB_VTY,
+                                       "%s: can't open configuration file [%s]",
+                                       __func__, config_file);
+                               goto tmp_free_and_out;
                        }
                }
        } else {
@@ -2548,26 +2599,29 @@ bool vty_read_config(const char *config_file, char *config_default_dir)
 #endif /* VTYSH */
                confp = fopen(config_default_dir, "r");
                if (confp == NULL) {
-                       zlog_warn("%s: failed to open configuration file %s: %s, checking backup",
-                                 __func__, config_default_dir,
-                                 safe_strerror(errno));
+                       flog_err(
+                               EC_LIB_SYSTEM_CALL,
+                               "%s: failed to open configuration file %s: %s, checking backup",
+                               __func__, config_default_dir,
+                               safe_strerror(errno));
 
                        confp = vty_use_backup_config(config_default_dir);
                        if (confp) {
-                               zlog_warn(
+                               flog_warn(
+                                       EC_LIB_BACKUP_CONFIG,
                                        "WARNING: using backup configuration file!");
                                fullpath = config_default_dir;
                        } else {
-                               flog_err(LIB_ERR_VTY,
-                                         "can't open configuration file [%s]",
-                                         config_default_dir);
+                               flog_err(EC_LIB_VTY,
+                                        "can't open configuration file [%s]",
+                                        config_default_dir);
                                goto tmp_free_and_out;
                        }
                } else
                        fullpath = config_default_dir;
        }
 
-       vty_read_file(confp);
+       vty_read_file(config, confp);
        read_success = true;
 
        fclose(confp);
@@ -2632,31 +2686,68 @@ void vty_log_fixed(char *buf, size_t len)
        }
 }
 
-int vty_config_lock(struct vty *vty)
+int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
 {
-       if (vty_config_is_lockless)
-               return 1;
-       if (vty_config == 0) {
-               vty->config = 1;
-               vty_config = 1;
+       if (exclusive && !vty_config_exclusive_lock(vty)) {
+               vty_out(vty, "VTY configuration is locked by other VTY\n");
+               return CMD_WARNING;
        }
-       return vty->config;
+
+       vty->node = CONFIG_NODE;
+       vty->config = true;
+       vty->private_config = private_config;
+
+       if (private_config) {
+               vty->candidate_config = nb_config_dup(running_config);
+               vty->candidate_config_base = nb_config_dup(running_config);
+               vty_out(vty,
+                       "Warning: uncommitted changes will be discarded on exit.\n\n");
+       } else {
+               vty->candidate_config = vty_shared_candidate_config;
+               if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
+                       vty->candidate_config_base =
+                               nb_config_dup(running_config);
+       }
+
+       return CMD_SUCCESS;
 }
 
-int vty_config_unlock(struct vty *vty)
+void vty_config_exit(struct vty *vty)
 {
-       if (vty_config_is_lockless)
-               return 0;
-       if (vty_config == 1 && vty->config == 1) {
-               vty->config = 0;
-               vty_config = 0;
+       /* Check if there's a pending confirmed commit. */
+       if (vty->t_confirmed_commit_timeout) {
+               vty_out(vty,
+                       "WARNING: exiting with a pending confirmed commit. Rolling back to previous configuration.\n\n");
+               nb_cli_confirmed_commit_rollback(vty);
+               nb_cli_confirmed_commit_clean(vty);
        }
-       return vty->config;
+
+       vty_config_exclusive_unlock(vty);
+
+       if (vty->candidate_config) {
+               if (vty->private_config)
+                       nb_config_free(vty->candidate_config);
+               vty->candidate_config = NULL;
+       }
+       if (vty->candidate_config_base) {
+               nb_config_free(vty->candidate_config_base);
+               vty->candidate_config_base = NULL;
+       }
+}
+
+int vty_config_exclusive_lock(struct vty *vty)
+{
+       if (vty_exclusive_lock == NULL) {
+               vty_exclusive_lock = vty;
+               return 1;
+       }
+       return 0;
 }
 
-void vty_config_lockless(void)
+void vty_config_exclusive_unlock(struct vty *vty)
 {
-       vty_config_is_lockless = 1;
+       if (vty_exclusive_lock == vty)
+               vty_exclusive_lock = NULL;
 }
 
 /* Master of the threads. */
@@ -3073,13 +3164,13 @@ static void vty_save_cwd(void)
                 * Hence not worrying about it too much.
                 */
                if (!chdir(SYSCONFDIR)) {
-                       flog_err_sys(LIB_ERR_SYSTEM_CALL,
+                       flog_err_sys(EC_LIB_SYSTEM_CALL,
                                     "Failure to chdir to %s, errno: %d",
                                     SYSCONFDIR, errno);
                        exit(-1);
                }
                if (getcwd(cwd, MAXPATHLEN) == NULL) {
-                       flog_err_sys(LIB_ERR_SYSTEM_CALL,
+                       flog_err_sys(EC_LIB_SYSTEM_CALL,
                                     "Failure to getcwd, errno: %d", errno);
                        exit(-1);
                }