summaryrefslogtreecommitdiffstats
path: root/lib/vty.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/vty.c376
1 files changed, 311 insertions, 65 deletions
diff --git a/lib/vty.c b/lib/vty.c
index ed8b71e..1c9cff4 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -6,6 +6,8 @@
#include <zebra.h>
+#include <fcntl.h>
+#include <sys/stat.h>
#include <lib/version.h>
#include <sys/types.h>
#include <sys/types.h>
@@ -44,6 +46,8 @@
#include <arpa/telnet.h>
#include <termios.h>
+#include "lib/config_paths.h"
+
#include "lib/vty_clippy.c"
DEFINE_MTYPE_STATIC(LIB, VTY, "VTY");
@@ -122,6 +126,13 @@ bool vty_log_commands;
static bool vty_log_commands_perm;
char const *const mgmt_daemons[] = {
+ "zebra",
+#ifdef HAVE_RIPD
+ "ripd",
+#endif
+#ifdef HAVE_RIPNGD
+ "ripngd",
+#endif
#ifdef HAVE_STATICD
"staticd",
#endif
@@ -157,10 +168,9 @@ static int vty_mgmt_unlock_running_inline(struct vty *vty)
return vty->mgmt_locked_running_ds ? -1 : 0;
}
-void vty_mgmt_resume_response(struct vty *vty, bool success)
+void vty_mgmt_resume_response(struct vty *vty, int ret)
{
uint8_t header[4] = {0, 0, 0, 0};
- int ret = CMD_SUCCESS;
if (!vty->mgmt_req_pending_cmd) {
zlog_err(
@@ -168,14 +178,10 @@ void vty_mgmt_resume_response(struct vty *vty, bool success)
return;
}
- if (!success)
- ret = CMD_WARNING_CONFIG_FAILED;
-
- MGMTD_FE_CLIENT_DBG(
- "resuming CLI cmd after %s on vty session-id: %" PRIu64
- " with '%s'",
- vty->mgmt_req_pending_cmd, vty->mgmt_session_id,
- success ? "succeeded" : "failed");
+ debug_fe_client("resuming CLI cmd after %s on vty session-id: %" PRIu64
+ " with '%s'",
+ vty->mgmt_req_pending_cmd, vty->mgmt_session_id,
+ ret == CMD_SUCCESS ? "success" : "failed");
vty->mgmt_req_pending_cmd = NULL;
@@ -383,11 +389,14 @@ int vty_json_no_pretty(struct vty *vty, struct json_object *json)
return vty_json_helper(vty, json, JSON_C_TO_STRING_NOSLASHESCAPE);
}
-void vty_json_empty(struct vty *vty)
+void vty_json_empty(struct vty *vty, struct json_object *json)
{
- json_object *json = json_object_new_object();
+ json_object *jsonobj = json;
+
+ if (!json)
+ jsonobj = json_object_new_object();
- vty_json(vty, json);
+ vty_json(vty, jsonobj);
}
/* Output current time to the vty. */
@@ -1565,7 +1574,7 @@ static void vty_read(struct event *thread)
break;
case '\r':
vty->escape = VTY_CR;
- /* fallthru */
+ fallthrough;
case '\n':
vty_out(vty, "\n");
buffer_flush_available(vty->obuf, vty->wfd);
@@ -2256,19 +2265,6 @@ bool mgmt_vty_read_configs(void)
snprintf(path, sizeof(path), "%s/mgmtd.conf", frr_sysconfdir);
confp = vty_open_config(path, config_default);
- if (!confp) {
- char *orig;
-
- snprintf(path, sizeof(path), "%s/zebra.conf", frr_sysconfdir);
- orig = XSTRDUP(MTYPE_TMP, host_config_get());
-
- zlog_info("mgmtd: trying backup config file: %s", path);
- confp = vty_open_config(path, config_default);
-
- host_config_set(path);
- XFREE(MTYPE_TMP, orig);
- }
-
if (confp) {
zlog_info("mgmtd: reading config file: %s", path);
@@ -2406,10 +2402,9 @@ static void vtysh_read(struct event *thread)
* we get response through callback.
*/
if (vty->mgmt_req_pending_cmd) {
- MGMTD_FE_CLIENT_DBG(
- "postpone CLI response pending mgmtd %s on vty session-id %" PRIu64,
- vty->mgmt_req_pending_cmd,
- vty->mgmt_session_id);
+ debug_fe_client("postpone CLI response pending mgmtd %s on vty session-id %" PRIu64,
+ vty->mgmt_req_pending_cmd,
+ vty->mgmt_session_id);
return;
}
@@ -2490,14 +2485,14 @@ void vty_close(struct vty *vty)
* so warn the user.
*/
if (vty->mgmt_num_pending_setcfg)
- MGMTD_FE_CLIENT_ERR(
+ log_err_fe_client(
"vty closed, uncommitted config will be lost.");
/* Drop out of configure / transaction if needed. */
vty_config_exit(vty);
if (mgmt_fe_client && vty->mgmt_session_id) {
- MGMTD_FE_CLIENT_DBG("closing vty session");
+ debug_fe_client("closing vty session");
mgmt_fe_destroy_client_session(mgmt_fe_client,
vty->mgmt_client_id);
vty->mgmt_session_id = 0;
@@ -3477,9 +3472,8 @@ void vty_init_vtysh(void)
static void vty_mgmt_server_connected(struct mgmt_fe_client *client,
uintptr_t usr_data, bool connected)
{
- MGMTD_FE_CLIENT_DBG("Got %sconnected %s MGMTD Frontend Server",
- !connected ? "dis: " : "",
- !connected ? "from" : "to");
+ debug_fe_client("Got %sconnected %s MGMTD Frontend Server",
+ !connected ? "dis: " : "", !connected ? "from" : "to");
/*
* We should not have any sessions for connecting or disconnecting case.
@@ -3515,8 +3509,8 @@ static void vty_mgmt_session_notify(struct mgmt_fe_client *client,
return;
}
- MGMTD_FE_CLIENT_DBG("%s session for client %" PRIu64 " successfully",
- create ? "Created" : "Destroyed", client_id);
+ debug_fe_client("%s session for client %" PRIu64 " successfully",
+ create ? "Created" : "Destroyed", client_id);
if (create) {
assert(session_id != 0);
@@ -3547,8 +3541,8 @@ static void vty_mgmt_ds_lock_notified(struct mgmt_fe_client *client,
zlog_err("%socking for DS %u failed, Err: '%s' vty %p",
lock_ds ? "L" : "Unl", ds_id, errmsg_if_any, vty);
else {
- MGMTD_FE_CLIENT_DBG("%socked DS %u successfully",
- lock_ds ? "L" : "Unl", ds_id);
+ debug_fe_client("%socked DS %u successfully",
+ lock_ds ? "L" : "Unl", ds_id);
if (ds_id == MGMTD_DS_CANDIDATE)
vty->mgmt_locked_candidate_ds = lock_ds;
else
@@ -3557,7 +3551,8 @@ static void vty_mgmt_ds_lock_notified(struct mgmt_fe_client *client,
if (!is_short_circuit && vty->mgmt_req_pending_cmd) {
assert(!strcmp(vty->mgmt_req_pending_cmd, "MESSAGE_LOCKDS_REQ"));
- vty_mgmt_resume_response(vty, success);
+ vty_mgmt_resume_response(vty,
+ success ? CMD_SUCCESS : CMD_WARNING);
}
}
@@ -3578,9 +3573,9 @@ static void vty_mgmt_set_config_result_notified(
vty_out(vty, "ERROR: SET_CONFIG request failed, Error: %s\n",
errmsg_if_any ? errmsg_if_any : "Unknown");
} else {
- MGMTD_FE_CLIENT_DBG("SET_CONFIG request for client 0x%" PRIx64
- " req-id %" PRIu64 " was successfull",
- client_id, req_id);
+ debug_fe_client("SET_CONFIG request for client 0x%" PRIx64
+ " req-id %" PRIu64 " was successfull",
+ client_id, req_id);
}
if (implicit_commit) {
@@ -3589,7 +3584,8 @@ static void vty_mgmt_set_config_result_notified(
vty_mgmt_unlock_running_inline(vty);
}
- vty_mgmt_resume_response(vty, success);
+ vty_mgmt_resume_response(vty, success ? CMD_SUCCESS
+ : CMD_WARNING_CONFIG_FAILED);
}
static void vty_mgmt_commit_config_result_notified(
@@ -3609,15 +3605,15 @@ static void vty_mgmt_commit_config_result_notified(
vty_out(vty, "ERROR: COMMIT_CONFIG request failed, Error: %s\n",
errmsg_if_any ? errmsg_if_any : "Unknown");
} else {
- MGMTD_FE_CLIENT_DBG(
- "COMMIT_CONFIG request for client 0x%" PRIx64
- " req-id %" PRIu64 " was successfull",
- client_id, req_id);
+ debug_fe_client("COMMIT_CONFIG request for client 0x%" PRIx64
+ " req-id %" PRIu64 " was successfull",
+ client_id, req_id);
if (errmsg_if_any)
vty_out(vty, "MGMTD: %s\n", errmsg_if_any);
}
- vty_mgmt_resume_response(vty, success);
+ vty_mgmt_resume_response(vty, success ? CMD_SUCCESS
+ : CMD_WARNING_CONFIG_FAILED);
}
static int vty_mgmt_get_data_result_notified(
@@ -3637,13 +3633,13 @@ static int vty_mgmt_get_data_result_notified(
client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
vty_out(vty, "ERROR: GET_DATA request failed, Error: %s\n",
errmsg_if_any ? errmsg_if_any : "Unknown");
- vty_mgmt_resume_response(vty, success);
+ vty_mgmt_resume_response(vty, CMD_WARNING);
return -1;
}
- MGMTD_FE_CLIENT_DBG("GET_DATA request succeeded, client 0x%" PRIx64
- " req-id %" PRIu64,
- client_id, req_id);
+ debug_fe_client("GET_DATA request succeeded, client 0x%" PRIx64
+ " req-id %" PRIu64,
+ client_id, req_id);
if (req_id != mgmt_last_req_id) {
mgmt_last_req_id = req_id;
@@ -3656,12 +3652,211 @@ static int vty_mgmt_get_data_result_notified(
}
if (next_key < 0) {
vty_out(vty, "]\n");
- vty_mgmt_resume_response(vty, success);
+ vty_mgmt_resume_response(vty, CMD_SUCCESS);
}
return 0;
}
+static ssize_t vty_mgmt_libyang_print(void *user_data, const void *buf,
+ size_t count)
+{
+ struct vty *vty = user_data;
+
+ vty_out(vty, "%.*s", (int)count, (const char *)buf);
+ return count;
+}
+
+static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
+ struct ly_err_item *ei)
+{
+ bool have_apptag = ei->apptag && ei->apptag[0] != 0;
+ bool have_path = ei->path && ei->path[0] != 0;
+ bool have_msg = ei->msg && ei->msg[0] != 0;
+ const char *severity = NULL;
+ const char *evalid = NULL;
+ const char *ecode = NULL;
+ LY_ERR err = ei->no;
+
+ if (ei->level == LY_LLERR)
+ severity = "error";
+ else if (ei->level == LY_LLWRN)
+ severity = "warning";
+
+ ecode = yang_ly_strerrcode(err);
+ if (err == LY_EVALID && ei->vecode != LYVE_SUCCESS)
+ evalid = yang_ly_strvecode(ei->vecode);
+
+ switch (format) {
+ case LYD_XML:
+ vty_out(vty,
+ "<rpc-error xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">");
+ vty_out(vty, "<error-type>application</error-type>");
+ if (severity)
+ vty_out(vty, "<error-severity>%s</error-severity>",
+ severity);
+ if (ecode)
+ vty_out(vty, "<error-code>%s</error-code>", ecode);
+ if (evalid)
+ vty_out(vty, "<error-validation>%s</error-validation>\n",
+ evalid);
+ if (have_path)
+ vty_out(vty, "<error-path>%s</error-path>\n", ei->path);
+ if (have_apptag)
+ vty_out(vty, "<error-app-tag>%s</error-app-tag>\n",
+ ei->apptag);
+ if (have_msg)
+ vty_out(vty, "<error-message>%s</error-message>\n",
+ ei->msg);
+
+ vty_out(vty, "</rpc-error>");
+ break;
+ case LYD_JSON:
+ vty_out(vty, "{ \"error-type\": \"application\"");
+ if (severity)
+ vty_out(vty, ", \"error-severity\": \"%s\"", severity);
+ if (ecode)
+ vty_out(vty, ", \"error-code\": \"%s\"", ecode);
+ if (evalid)
+ vty_out(vty, ", \"error-validation\": \"%s\"", evalid);
+ if (have_path)
+ vty_out(vty, ", \"error-path\": \"%s\"", ei->path);
+ if (have_apptag)
+ vty_out(vty, ", \"error-app-tag\": \"%s\"", ei->apptag);
+ if (have_msg)
+ vty_out(vty, ", \"error-message\": \"%s\"", ei->msg);
+
+ vty_out(vty, "}");
+ break;
+ case LYD_UNKNOWN:
+ case LYD_LYB:
+ default:
+ vty_out(vty, "%% error");
+ if (severity)
+ vty_out(vty, " severity: %s", severity);
+ if (evalid)
+ vty_out(vty, " invalid: %s", evalid);
+ if (have_path)
+ vty_out(vty, " path: %s", ei->path);
+ if (have_apptag)
+ vty_out(vty, " app-tag: %s", ei->apptag);
+ if (have_msg)
+ vty_out(vty, " msg: %s", ei->msg);
+ break;
+ }
+}
+
+static uint vty_out_yang_errors(struct vty *vty, LYD_FORMAT format)
+{
+ struct ly_err_item *ei = ly_err_first(ly_native_ctx);
+ uint count;
+
+ if (!ei)
+ return 0;
+
+ if (format == LYD_JSON)
+ vty_out(vty, "\"ietf-restconf:errors\": [ ");
+
+ for (count = 0; ei; count++, ei = ei->next) {
+ if (count)
+ vty_out(vty, ", ");
+ vty_out_yang_error(vty, format, ei);
+ }
+
+ if (format == LYD_JSON)
+ vty_out(vty, " ]");
+
+ ly_err_clean(ly_native_ctx, NULL);
+
+ return count;
+}
+
+
+static int vty_mgmt_get_tree_result_notified(
+ struct mgmt_fe_client *client, uintptr_t user_data, uint64_t client_id,
+ uint64_t session_id, uintptr_t session_ctx, uint64_t req_id,
+ Mgmtd__DatastoreId ds_id, LYD_FORMAT result_type, void *result,
+ size_t len, int partial_error)
+{
+ struct vty *vty;
+ struct lyd_node *dnode;
+ int ret = CMD_SUCCESS;
+ LY_ERR err;
+
+ vty = (struct vty *)session_ctx;
+
+ debug_fe_client("GET_TREE request %ssucceeded, client 0x%" PRIx64
+ " req-id %" PRIu64,
+ partial_error ? "partially " : "", client_id, req_id);
+
+ assert(result_type == LYD_LYB ||
+ result_type == vty->mgmt_req_pending_data);
+
+ if (vty->mgmt_req_pending_data == LYD_XML && partial_error)
+ vty_out(vty,
+ "<!-- some errors occurred gathering results -->\n");
+
+ if (result_type == LYD_LYB) {
+ /*
+ * parse binary into tree and print in the specified format
+ */
+ result_type = vty->mgmt_req_pending_data;
+
+ err = lyd_parse_data_mem(ly_native_ctx, result, LYD_LYB, 0, 0,
+ &dnode);
+ if (!err)
+ err = lyd_print_clb(vty_mgmt_libyang_print, vty, dnode,
+ result_type, LYD_PRINT_WITHSIBLINGS);
+ lyd_free_all(dnode);
+
+ if (vty_out_yang_errors(vty, result_type) || err)
+ ret = CMD_WARNING;
+ } else {
+ /*
+ * Print the in-format result
+ */
+ assert(result_type == LYD_XML || result_type == LYD_JSON);
+ vty_out(vty, "%.*s\n", (int)len - 1, (const char *)result);
+ }
+
+ vty_mgmt_resume_response(vty, ret);
+
+ return 0;
+}
+
+static int vty_mgmt_error_notified(struct mgmt_fe_client *client,
+ uintptr_t user_data, uint64_t client_id,
+ uint64_t session_id, uintptr_t session_ctx,
+ uint64_t req_id, int error,
+ const char *errstr)
+{
+ struct vty *vty = (struct vty *)session_ctx;
+ const char *cname = mgmt_fe_client_name(client);
+
+ if (!vty->mgmt_req_pending_cmd) {
+ debug_fe_client("Erorr with no pending command: %d returned for client %s 0x%" PRIx64
+ " session-id %" PRIu64 " req-id %" PRIu64
+ "error-str %s",
+ error, cname, client_id, session_id, req_id,
+ errstr);
+ vty_out(vty,
+ "%% Error %d from MGMTD for %s with no pending command: %s\n",
+ error, cname, errstr);
+ return CMD_WARNING;
+ }
+
+ debug_fe_client("Erorr %d returned for client %s 0x%" PRIx64
+ " session-id %" PRIu64 " req-id %" PRIu64 "error-str %s",
+ error, cname, client_id, session_id, req_id, errstr);
+
+ vty_out(vty, "%% %s (for %s, client %s)\n", errstr,
+ vty->mgmt_req_pending_cmd, cname);
+
+ vty_mgmt_resume_response(vty, error ? CMD_WARNING : CMD_SUCCESS);
+
+ return 0;
+}
+
static struct mgmt_fe_client_cbs mgmt_cbs = {
.client_connect_notify = vty_mgmt_server_connected,
.client_session_notify = vty_mgmt_session_notify,
@@ -3669,6 +3864,9 @@ static struct mgmt_fe_client_cbs mgmt_cbs = {
.set_config_notify = vty_mgmt_set_config_result_notified,
.commit_config_notify = vty_mgmt_commit_config_result_notified,
.get_data_notify = vty_mgmt_get_data_result_notified,
+ .get_tree_notify = vty_mgmt_get_tree_result_notified,
+ .error_notify = vty_mgmt_error_notified,
+
};
void vty_init_mgmt_fe(void)
@@ -3715,12 +3913,15 @@ int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
return 0;
}
-int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit)
+int vty_mgmt_send_config_data(struct vty *vty, const char *xpath_base,
+ bool implicit_commit)
{
Mgmtd__YangDataValue value[VTY_MAXCFGCHANGES];
Mgmtd__YangData cfg_data[VTY_MAXCFGCHANGES];
Mgmtd__YangCfgDataReq cfg_req[VTY_MAXCFGCHANGES];
Mgmtd__YangCfgDataReq *cfgreq[VTY_MAXCFGCHANGES] = {0};
+ char xpath[VTY_MAXCFGCHANGES][XPATH_MAXLEN];
+ char *change_xpath;
size_t indx;
if (vty->type == VTY_FILE) {
@@ -3756,6 +3957,9 @@ int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit)
}
}
+ if (xpath_base == NULL)
+ xpath_base = "";
+
for (indx = 0; indx < vty->num_cfg_changes; indx++) {
mgmt_yang_data_init(&cfg_data[indx]);
@@ -3768,29 +3972,47 @@ int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit)
cfg_data[indx].value = &value[indx];
}
- cfg_data[indx].xpath = vty->cfg_changes[indx].xpath;
+ change_xpath = vty->cfg_changes[indx].xpath;
+
+ memset(xpath[indx], 0, sizeof(xpath[indx]));
+ /* If change xpath is relative, prepend base xpath. */
+ if (change_xpath[0] == '.') {
+ strlcpy(xpath[indx], xpath_base, sizeof(xpath[indx]));
+ change_xpath++; /* skip '.' */
+ }
+ strlcat(xpath[indx], change_xpath, sizeof(xpath[indx]));
+
+ cfg_data[indx].xpath = xpath[indx];
mgmt_yang_cfg_data_req_init(&cfg_req[indx]);
cfg_req[indx].data = &cfg_data[indx];
switch (vty->cfg_changes[indx].operation) {
- case NB_OP_DESTROY:
+ case NB_OP_DELETE:
cfg_req[indx].req_type =
MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA;
break;
+ case NB_OP_DESTROY:
+ cfg_req[indx].req_type =
+ MGMTD__CFG_DATA_REQ_TYPE__REMOVE_DATA;
+ break;
+
+ case NB_OP_CREATE_EXCL:
+ cfg_req[indx].req_type =
+ MGMTD__CFG_DATA_REQ_TYPE__CREATE_DATA;
+ break;
+
+ case NB_OP_REPLACE:
+ cfg_req[indx].req_type =
+ MGMTD__CFG_DATA_REQ_TYPE__REPLACE_DATA;
+ break;
+
case NB_OP_CREATE:
case NB_OP_MODIFY:
case NB_OP_MOVE:
- case NB_OP_PRE_VALIDATE:
- case NB_OP_APPLY_FINISH:
cfg_req[indx].req_type =
MGMTD__CFG_DATA_REQ_TYPE__SET_DATA;
break;
- case NB_OP_GET_ELEM:
- case NB_OP_GET_NEXT:
- case NB_OP_GET_KEYS:
- case NB_OP_LOOKUP_ENTRY:
- case NB_OP_RPC:
default:
assertf(false,
"Invalid operation type for send config: %d",
@@ -3874,6 +4096,30 @@ int vty_mgmt_send_get_req(struct vty *vty, bool is_config,
return 0;
}
+int vty_mgmt_send_get_data_req(struct vty *vty, uint8_t datastore,
+ LYD_FORMAT result_type, uint8_t flags,
+ uint8_t defaults, const char *xpath)
+{
+ LYD_FORMAT intern_format = result_type;
+
+ vty->mgmt_req_id++;
+
+ if (mgmt_fe_send_get_data_req(mgmt_fe_client, vty->mgmt_session_id,
+ vty->mgmt_req_id, datastore,
+ intern_format, flags, defaults, xpath)) {
+ zlog_err("Failed to send GET-DATA to MGMTD session-id: %" PRIu64
+ " req-id %" PRIu64 ".",
+ vty->mgmt_session_id, vty->mgmt_req_id);
+ vty_out(vty, "Failed to send GET-DATA to MGMTD!\n");
+ return -1;
+ }
+
+ vty->mgmt_req_pending_cmd = "MESSAGE_GET_DATA_REQ";
+ vty->mgmt_req_pending_data = result_type;
+
+ return 0;
+}
+
/* Install vty's own commands like `who' command. */
void vty_init(struct event_loop *master_thread, bool do_command_logging)
{