#include "libfrr.h"
#include "frrstr.h"
#include "lib_errors.h"
+#include "northbound_cli.h"
#include <arpa/telnet.h>
#include <termios.h>
/* 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;
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:
break;
}
+ vty->xpath_index = 0;
+
vty_prompt(vty);
vty->cp = 0;
}
case VTY_NODE:
case BFD_NODE:
case BFD_PEER_NODE:
- vty_config_unlock(vty);
+ vty_config_exit(vty);
vty->node = ENABLE_NODE;
break;
default:
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;
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;
#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
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);
}
/* 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();
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);
break;
}
- nl = strchr(vty->error_buf, '\n');
- if (nl)
- *nl = '\0';
- flog_err(EC_LIB_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);
}
/* 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;
if (getcwd(cwd, MAXPATHLEN) == NULL) {
flog_err_sys(
EC_LIB_SYSTEM_CALL,
- "Failure to determine Current Working Directory %d!",
- errno);
- exit(1);
+ "%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);
EC_LIB_BACKUP_CONFIG,
"WARNING: using backup configuration file!");
else {
- flog_err(EC_LIB_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 {
fullpath = config_default_dir;
}
- vty_read_file(confp);
+ vty_read_file(config, confp);
read_success = true;
fclose(confp);
}
}
-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. */