FEATURE client-side funtions
diff --git a/src/io.c b/src/io.c
index b6f2ac9..4d76a11 100644
--- a/src/io.c
+++ b/src/io.c
@@ -573,7 +573,7 @@
int count, i;
const char *attrs;
struct lyd_node *content;
- struct nc_rpc_server *rpc;
+ struct nc_server_rpc *rpc;
const char **capabilities;
uint32_t *sid = NULL;
char *buf = NULL;
@@ -618,7 +618,7 @@
break;
case NC_MSG_REPLY:
- rpc = va_arg(ap, struct nc_rpc_server *);
+ rpc = va_arg(ap, struct nc_server_rpc *);
switch (session->ti_type) {
case NC_TI_FD:
write(session->ti.fd.out, "<rpc-reply", 10);
@@ -743,7 +743,7 @@
int count;
const char *attrs;
struct lyd_node *content;
- struct nc_rpc_server *rpc;
+ struct nc_server_rpc *rpc;
char *buf = NULL;
struct wclb_arg arg;
@@ -764,7 +764,7 @@
session->msgid++;
break;
case NC_MSG_REPLY:
- rpc = va_arg(ap, struct nc_rpc_server *);
+ rpc = va_arg(ap, struct nc_server_rpc *);
write_clb((void *)&arg, "<rpc-reply", 10);
lyxml_dump_clb(write_clb, (void *)&arg, rpc->root, LYXML_DUMP_ATTRS);
write_clb((void *)&arg, ">", 1);
diff --git a/src/messages.c b/src/messages.c
index d222128..81287b6 100644
--- a/src/messages.c
+++ b/src/messages.c
@@ -33,54 +33,12 @@
const char *rpcedit_testopt2str[] = {NULL, "test-then-set", "set", "test-only"};
const char *rpcedit_erropt2str[] = {NULL, "stop-on-error", "continue-on-error", "rollback-on-error"};
-API struct nc_filter *
-nc_filter_new(NC_FILTER type, char *data, int constdata)
-{
- struct nc_filter *filter;
-
- if (!data) {
- data = "";
- constdata = 1;
- }
-
- filter = malloc(sizeof *filter);
- if (!filter) {
- ERRMEM;
- return NULL;
- }
-
- filter->type = type;
- filter->refs = 1;
- if (constdata) {
- filter->data = strdup(data);
- } else {
- filter->data = data;
- }
-
- return filter;
-}
-
-API void
-nc_filter_free(struct nc_filter *filter)
-{
- if (!filter) {
- return;
- }
-
- filter->refs--;
-
- if (!filter->refs) {
- free(filter->data);
- free(filter);
- }
-}
-
API struct nc_rpc *
-nc_rpc_generic(struct lyd_node *data)
+nc_rpc_generic(const struct lyd_node *data, NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_generic *rpc;
- if (data->prev != data) {
+ if (data->next || (data->prev != data)) {
ERR("Generic RPC must have a single root node.");
return NULL;
}
@@ -92,13 +50,18 @@
}
rpc->type = NC_RPC_GENERIC;
- rpc->data = data;
+ if (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE) {
+ rpc->data = lyd_dup(data, 1);
+ } else {
+ rpc->data = (struct lyd_node *)data;
+ }
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
API struct nc_rpc *
-nc_rpc_generic_xml(const char *xml_str)
+nc_rpc_generic_xml(const char *xml_str, NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_generic_xml *rpc;
@@ -109,17 +72,27 @@
}
rpc->type = NC_RPC_GENERIC_XML;
- rpc->xml_str = strdup(xml_str);
+ if (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE) {
+ rpc->xml_str = strdup(xml_str);
+ } else {
+ rpc->xml_str = (char *)xml_str;
+ }
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
API struct nc_rpc *
-nc_rpc_getconfig(NC_DATASTORE source, struct nc_filter *filter)
+nc_rpc_getconfig(NC_DATASTORE source, const char *filter, NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_getconfig *rpc;
- rpc = calloc(1, sizeof *rpc);
+ if ((filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
+ ERR("Filter must either be an XML subtree or an XPath expression.");
+ return NULL;
+ }
+
+ rpc = malloc(sizeof *rpc);
if (!rpc) {
ERRMEM;
return NULL;
@@ -127,17 +100,19 @@
rpc->type = NC_RPC_GETCONFIG;
rpc->source = source;
- if (filter) {
- filter->refs++;
- rpc->filter = filter;
+ if (filter && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
+ rpc->filter = strdup(filter);
+ } else {
+ rpc->filter = (char *)filter;
}
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
API struct nc_rpc *
nc_rpc_edit(NC_DATASTORE target, NC_RPC_EDIT_DFLTOP default_op, NC_RPC_EDIT_TESTOPT test_opt,
- NC_RPC_EDIT_ERROPT error_opt, const char *edit_content)
+ NC_RPC_EDIT_ERROPT error_opt, const char *edit_content, NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_edit *rpc;
@@ -146,7 +121,7 @@
return NULL;
}
- rpc = calloc(1, sizeof *rpc);
+ rpc = malloc(sizeof *rpc);
if (!rpc) {
ERRMEM;
return NULL;
@@ -157,13 +132,19 @@
rpc->default_op = default_op;
rpc->test_opt = test_opt;
rpc->error_opt = error_opt;
- rpc->edit_cont = strdup(edit_content);
+ if (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE) {
+ rpc->edit_cont = strdup(edit_content);
+ } else {
+ rpc->edit_cont = (char *)edit_content;
+ }
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
API struct nc_rpc *
-nc_rpc_copy(NC_DATASTORE target, const char *url_trg, NC_DATASTORE source, const char *url_or_config_src)
+nc_rpc_copy(NC_DATASTORE target, const char *url_trg, NC_DATASTORE source, const char *url_or_config_src,
+ NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_copy *rpc;
@@ -172,7 +153,7 @@
return NULL;
}
- rpc = calloc(1, sizeof *rpc);
+ rpc = malloc(sizeof *rpc);
if (!rpc) {
ERRMEM;
return NULL;
@@ -180,27 +161,28 @@
rpc->type = NC_RPC_COPY;
rpc->target = target;
- if (url_trg) {
+ if (url_trg && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->url_trg = strdup(url_trg);
} else {
- rpc->url_trg = NULL;
+ rpc->url_trg = (char *)url_trg;
}
rpc->source = source;
- if (url_or_config_src) {
+ if (url_or_config_src && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->url_config_src = strdup(url_or_config_src);
} else {
- rpc->url_config_src = NULL;
+ rpc->url_config_src = (char *)url_or_config_src;
}
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
API struct nc_rpc *
-nc_rpc_delete(NC_DATASTORE target, char *url)
+nc_rpc_delete(NC_DATASTORE target, const char *url, NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_delete *rpc;
- rpc = calloc(1, sizeof *rpc);
+ rpc = malloc(sizeof *rpc);
if (!rpc) {
ERRMEM;
return NULL;
@@ -208,11 +190,12 @@
rpc->type = NC_RPC_DELETE;
rpc->target = target;
- if (url) {
+ if (url && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->url = strdup(url);
} else {
- rpc->url = NULL;
+ rpc->url = (char *)url;
}
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
@@ -252,21 +235,28 @@
}
API struct nc_rpc *
-nc_rpc_get(struct nc_filter *filter)
+nc_rpc_get(const char *filter, NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_get *rpc;
- rpc = calloc(1, sizeof *rpc);
+ if ((filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
+ ERR("Filter must either be an XML subtree or an XPath expression.");
+ return NULL;
+ }
+
+ rpc = malloc(sizeof *rpc);
if (!rpc) {
ERRMEM;
return NULL;
}
rpc->type = NC_RPC_GET;
- if (filter) {
- filter->refs++;
- rpc->filter = filter;
+ if (filter && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
+ rpc->filter = strdup(filter);
+ } else {
+ rpc->filter = (char *)filter;
}
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
@@ -289,7 +279,8 @@
}
API struct nc_rpc *
-nc_rpc_commit(int confirmed, uint32_t confirm_timeout, const char *persist, const char *persist_id)
+nc_rpc_commit(int confirmed, uint32_t confirm_timeout, const char *persist, const char *persist_id,
+ NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_commit *rpc;
@@ -302,16 +293,17 @@
rpc->type = NC_RPC_COMMIT;
rpc->confirmed = confirmed;
rpc->confirm_timeout = confirm_timeout;
- if (persist) {
+ if (persist && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->persist = strdup(persist);
} else {
- rpc->persist = NULL;
+ rpc->persist = (char *)persist;
}
- if (persist_id) {
+ if (persist_id && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->persist_id = strdup(persist_id);
} else {
- rpc->persist_id = NULL;
+ rpc->persist_id = (char *)persist_id;
}
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
@@ -333,7 +325,7 @@
}
API struct nc_rpc *
-nc_rpc_cancel(const char *persist_id)
+nc_rpc_cancel(const char *persist_id, NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_cancel *rpc;
@@ -344,17 +336,18 @@
}
rpc->type = NC_RPC_CANCEL;
- if (persist_id) {
+ if (persist_id && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->persist_id = strdup(persist_id);
} else {
- rpc->persist_id = NULL;
+ rpc->persist_id = (char *)persist_id;
}
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
API struct nc_rpc *
-nc_rpc_validate(NC_DATASTORE source, const char *url_or_config)
+nc_rpc_validate(NC_DATASTORE source, const char *url_or_config, NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_validate *rpc;
@@ -371,17 +364,18 @@
rpc->type = NC_RPC_VALIDATE;
rpc->source = source;
- if (url_or_config) {
+ if (url_or_config && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->url_config_src = strdup(url_or_config);
} else {
- rpc->url_config_src = NULL;
+ rpc->url_config_src = (char *)url_or_config;
}
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
API struct nc_rpc *
-nc_rpc_getschema(const char *identifier, const char *version, const char *format)
+nc_rpc_getschema(const char *identifier, const char *version, const char *format, NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_getschema *rpc;
@@ -392,26 +386,37 @@
}
rpc->type = NC_RPC_GETSCHEMA;
- rpc->identifier = strdup(identifier);
- if (version) {
+ if (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE) {
+ rpc->identifier = strdup(identifier);
+ } else {
+ rpc->identifier = (char *)identifier;
+ }
+ if (version && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->version = strdup(version);
} else {
- rpc->version = NULL;
+ rpc->version = (char *)version;
}
- if (format) {
+ if (format && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->format = strdup(format);
} else {
- rpc->format = NULL;
+ rpc->format = (char *)format;
}
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
API struct nc_rpc *
-nc_rpc_subscribe(const char *stream_name, struct nc_filter *filter, const char *start_time, const char *stop_time)
+nc_rpc_subscribe(const char *stream_name, const char *filter, const char *start_time, const char *stop_time,
+ NC_RPC_PARAMTYPE paramtype)
{
struct nc_rpc_subscribe *rpc;
+ if ((filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
+ ERR("Filter must either be an XML subtree or an XPath expression.");
+ return NULL;
+ }
+
rpc = malloc(sizeof *rpc);
if (!rpc) {
ERRMEM;
@@ -419,35 +424,41 @@
}
rpc->type = NC_RPC_SUBSCRIBE;
- if (stream_name) {
+ if (stream_name && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->stream = strdup(stream_name);
} else {
- rpc->stream = NULL;
+ rpc->stream = (char *)stream_name;
}
- if (filter) {
- filter->refs++;
- rpc->filter = filter;
+ if (filter && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
+ rpc->filter = strdup(filter);
} else {
- filter = NULL;
+ rpc->filter = (char *)filter;
}
- if (start_time) {
+ if (start_time && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->start = strdup(start_time);
} else {
- rpc->start = NULL;
+ rpc->start = (char *)start_time;
}
- if (stop_time) {
+ if (stop_time && (paramtype == NC_RPC_PARAMTYPE_DUP_AND_FREE)) {
rpc->stop = strdup(stop_time);
} else {
- rpc->stop = NULL;
+ rpc->stop = (char *)stop_time;
}
+ rpc->free = (paramtype == NC_RPC_PARAMTYPE_CONST ? 0 : 1);
return (struct nc_rpc *)rpc;
}
+static void
+nc_server_rpc_free(struct nc_server_rpc *rpc)
+{
+ lyxml_free(rpc->tree->schema->module->ctx, rpc->root);
+ lyd_free(rpc->tree);
+}
+
API void
nc_rpc_free(struct nc_rpc *rpc)
{
- struct nc_rpc_server *rpc_server;
struct nc_rpc_generic *rpc_generic;
struct nc_rpc_generic_xml *rpc_generic_xml;
struct nc_rpc_getconfig *rpc_getconfig;
@@ -465,65 +476,84 @@
return;
}
- switch(rpc->type) {
- case NC_RPC_SERVER:
- rpc_server = (struct nc_rpc_server *)rpc;
- lyxml_free_elem(rpc_server->tree->schema->module->ctx, rpc_server->root);
- lyd_free(rpc_server->tree);
- break;
+ switch (rpc->type) {
case NC_RPC_GENERIC:
rpc_generic = (struct nc_rpc_generic *)rpc;
- lyd_free(rpc_generic->data);
+ if (rpc_generic->free) {
+ lyd_free(rpc_generic->data);
+ }
break;
case NC_RPC_GENERIC_XML:
rpc_generic_xml = (struct nc_rpc_generic_xml *)rpc;
- free(rpc_generic_xml->xml_str);
+ if (rpc_generic_xml->free) {
+ free(rpc_generic_xml->xml_str);
+ }
break;
case NC_RPC_GETCONFIG:
rpc_getconfig = (struct nc_rpc_getconfig *)rpc;
- nc_filter_free(rpc_getconfig->filter);
+ if (rpc_getconfig->free) {
+ free(rpc_getconfig->filter);
+ }
break;
case NC_RPC_EDIT:
rpc_edit = (struct nc_rpc_edit *)rpc;
- free(rpc_edit->edit_cont);
+ if (rpc_edit->free) {
+ free(rpc_edit->edit_cont);
+ }
break;
case NC_RPC_COPY:
rpc_copy = (struct nc_rpc_copy *)rpc;
- free(rpc_copy->url_config_src);
+ if (rpc_copy->free) {
+ free(rpc_copy->url_config_src);
+ }
break;
case NC_RPC_DELETE:
rpc_delete = (struct nc_rpc_delete *)rpc;
- free(rpc_delete->url);
+ if (rpc_delete->free) {
+ free(rpc_delete->url);
+ }
break;
case NC_RPC_GET:
rpc_get = (struct nc_rpc_get *)rpc;
- nc_filter_free(rpc_get->filter);
+ if (rpc_get->free) {
+ free(rpc_get->filter);
+ }
break;
case NC_RPC_COMMIT:
rpc_commit = (struct nc_rpc_commit *)rpc;
- free(rpc_commit->persist);
- free(rpc_commit->persist_id);
+ if (rpc_commit->free) {
+ free(rpc_commit->persist);
+ free(rpc_commit->persist_id);
+ }
break;
case NC_RPC_CANCEL:
rpc_cancel = (struct nc_rpc_cancel *)rpc;
- free(rpc_cancel->persist_id);
+ if (rpc_cancel->free) {
+ free(rpc_cancel->persist_id);
+ }
break;
case NC_RPC_VALIDATE:
rpc_validate = (struct nc_rpc_validate *)rpc;
- free(rpc_validate->url_config_src);
+ if (rpc_validate->free) {
+ free(rpc_validate->url_config_src);
+ }
break;
case NC_RPC_GETSCHEMA:
rpc_getschema = (struct nc_rpc_getschema *)rpc;
- free(rpc_getschema->identifier);
- free(rpc_getschema->version);
- free(rpc_getschema->format);
+ if (rpc_getschema->free) {
+ free(rpc_getschema->identifier);
+ free(rpc_getschema->version);
+ free(rpc_getschema->format);
+ }
break;
case NC_RPC_SUBSCRIBE:
rpc_subscribe = (struct nc_rpc_subscribe *)rpc;
- free(rpc_subscribe->stream);
- nc_filter_free(rpc_subscribe->filter);
- free(rpc_subscribe->start);
- free(rpc_subscribe->stop);
+ if (rpc_subscribe->free) {
+ free(rpc_subscribe->stream);
+ free(rpc_subscribe->filter);
+ free(rpc_subscribe->start);
+ free(rpc_subscribe->stop);
+ }
break;
case NC_RPC_KILL:
case NC_RPC_DISCARD:
@@ -540,9 +570,9 @@
nc_reply_free(struct nc_reply *reply)
{
struct nc_reply_error *error;
- struct nc_reply_ok *ok;
struct nc_reply_data *data;
struct lyd_node *node;
+ int i, j;
if (!reply) {
return;
@@ -551,20 +581,42 @@
switch(reply->type) {
case NC_REPLY_DATA:
data = (struct nc_reply_data *)reply;
- lyxml_free_elem(data->data->schema->module->ctx, data->root);
for (node = data->data; data->data; node = data->data) {
data->data = node->next;
lyd_free(node);
}
break;
case NC_REPLY_OK:
- ok = (struct nc_reply_ok *)reply;
- lyxml_free_elem(ok->ctx, ok->root);
+ /* nothing to free */
break;
case NC_REPLY_ERROR:
error = (struct nc_reply_error *)reply;
- (void)error;
- /* TODO */
+ for (i = 0; i < error->err_count; ++i) {
+ lydict_remove(error->ctx, error->err[i].type);
+ lydict_remove(error->ctx, error->err[i].tag);
+ lydict_remove(error->ctx, error->err[i].severity);
+ lydict_remove(error->ctx, error->err[i].apptag);
+ lydict_remove(error->ctx, error->err[i].path);
+ lydict_remove(error->ctx, error->err[i].message);
+ lydict_remove(error->ctx, error->err[i].message_lang);
+ lydict_remove(error->ctx, error->err[i].sid);
+ for (j = 0; j < error->err[i].attr_count; ++j) {
+ lydict_remove(error->ctx, error->err[i].attr[j]);
+ }
+ free(error->err[i].attr);
+ for (j = 0; j < error->err[i].elem_count; ++j) {
+ lydict_remove(error->ctx, error->err[i].elem[j]);
+ }
+ free(error->err[i].elem);
+ for (j = 0; j < error->err[i].ns_count; ++j) {
+ lydict_remove(error->ctx, error->err[i].ns[j]);
+ }
+ free(error->err[i].ns);
+ for (j = 0; j < error->err[i].other_count; ++j) {
+ lyxml_free(error->ctx, error->err[i].other[j]);
+ }
+ free(error->err[i].other);
+ }
break;
case NC_REPLY_NOTIF:
nc_notif_free((struct nc_notif *)reply);
@@ -580,7 +632,6 @@
return;
}
- lyxml_free_elem(notif->tree->schema->module->ctx, notif->root);
lyd_free(notif->tree);
free(notif);
}
diff --git a/src/messages.h b/src/messages.h
index 08a10a3..3917f96 100644
--- a/src/messages.h
+++ b/src/messages.h
@@ -23,6 +23,15 @@
#ifndef NC_MESSAGES_H_
#define NC_MESSAGES_H_
+#include <stdint.h>
+
+typedef enum {
+ NC_REPLY_ERROR,
+ NC_REPLY_OK,
+ NC_REPLY_DATA,
+ NC_REPLY_NOTIF
+} NC_REPLY_TYPE;
+
typedef enum {
NC_RPC_EDIT_DFLTOP_UNKNOWN = 0,
NC_RPC_EDIT_DFLTOP_MERGE,
@@ -45,50 +54,94 @@
} NC_RPC_EDIT_ERROPT;
typedef enum {
- NC_FILTER_SUBTREE,
- NC_FILTER_XPATH
-} NC_FILTER;
-
-struct nc_filter;
+ NC_RPC_PARAMTYPE_CONST,
+ NC_RPC_PARAMTYPE_FREE,
+ NC_RPC_PARAMTYPE_DUP_AND_FREE
+} NC_RPC_PARAMTYPE;
/**
- * @brief Create NETCONF filter for \<get\> or \<get-config\> RPCs.
- *
- * The returned object can be used repeatedly. Caller is supposed to free it using nc_filter_free().
- *
- * @param[in] type Filter type of the \p data. #NC_FILTER_SUBTREE and #NC_FILTER_XPATH are supported.
- * Note that #NC_FILTER_XPATH is accepted only on sessions supporting the :xpath capability.
- * @param[in] data Content of the filter. Serialized XML data in case of #NC_FILTER_SUBTREE and XPath query
- * in case of #NC_FILTER_XPATH (use YANG schema names as namespace prefixes).
- * @param[in] constdata Flag for handling \p data. If set, the \p data is handled as const char* and the string
- * is duplicated for internal use. If not set, \p data is not duplicated but caller is supposed
- * to forget about the provided string.
- * @return Created filter structure to be used in nc_rpc_getconfig() and nc_rpc_get(). NULL in case of failure.
+ * @brief NETCONF error structure representation
*/
-struct nc_filter *nc_filter_new(NC_FILTER type, char *data, int constdata);
+struct nc_err {
+ /**
+ * @brief \<error-type\>, error layer where the error occurred.
+ */
+ const char *type;
+ /**
+ * @brief \<error-tag\>.
+ */
+ const char *tag;
+ /**
+ * @brief \<error-severity\>.
+ */
+ const char *severity;
+ /**
+ * @brief \<error-app-tag\>, the data-model-specific or implementation-specific error condition, if one exists.
+ */
+ const char *apptag;
+ /**
+ * @brief \<error-path\>, XPATH expression identifying the element with the error.
+ */
+ const char *path;
+ /**
+ * @brief \<error-message\>, Human-readable description of the error.
+ */
+ const char *message;
+ const char *message_lang;
-/**
- * @brief Free the NETCONF filter object.
- *
- * @param[in] filter Object to free.
- */
-void nc_filter_free(struct nc_filter *filter);
+ /* <error-info> */
+
+ /**
+ * @brief \<session-id\>, session ID of the session holding the requested lock.
+ */
+ const char *sid;
+ /**
+ * @brief \<bad-attr\>, the name of the data-model-specific XML attribute that caused the error.
+ */
+ const char **attr;
+ int attr_count;
+ /**
+ * @brief \<bad-element\>, the name of the data-model-specific XML element that caused the error.
+ */
+ const char **elem;
+ int elem_count;
+ /**
+ * @brief \<bad-namespace\>, the name of the unexpected XML namespace that caused the error.
+ */
+ const char **ns;
+ int ns_count;
+ /**
+ * @brief Remaining non-standard elements.
+ */
+ struct lyxml_elem **other;
+ int other_count;
+};
+
+struct nc_reply {
+ NC_REPLY_TYPE type;
+};
+
+struct nc_reply_error {
+ NC_REPLY_TYPE type; /**< NC_REPLY_ERROR */
+ struct ly_ctx *ctx;
+ struct nc_err *err; /**< errors, any of the values inside can be NULL */
+ int err_count;
+};
+
+struct nc_reply_data {
+ NC_REPLY_TYPE type; /**< NC_REPLY_DATA */
+ struct lyd_node *data; /**< libyang data tree */
+};
+
+struct nc_notif {
+ NC_REPLY_TYPE type; /**< NC_REPLY_NOTIF */
+ struct lyd_node *tree; /**< libyang data tree of the message */
+};
/**
* @brief NETCONF RPC object
*/
struct nc_rpc;
-struct nc_rpc_server;
-
-/**
- * @brief NETCONF RPC reply object
- */
-struct nc_reply;
-
-/**
- * @brief NETCONF Notification object
- */
-struct nc_notif;
/**
* @brief Create a generic NETCONF RPC
@@ -96,10 +149,11 @@
* Note that created object can be sent via any NETCONF session that shares the context
* of the \p data.
*
- * @param[in] data NETCONF RPC data. Their ownership is passed to the RPC (they are freed with it).
+ * @param[in] data NETCONF RPC data as a data tree.
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
-struct nc_rpc *nc_rpc_generic(struct lyd_node *data);
+struct nc_rpc *nc_rpc_generic(const struct lyd_node *data, NC_RPC_PARAMTYPE paramtype);
/**
* @brief Create a generic NETCONF RPC from an XML string
@@ -110,10 +164,11 @@
* check. Created object can be sent via any NETCONF session which supports all the
* needed NETCONF capabilities for the RPC.
*
- * @param[in] data NETCONF RPC data. Their ownership is passed to the RPC (they are freed with it).
+ * @param[in] xml_str NETCONF RPC data as an XML string.
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
-struct nc_rpc *nc_rpc_generic_xml(const char *xml_str);
+struct nc_rpc *nc_rpc_generic_xml(const char *xml_str, NC_RPC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<get-config\>
@@ -125,10 +180,11 @@
* needed NETCONF capabilities for the RPC.
*
* @param[in] source Source datastore being queried.
- * @param[in] filter Optional filter data, see nc_filter_new().
+ * @param[in] filter Optional filter data, an XML subtree or XPath expression.
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
-struct nc_rpc *nc_rpc_getconfig(NC_DATASTORE source, struct nc_filter *filter);
+struct nc_rpc *nc_rpc_getconfig(NC_DATASTORE source, const char *filter, NC_RPC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<edit-config\>
@@ -144,10 +200,11 @@
* @param[in] test_opt Optional test option.
* @param[in] error_opt Optional error option.
* @param[in] edit_content Config or URL where the config to perform is to be found.
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_edit(NC_DATASTORE target, NC_RPC_EDIT_DFLTOP default_op, NC_RPC_EDIT_TESTOPT test_opt,
- NC_RPC_EDIT_ERROPT error_opt, const char *edit_content);
+ NC_RPC_EDIT_ERROPT error_opt, const char *edit_content, NC_RPC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<copy-config\>
@@ -162,9 +219,11 @@
* @param[in] url_trg Used instead \p target if the target is an URL.
* @param[in] source Source datastore.
* @param[in] url_or_config_src Used instead \p source if the source is an URL or a config.
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
-struct nc_rpc *nc_rpc_copy(NC_DATASTORE target, const char *url_trg, NC_DATASTORE source, const char *url_or_config_src);
+struct nc_rpc *nc_rpc_copy(NC_DATASTORE target, const char *url_trg, NC_DATASTORE source,
+ const char *url_or_config_src, NC_RPC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<delete-config\>
@@ -177,9 +236,10 @@
*
* @param[in] target Target datastore to delete.
* @param[in] url Used instead \p target if the target is an URL.
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
-struct nc_rpc *nc_rpc_delete(NC_DATASTORE target, char *url);
+struct nc_rpc *nc_rpc_delete(NC_DATASTORE target, const char *url, NC_RPC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<lock\>
@@ -218,10 +278,11 @@
* check. Created object can be sent via any NETCONF session which supports all the
* needed NETCONF capabilities for the RPC.
*
- * @param[in] filter Optional filter data, see nc_filter_new().
+ * @param[in] filter Optional filter data, an XML subtree or XPath expression.
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
-struct nc_rpc *nc_rpc_get(struct nc_filter *filter);
+struct nc_rpc *nc_rpc_get(const char *filter, NC_RPC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<kill-session\>
@@ -250,9 +311,11 @@
* @param[in] confirm_timeout Optional confirm timeout.
* @param[in] persist Optional identification string of a new persistent confirmed commit.
* @param[in] persist_id Optional identification string of a persistent confirmed commit to be commited.
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
-struct nc_rpc *nc_rpc_commit(int confirmed, uint32_t confirm_timeout, const char *persist, const char *persist_id);
+struct nc_rpc *nc_rpc_commit(int confirmed, uint32_t confirm_timeout, const char *persist, const char *persist_id,
+ NC_RPC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<discard-changes\>
@@ -277,9 +340,10 @@
* needed NETCONF capabilities for the RPC.
*
* @param[in] persist_id Optional identification string of a persistent confirmed commit.
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
-struct nc_rpc *nc_rpc_cancel(const char *persist_id);
+struct nc_rpc *nc_rpc_cancel(const char *persist_id, NC_RPC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<validate\>
@@ -292,9 +356,10 @@
*
* @param[in] source Source datastore being validated.
* @param[in] url_or_config Usedn instead \p source if the source is an URL or a config.
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
-struct nc_rpc *nc_rpc_validate(NC_DATASTORE source, const char *url_or_config);
+struct nc_rpc *nc_rpc_validate(NC_DATASTORE source, const char *url_or_config, NC_RPC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<get-schema\>
@@ -308,9 +373,10 @@
* @param[in] identifier Requested model identifier.
* @param[in] version Optional model version, either YANG version (1.0/1.1) or revision date.
* @param[in] format Optional format of the model (default is YANG).
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
-struct nc_rpc *nc_rpc_getschema(const char *identifier, const char *version, const char *format);
+struct nc_rpc *nc_rpc_getschema(const char *identifier, const char *version, const char *format, NC_RPC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<create-subscription\>
@@ -322,13 +388,14 @@
* needed NETCONF capabilities for the RPC.
*
* @param[in] stream_name Optional name of a NETCONF stream to subscribe to.
- * @param[in] filter Optional filter data, see nc_filter_new().
+ * @param[in] filter Optional filter data, an XML subtree or XPath expression.
* @param[in] start_time Optional YANG datetime identifying the start of the subscription.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
+ * @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
-struct nc_rpc *nc_rpc_subscribe(const char *stream_name, struct nc_filter *filter, const char *start_time,
- const char *stop_time);
+struct nc_rpc *nc_rpc_subscribe(const char *stream_name, const char *filter, const char *start_time,
+ const char *stop_time, NC_RPC_PARAMTYPE paramtype);
/**
* @brief Free the NETCONF RPC object.
diff --git a/src/messages_p.h b/src/messages_p.h
index 599f080..8264466 100644
--- a/src/messages_p.h
+++ b/src/messages_p.h
@@ -32,51 +32,59 @@
extern const char *rpcedit_erropt2str[];
typedef enum {
- NC_RPC_SERVER, /**< server-side RPC object, see #nc_rpc_server. All other values define client-side RPC object. */
- NC_RPC_GENERIC, /**< user-defined generic RPC with content as data, see #nc_rpc_generic. */
- NC_RPC_GENERIC_XML, /**< user-defined generic RPC with content as an XML string, see #nc_rpc_generic_xml. */
+ NC_RPC_GENERIC, /**< user-defined generic RPC with content as data. */
+ NC_RPC_GENERIC_XML, /**< user-defined generic RPC with content as an XML string. */
/* ietf-netconf */
- NC_RPC_GETCONFIG, /**< \<get-config\> RPC, see #nc_rpc_getconfig. */
- NC_RPC_EDIT, /**< \<edit-config\> RPC, see #nc_rpc_edit. */
- NC_RPC_COPY, /**< \<copy-config\> RPC, see #nc_rpc_copy. */
- NC_RPC_DELETE, /**< \<delete-config\> RPC, see #nc_rpc_delete. */
- NC_RPC_LOCK, /**< \<lock\> RPC, see #nc_rpc_lock. */
- NC_RPC_UNLOCK, /**< \<unlock\> RPC, see #nc_rpc_lock. */
- NC_RPC_GET, /**< \<get\> RPC, see #nc_rpc_get. */
+ NC_RPC_GETCONFIG, /**< \<get-config\> RPC. */
+ NC_RPC_EDIT, /**< \<edit-config\> RPC. */
+ NC_RPC_COPY, /**< \<copy-config\> RPC. */
+ NC_RPC_DELETE, /**< \<delete-config\> RPC. */
+ NC_RPC_LOCK, /**< \<lock\> RPC. */
+ NC_RPC_UNLOCK, /**< \<unlock\> RPC. */
+ NC_RPC_GET, /**< \<get\> RPC. */
/* NC_RPC_CLOSE is not defined since sending \<close-session\> is done by nc_session_free() */
- NC_RPC_KILL, /**< \<kill-session\> RPC, see #nc_rpc_kill. */
- NC_RPC_COMMIT, /**< \<commit\> RPC, see #nc_rpc_commit. */
- NC_RPC_DISCARD, /**< \<discard-changes\> RPC, #nc_rpc with this type. */
- NC_RPC_CANCEL, /**< \<cancel-commit\> RPC, see #c_rpc_cancel. */
- NC_RPC_VALIDATE, /**< \<validate\> RPC, see #nc_rpc_validate. */
+ NC_RPC_KILL, /**< \<kill-session\> RPC. */
+ NC_RPC_COMMIT, /**< \<commit\> RPC. */
+ NC_RPC_DISCARD, /**< \<discard-changes\> RPC. */
+ NC_RPC_CANCEL, /**< \<cancel-commit\> RPC. */
+ NC_RPC_VALIDATE, /**< \<validate\> RPC. */
/* ietf-netconf-monitoring */
- NC_RPC_GETSCHEMA, /**< \<get-schema\> RPC, see #nc_rpc_getschema. */
+ NC_RPC_GETSCHEMA, /**< \<get-schema\> RPC. */
/* notifications */
- NC_RPC_SUBSCRIBE /**< \<create-subscription\> RPC, see #nc_rpc_subscribe. */
+ NC_RPC_SUBSCRIBE /**< \<create-subscription\> RPC. */
} NC_RPC_TYPE;
typedef enum {
- NC_REPLY_ERROR,
- NC_REPLY_OK,
- NC_REPLY_DATA,
- NC_REPLY_NOTIF
-} NC_REPLY_TYPE;
-
-struct nc_filter {
- NC_FILTER type; /**< filter type */
- int refs; /**< number of references */
- char *data; /**< filter data according to type */
-};
+ NC_ERR_EMPTY,
+ NC_ERR_IN_USE,
+ NC_ERR_INVALID_VALUE,
+ NC_ERR_TOO_BIG,
+ NC_ERR_MISSING_ATTR,
+ NC_ERR_BAD_ATTR,
+ NC_ERR_UNKNOWN_ATTR,
+ NC_ERR_MISSING_ELEM,
+ NC_ERR_BAD_ELEM,
+ NC_ERR_UNKNOWN_ELEM,
+ NC_ERR_UNKNOWN_NS,
+ NC_ERR_ACCESS_DENIED,
+ NC_ERR_LOCK_DENIED,
+ NC_ERR_RES_DENIED,
+ NC_ERR_ROLLBACK_FAILED,
+ NC_ERR_DATA_EXISTS,
+ NC_ERR_DATA_MISSING,
+ NC_ERR_OP_NOT_SUPPORTED,
+ NC_ERR_OP_FAILED,
+ NC_ERR_MALFORMED_MSG
+} NC_ERR;
struct nc_rpc {
NC_RPC_TYPE type;
};
-struct nc_rpc_server {
- NC_RPC_TYPE type; /**< NC_RPC_SERVER */
+struct nc_server_rpc {
struct lyxml_elem *root; /**< RPC element of the received XML message */
struct lyd_node *tree; /**< libyang data tree of the message (NETCONF operation) */
};
@@ -84,17 +92,20 @@
struct nc_rpc_generic {
NC_RPC_TYPE type; /**< NC_RPC_GENERIC */
struct lyd_node *data; /**< RPC data */
+ char free;
};
struct nc_rpc_generic_xml {
NC_RPC_TYPE type; /**< NC_RPC_GENERIC_XML */
char *xml_str;
+ char free;
};
struct nc_rpc_getconfig {
NC_RPC_TYPE type; /**< NC_RPC_GETCONFIG */
NC_DATASTORE source; /**< NETCONF datastore being queried */
- struct nc_filter *filter;/**< data filter */
+ char *filter; /**< either XML subtree (starts with '<') or an XPath (starts with '/' or an alpha) */
+ char free;
};
struct nc_rpc_edit {
@@ -104,6 +115,7 @@
NC_RPC_EDIT_TESTOPT test_opt;
NC_RPC_EDIT_ERROPT error_opt;
char *edit_cont; /**< either URL (starts with aplha) or config (starts with '<') */
+ char free;
};
struct nc_rpc_copy {
@@ -112,12 +124,14 @@
char *url_trg;
NC_DATASTORE source;
char *url_config_src; /**< either URL (starts with aplha) or config (starts with '<') */
+ char free;
};
struct nc_rpc_delete {
NC_RPC_TYPE type; /**< NC_RPC_DELETE */
NC_DATASTORE target;
char *url;
+ char free;
};
struct nc_rpc_lock {
@@ -127,7 +141,8 @@
struct nc_rpc_get {
NC_RPC_TYPE type; /**< NC_RPC_GET */
- struct nc_filter *filter;/**< data filter */
+ char *filter; /**< either XML subtree (starts with '<') or an XPath (starts with '/' or an alpha) */
+ char free;
};
struct nc_rpc_kill {
@@ -141,17 +156,20 @@
uint32_t confirm_timeout;
char *persist;
char *persist_id;
+ char free;
};
struct nc_rpc_cancel {
NC_RPC_TYPE type; /**< NC_RPC_CANCEL */
char *persist_id;
+ char free;
};
struct nc_rpc_validate {
NC_RPC_TYPE type; /**< NC_RPC_VALIDATE */
NC_DATASTORE source;
char *url_config_src; /**< either URL (starts with alpha) or config (starts with '<') */
+ char free;
};
struct nc_rpc_getschema {
@@ -159,43 +177,16 @@
char *identifier; /**< requested model identifier */
char *version; /**< either YANG version (1.0/1.1) or revision date */
char *format; /**< model format */
+ char free;
};
struct nc_rpc_subscribe {
NC_RPC_TYPE type; /**< NC_RPC_SUBSCRIBE */
char *stream; /**< stream name */
- struct nc_filter *filter;
+ char *filter; /**< either XML subtree (starts with '<') or an XPath (starts with '/' or an alpha) */
char *start;
char *stop;
-};
-
-struct nc_reply {
- NC_REPLY_TYPE type;
- struct lyxml_elem *root;
-};
-
-struct nc_reply_error {
- NC_REPLY_TYPE type; /**< NC_REPLY_ERROR */
- struct lyxml_elem *root;
- /* TODO */
-};
-
-struct nc_reply_ok {
- NC_REPLY_TYPE type; /**< NC_REPLY_OK */
- struct lyxml_elem *root;
- struct ly_ctx *ctx;
-};
-
-struct nc_reply_data {
- NC_REPLY_TYPE type; /**< NC_REPLY_DATA */
- struct lyxml_elem *root; /**< only the top reply element */
- struct lyd_node *data; /**< libyang data tree */
-};
-
-struct nc_notif {
- NC_REPLY_TYPE type; /**< NC_REPLY_NOTIF */
- struct lyxml_elem *root; /**< only the top notification element */
- struct lyd_node *tree; /**< libyang data tree of the message */
+ char free;
};
#endif /* NC_MESSAGES_P_H_ */
diff --git a/src/session.c b/src/session.c
index e7ca1da..1d0b3e6 100644
--- a/src/session.c
+++ b/src/session.c
@@ -41,7 +41,7 @@
static NC_MSG_TYPE nc_send_hello_(struct nc_session *session);
static NC_MSG_TYPE nc_recv_hello(struct nc_session *session);
-static NC_MSG_TYPE nc_send_rpc_(struct nc_session *session, struct lyd_node *op);
+static NC_MSG_TYPE nc_send_msg(struct nc_session *session, struct lyd_node *op);
static char *schema_searchpath = NULL;
@@ -125,7 +125,7 @@
static int
ctx_load_model(struct nc_session *session, const char *cpblt)
{
- struct lys_module *module;
+ const struct lys_module *module;
char *ptr, *ptr2;
char *model_name, *revision = NULL, *features = NULL;
@@ -153,8 +153,11 @@
revision = strndup(ptr, ptr2 - ptr);
}
- /* load module */
- module = ly_ctx_load_module(session->ctx, model_name, revision);
+ /* load module if needed */
+ module = ly_ctx_get_module(session->ctx, model_name, revision);
+ if (!module) {
+ module = ly_ctx_load_module(session->ctx, model_name, revision);
+ }
free(model_name);
free(revision);
@@ -201,20 +204,13 @@
static int
ctx_load_ietf_netconf(struct ly_ctx *ctx, const char **cpblts)
{
- int fd, i;
- struct lys_module *ietfnc;
+ int i;
+ const struct lys_module *ietfnc;
- fd = open(SCHEMAS_DIR"ietf-netconf.yin", O_RDONLY);
- if (fd < 0) {
- ERR("Loading base NETCONF schema (%s) failed (%s).", SCHEMAS_DIR"ietf-netconf", strerror(errno));
+ if (!(ietfnc = ly_ctx_load_module(ctx, "ietf-netconf", NULL))) {
+ ERR("Loading base NETCONF schema failed.");
return 1;
}
- if (!(ietfnc = lys_read(ctx, fd, LYS_IN_YIN))) {
- ERR("Loading base NETCONF schema (%s) failed.", SCHEMAS_DIR"ietf-netconf");
- close(fd);
- return 1;
- }
- close(fd);
/* set supported capabilities from ietf-netconf */
for (i = 0; cpblts[i]; ++i) {
@@ -249,14 +245,16 @@
struct nc_session *session = (struct nc_session *)user_data;
struct nc_rpc *rpc;
struct nc_reply *reply;
+ struct nc_reply_data *data_rpl;
NC_MSG_TYPE msg;
char *model_data;
+ uint64_t msgid;
/* TODO later replace with yang to reduce model size? */
- rpc = nc_rpc_getschema(name, revision, "yin");
+ rpc = nc_rpc_getschema(name, revision, "yin", NC_RPC_PARAMTYPE_CONST);
*format = LYS_IN_YIN;
- while ((msg = nc_send_rpc(session, rpc)) == NC_MSG_WOULDBLOCK) {
+ while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
usleep(1000);
}
if (msg == NC_MSG_ERROR) {
@@ -264,9 +262,9 @@
nc_rpc_free(rpc);
return NULL;
}
- nc_rpc_free(rpc);
- msg = nc_recv_reply(session, 250, &reply);
+ msg = nc_recv_reply(session, rpc, msgid, 250, &reply);
+ nc_rpc_free(rpc);
if (msg == NC_MSG_WOULDBLOCK) {
ERR("Timeout for receiving reply to a <get-schema> expired.");
return NULL;
@@ -275,8 +273,9 @@
return NULL;
}
- /* TODO get data from reply */
- model_data = NULL;
+ data_rpl = (struct nc_reply_data *)reply;
+ model_data = lyxml_serialize(((struct lyd_node_anyxml *)data_rpl->data)->value);
+ nc_reply_free(reply);
*free_model_data = NULL;
return model_data;
@@ -295,8 +294,14 @@
/* check if get-schema is supported */
for (i = 0; session->cpblts[i]; ++i) {
if (!strncmp(session->cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", 51)) {
- old_clb = ly_ctx_get_module_clb(session->ctx, &old_data);
- ly_ctx_set_module_clb(session->ctx, &libyang_module_clb, session);
+ /* it is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
+ if (ly_ctx_load_module(session->ctx, "ietf-netconf-monitoring", NULL)) {
+ /* set module retrieval using <get-schema> */
+ old_clb = ly_ctx_get_module_clb(session->ctx, &old_data);
+ ly_ctx_set_module_clb(session->ctx, &libyang_module_clb, session);
+ } else {
+ WRN("Loading NETCONF monitoring schema failed, cannot use <get-schema>.");
+ }
break;
}
}
@@ -370,6 +375,7 @@
if (nc_handshake(session)) {
goto fail;
}
+ session->status = NC_STATUS_RUNNING;
/* check/fill libyang context */
if (session->flags & NC_SESSION_SHAREDCTX) {
@@ -382,7 +388,6 @@
}
}
- session->status = NC_STATUS_RUNNING;
return session;
fail:
@@ -451,11 +456,10 @@
int r, i;
int multisession = 0; /* flag for more NETCONF session on a single SSH session */
struct nc_session *siter;
- struct nc_notif_cont *ntfiter;
- struct nc_reply_cont *rpliter;
+ struct nc_msg_cont *contiter;
struct lyxml_elem *rpl, *child;
struct lyd_node *close_rpc;
- struct lys_module *ietfnc;
+ const struct lys_module *ietfnc;
void *p;
if (!session || session->status == NC_STATUS_CLOSING) {
@@ -481,30 +485,30 @@
if (session->side == NC_CLIENT && session->status == NC_STATUS_RUNNING) {
/* cleanup message queues */
/* notifications */
- for (ntfiter = session->notifs; ntfiter; ) {
- nc_notif_free(ntfiter->msg);
+ for (contiter = session->notifs; contiter; ) {
+ lyxml_free(session->ctx, contiter->msg);
- p = ntfiter;
- ntfiter = ntfiter->next;
+ p = contiter;
+ contiter = contiter->next;
free(p);
}
/* rpc replies */
- for (rpliter = session->replies; rpliter; ) {
- nc_reply_free(rpliter->msg);
+ for (contiter = session->replies; contiter; ) {
+ lyxml_free(session->ctx, contiter->msg);
- p = rpliter;
- rpliter = rpliter->next;
+ p = contiter;
+ contiter = contiter->next;
free(p);
}
/* send closing info to the other side */
ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
if (!ietfnc) {
- WRN("%s: Missing ietf-netconf schema in context (session %u), unable to send <close-session\\>", session->id);
+ WRN("%s: Missing ietf-netconf schema in context (session %u), unable to send <close-session\\>", __func__, session->id);
} else {
close_rpc = lyd_new(NULL, ietfnc, "close-session");
- nc_send_rpc_(session, close_rpc);
+ nc_send_msg(session, close_rpc);
lyd_free(close_rpc);
switch (nc_read_msg(session, 200, &rpl)) {
case NC_MSG_REPLY:
@@ -516,7 +520,7 @@
if (!child) {
WRN("The reply to <close-session\\> was not <ok\\> as expected.");
}
- lyxml_free_elem(session->ctx, rpl);
+ lyxml_free(session->ctx, rpl);
break;
case NC_MSG_WOULDBLOCK:
WRN("Timeout for receiving a reply to <close-session\\> elapsed.");
@@ -742,19 +746,19 @@
}
/* cleanup */
- lyxml_free_elem(session->ctx, xml);
+ lyxml_free(session->ctx, xml);
return msgtype;
error:
/* cleanup */
- lyxml_free_elem(session->ctx, xml);
+ lyxml_free(session->ctx, xml);
return NC_MSG_ERROR;
}
-API NC_MSG_TYPE
-nc_recv_rpc(struct nc_session *session, int timeout, struct nc_rpc_server **rpc)
+NC_MSG_TYPE
+nc_recv_rpc(struct nc_session *session, int timeout, struct nc_server_rpc **rpc)
{
int r;
struct lyxml_elem *xml = NULL;
@@ -783,9 +787,8 @@
switch(msgtype) {
case NC_MSG_RPC:
*rpc = malloc(sizeof **rpc);
- (*rpc)->type = NC_RPC_SERVER;
(*rpc)->tree = lyd_parse_xml(session->ctx, xml, LYD_OPT_DESTRUCT);
- (*rpc)->root = xml;
+ lyxml_free(session->ctx, xml);
break;
case NC_MSG_HELLO:
ERR("SESSION %u: Received another <hello> message.", session->id);
@@ -808,78 +811,407 @@
error:
/* cleanup */
- lyxml_free_elem(session->ctx, xml);
+ lyxml_free(session->ctx, xml);
return NC_MSG_ERROR;
}
-static struct nc_reply *
-parse_reply(struct ly_ctx *ctx, struct lyxml_elem *xml)
+static NC_MSG_TYPE
+get_msg(struct nc_session *session, int timeout, uint64_t msgid, struct lyxml_elem **msg)
{
- struct lyxml_elem *iter;
- struct nc_reply_error *error;
- struct nc_reply_ok *ok;
- struct nc_reply_data *data;
- struct nc_reply *reply;
+ int r;
+ char *ptr;
+ const char *str_msgid;
+ uint64_t cur_msgid;
+ struct lyxml_elem *xml;
+ struct nc_msg_cont *cont, *prev_cont, **cont_ptr;
+ NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
+
+next_message:
+ if (msgtype) {
+ /* second run, wait and give a chance to nc_recv_reply() */
+ usleep(TIMEOUT_STEP);
+ timeout = timeout - (TIMEOUT_STEP);
+ }
+ r = session_ti_lock(session, timeout);
+ if (r > 0) {
+ /* error */
+ return NC_MSG_ERROR;
+ } else if (r < 0) {
+ /* timeout */
+ return NC_MSG_WOULDBLOCK;
+ }
+
+ /* try to get notification from the session's queue */
+ if (!msgid && session->notifs) {
+ cont = session->notifs;
+ session->notifs = cont->next;
+
+ session_ti_unlock(session);
+
+ *msg = cont->msg;
+ free(cont);
+
+ return NC_MSG_NOTIF;
+ }
+
+ /* try to get rpc-reply from the session's queue */
+ if (msgid && session->replies) {
+ prev_cont = NULL;
+ for (cont = session->replies; cont; cont = cont->next) {
+ /* errors checked in the condition below */
+ str_msgid = lyxml_get_attr(cont->msg, "message-id", NULL);
+ cur_msgid = strtoul(str_msgid, &ptr, 10);
+
+ if (cur_msgid == msgid) {
+ if (!prev_cont) {
+ session->replies = cont->next;
+ } else {
+ prev_cont->next = cont->next;
+ }
+ session_ti_unlock(session);
+
+ *msg = cont->msg;
+ free(cont);
+
+ return NC_MSG_REPLY;
+ }
+
+ prev_cont = cont;
+ }
+ }
+
+ /* read message from wire */
+ msgtype = nc_read_msg(session, timeout, &xml);
+
+ /* we read rpc-reply, want a notif */
+ if (!msgid && (msgtype == NC_MSG_REPLY)) {
+ /* just check that message-id is fine */
+ str_msgid = lyxml_get_attr(xml, "message-id", NULL);
+ if (!str_msgid) {
+ session_ti_unlock(session);
+ ERR("SESSION %u: Received a <rpc-reply> with no message-id, discarding.", session->id);
+ lyxml_free(session->ctx, xml);
+ goto next_message;
+ }
+ cur_msgid = strtoul(str_msgid, &ptr, 10);
+ if (ptr[0]) {
+ session_ti_unlock(session);
+ ERR("SESSION %u: Received a <rpc-reply> with an invalid message-id (\"%s\"), discarding.", session->id, str_msgid);
+ lyxml_free(session->ctx, xml);
+ goto next_message;
+ }
+
+ cont_ptr = &session->replies;
+ while (*cont_ptr) {
+ cont_ptr = &((*cont_ptr)->next);
+ }
+ *cont_ptr = malloc(sizeof **cont_ptr);
+ (*cont_ptr)->msg = xml;
+ (*cont_ptr)->next = NULL;
+ }
+
+ /* we read notif, want a rpc-reply */
+ if (msgid && (msgtype == NC_MSG_NOTIF)) {
+ if (!session->notif) {
+ session_ti_unlock(session);
+ ERR("SESSION %u: Received a <notification> but session is not subscribed.", session->id);
+ lyxml_free(session->ctx, xml);
+ goto next_message;
+ }
+
+ cont_ptr = &session->notifs;
+ while (*cont_ptr) {
+ cont_ptr = &((*cont_ptr)->next);
+ }
+ *cont_ptr = malloc(sizeof **cont_ptr);
+ (*cont_ptr)->msg = xml;
+ (*cont_ptr)->next = NULL;
+ }
+
+ session_ti_unlock(session);
+
+ switch (msgtype) {
+ case NC_MSG_NOTIF:
+ /* we want a rpc-reply */
+ if (msgid) {
+ goto next_message;
+ }
+ *msg = xml;
+ break;
+
+ case NC_MSG_REPLY:
+ /* we want a notif */
+ if (!msgid) {
+ goto next_message;
+ }
+ *msg = xml;
+ break;
+
+ case NC_MSG_HELLO:
+ ERR("SESSION %u: Received another <hello> message.", session->id);
+ lyxml_free(session->ctx, xml);
+ goto next_message;
+
+ case NC_MSG_RPC:
+ ERR("SESSION %u: Received <rpc> from NETCONF server.", session->id);
+ lyxml_free(session->ctx, xml);
+ goto next_message;
+
+ default:
+ /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
+ * NC_MSG_NONE is not returned by nc_read_msg()
+ */
+ break;
+ }
+
+ return msgtype;
+}
+
+/* cannot strictly fail, but does not need to fill any error parameter at all */
+static void
+parse_rpc_error(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_err *err)
+{
+ struct lyxml_elem *iter, *next, *info;
LY_TREE_FOR(xml->child, iter) {
- if (!iter->ns || strcmp(iter->ns->value, NC_NS_BASE)) {
+ if (!iter->ns) {
+ if (iter->content) {
+ WRN("<rpc-error> child \"%s\" with value \"%s\" without namespace.", iter->name, iter->content);
+ } else {
+ WRN("<rpc-error> child \"%s\" without namespace.", iter->name);
+ }
+ continue;
+ } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
+ if (iter->content) {
+ WRN("<rpc-error> child \"%s\" with value \"%s\" in an unknown namespace \"%s\".",
+ iter->name, iter->content, iter->ns->value);
+ } else {
+ WRN("<rpc-error> child \"%s\" in an unknown namespace \"%s\".", iter->name, iter->ns->value);
+ }
continue;
}
- if (!strcmp(iter->name, "ok")) {
- if (reply) {
- ERR("Unexpected content of the <rpc-reply>.");
- goto error;
+ if (!strcmp(iter->name, "error-type")) {
+ if (!iter->content || (strcmp(iter->content, "transport") && strcmp(iter->content, "rpc")
+ && strcmp(iter->content, "protocol") && strcmp(iter->content, "application"))) {
+ WRN("<rpc-error> <error-type> unknown value \"%s\".", (iter->content ? iter->content : ""));
+ } else if (err->type) {
+ WRN("<rpc-error> <error-type> duplicated.");
+ } else {
+ err->type = lydict_insert(ctx, iter->content, 0);
}
- ok = malloc(sizeof *ok);
- ok->type = NC_REPLY_OK;
- ok->ctx = ctx;
- ok->root = xml;
- reply = (struct nc_reply *)ok;
- } else if (!strcmp(iter->name, "data")) {
- if (reply) {
- ERR("Unexpected content of the <rpc-reply>.");
- goto error;
+ } else if (!strcmp(iter->name, "error-tag")) {
+ if (!iter->content || (strcmp(iter->content, "in-use") && strcmp(iter->content, "invalid-value")
+ && strcmp(iter->content, "too-big") && strcmp(iter->content, "missing-attribute")
+ && strcmp(iter->content, "bad-attribute") && strcmp(iter->content, "unknown-attribute")
+ && strcmp(iter->content, "missing-element") && strcmp(iter->content, "bad-element")
+ && strcmp(iter->content, "unknown-element") && strcmp(iter->content, "unknown-namespace")
+ && strcmp(iter->content, "access-denied") && strcmp(iter->content, "lock-denied")
+ && strcmp(iter->content, "resource-denied") && strcmp(iter->content, "rollback-failed")
+ && strcmp(iter->content, "data-exists") && strcmp(iter->content, "data-missing")
+ && strcmp(iter->content, "operation-not-supported") && strcmp(iter->content, "operation-failed")
+ && strcmp(iter->content, "malformed-message"))) {
+ WRN("<rpc-error> <error-tag> unknown value \"%s\".", (iter->content ? iter->content : ""));
+ } else if (err->tag) {
+ WRN("<rpc-error> <error-tag> duplicated.");
+ } else {
+ err->tag = lydict_insert(ctx, iter->content, 0);
}
- data = malloc(sizeof *data);
- data->type = NC_REPLY_DATA;
- data->root = xml;
- data->data = lyd_parse_xml(ctx, iter, LYD_OPT_DESTRUCT);
- reply = (struct nc_reply *)data;
- } else if (!strcmp(iter->name, "rpc-error")) {
- if (reply && (reply->type != NC_REPLY_ERROR)) {
- ERR("<rpc-reply> content mismatch.");
- goto error;
+ } else if (!strcmp(iter->name, "error-severity")) {
+ if (!iter->content || (strcmp(iter->content, "error") && strcmp(iter->content, "warning"))) {
+ WRN("<rpc-error> <error-severity> unknown value \"%s\".", (iter->content ? iter->content : ""));
+ } else if (err->severity) {
+ WRN("<rpc-error> <error-severity> duplicated.");
+ } else {
+ err->severity = lydict_insert(ctx, iter->content, 0);
}
- /* TODO */
- error = malloc(sizeof *error);
- error->type = NC_REPLY_ERROR;
- reply = (struct nc_reply *)error;
+ } else if (!strcmp(iter->name, "error-app-tag")) {
+ if (err->apptag) {
+ WRN("<rpc-error> <error-app-tag> duplicated.");
+ } else {
+ err->apptag = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
+ }
+ } else if (!strcmp(iter->name, "error-path")) {
+ if (err->path) {
+ WRN("<rpc-error> <error-path> duplicated.");
+ } else {
+ err->path = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
+ }
+ } else if (!strcmp(iter->name, "error-message")) {
+ if (err->message) {
+ WRN("<rpc-error> <error-message> duplicated.");
+ } else {
+ err->message_lang = lyxml_get_attr(iter, "xml:lang", NULL);
+ if (!err->message_lang) {
+ VRB("<rpc-error> <error-message> without the recommended \"xml:lang\" attribute.");
+ }
+ err->message = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
+ }
+ } else if (!strcmp(iter->name, "error-info")) {
+ LY_TREE_FOR_SAFE(iter->child, next, info) {
+ if (info->ns && !strcmp(info->ns->value, NC_NS_BASE)) {
+ if (!strcmp(info->name, "session-id")) {
+ if (err->sid) {
+ WRN("<rpc-error> <error-info> <session-id> duplicated.");
+ } else {
+ err->sid = lydict_insert(ctx, (info->content ? info->content : ""), 0);
+ }
+ } else if (!strcmp(info->name, "bad-attr")) {
+ ++err->attr_count;
+ err->attr = realloc(err->attr, err->attr_count * sizeof *err->attr);
+ err->attr[err->attr_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
+ } else if (!strcmp(info->name, "bad-element")) {
+ ++err->elem_count;
+ err->elem = realloc(err->elem, err->elem_count * sizeof *err->elem);
+ err->elem[err->elem_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
+ } else if (!strcmp(info->name, "bad-namespace")) {
+ ++err->ns_count;
+ err->ns = realloc(err->ns, err->ns_count * sizeof *err->ns);
+ err->ns[err->ns_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
+ } else {
+ if (info->content) {
+ WRN("<rpc-error> <error-info> unknown child \"%s\" with value \"%s\".",
+ info->name, info->content);
+ } else {
+ WRN("<rpc-error> <error-info> unknown child \"%s\".", info->name);
+ }
+ }
+ } else {
+ lyxml_unlink(ctx, info);
+ ++err->other_count;
+ err->other = realloc(err->other, err->other_count * sizeof *err->other);
+ err->other[err->other_count - 1] = info;
+ }
+ }
+ } else {
+ if (iter->content) {
+ WRN("<rpc-error> unknown child \"%s\" with value \"%s\".", iter->name, iter->content);
+ } else {
+ WRN("<rpc-error> unknown child \"%s\".", iter->name);
+ }
}
}
+}
- if (!reply) {
- ERR("Invalid content of the <rpc-reply>.");
+static struct nc_reply *
+parse_reply(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_rpc *rpc)
+{
+ struct lyxml_elem *iter;
+ const struct lys_node *schema;
+ struct lyd_node *data;
+ struct nc_reply_error *error_rpl;
+ struct nc_reply_data *data_rpl;
+ struct nc_reply *reply = NULL;
+ int i;
+
+ if (!xml->child) {
+ ERR("An empty <rpc-reply>.");
+ return NULL;
}
+
+ /* rpc-error */
+ if (!strcmp(xml->child->name, "rpc-error") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
+ /* count and check elements */
+ i = 0;
+ LY_TREE_FOR(xml->child, iter) {
+ if (strcmp(iter->name, "rpc-error")) {
+ ERR("<rpc-reply> content mismatch (<rpc-error> and <%s>).", iter->name);
+ return NULL;
+ } else if (!iter->ns) {
+ ERR("<rpc-reply> content mismatch (<rpc-error> without namespace).");
+ return NULL;
+ } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
+ ERR("<rpc-reply> content mismatch (<rpc-error> with NS \"%s\").", iter->ns->value);
+ return NULL;
+ }
+ ++i;
+ }
+
+ error_rpl = malloc(sizeof *error_rpl);
+ error_rpl->type = NC_REPLY_ERROR;
+ error_rpl->ctx = ctx;
+ error_rpl->err = calloc(i, sizeof *error_rpl->err);
+ error_rpl->err_count = i;
+ reply = (struct nc_reply *)error_rpl;
+
+ i = 0;
+ LY_TREE_FOR(xml->child, iter) {
+ parse_rpc_error(ctx, iter, error_rpl->err + i);
+ ++i;
+ }
+
+ /* ok */
+ } else if (!strcmp(xml->child->name, "ok") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
+ if (xml->child->next) {
+ ERR("<rpc-reply> content mismatch (<ok> and <%s>).", xml->child->next->name);
+ return NULL;
+ }
+ reply = malloc(sizeof *reply);
+ reply->type = NC_REPLY_OK;
+
+ /* some RPC output */
+ } else {
+ switch (rpc->type) {
+ case NC_RPC_GENERIC:
+ schema = ((struct nc_rpc_generic *)rpc)->data->schema;
+ break;
+ case NC_RPC_GENERIC_XML:
+ data = lyd_parse(ctx, ((struct nc_rpc_generic_xml *)rpc)->xml_str, LYD_XML, 0);
+ if (!data) {
+ ERR("Failed to parse a generic RPC XML.");
+ return NULL;
+ }
+ schema = data->schema;
+ lyd_free(data);
+ break;
+ case NC_RPC_GETCONFIG:
+ schema = ly_ctx_get_node(ctx, "/ietf-netconf:get-config");
+ break;
+ case NC_RPC_GET:
+ schema = ly_ctx_get_node(ctx, "/ietf-netconf:get");
+ break;
+ case NC_RPC_GETSCHEMA:
+ schema = ly_ctx_get_node(ctx, "/ietf-netconf-monitoring:get-schema");
+ break;
+ case NC_RPC_EDIT:
+ case NC_RPC_COPY:
+ case NC_RPC_DELETE:
+ case NC_RPC_LOCK:
+ case NC_RPC_UNLOCK:
+ case NC_RPC_KILL:
+ case NC_RPC_COMMIT:
+ case NC_RPC_DISCARD:
+ case NC_RPC_CANCEL:
+ case NC_RPC_VALIDATE:
+ case NC_RPC_SUBSCRIBE:
+ /* there is no output defined */
+ break;
+ }
+
+ if (!schema) {
+ ERR("%s: internal error (%s:%d).", __func__, __FILE__, __LINE__);
+ return NULL;
+ }
+
+ data_rpl = malloc(sizeof *data_rpl);
+ data_rpl->type = NC_REPLY_DATA;
+ data_rpl->data = lyd_parse_output_xml(schema, xml, LYD_OPT_DESTRUCT);
+ if (!data_rpl->data) {
+ ERR("Failed to parse <rpc-reply>.");
+ free(data_rpl);
+ return NULL;
+ }
+ reply = (struct nc_reply *)data_rpl;
+ }
+
return reply;
-
-error:
- if (reply) {
- reply->root = NULL;
- nc_reply_free(reply);
- }
- return NULL;
}
API NC_MSG_TYPE
-nc_recv_reply(struct nc_session *session, int timeout, struct nc_reply **reply)
+nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct nc_reply **reply)
{
- int r;
struct lyxml_elem *xml;
- struct nc_reply_cont *cont_r;
- struct nc_notif_cont **cont_n;
- struct nc_notif *notif;
NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
if (!session || !reply) {
@@ -891,102 +1223,25 @@
}
*reply = NULL;
- do {
- if (msgtype && session->notif) {
- /* second run, wait and give a chance to nc_recv_notif() */
- usleep(TIMEOUT_STEP);
- timeout = timeout - (TIMEOUT_STEP);
- }
- r = session_ti_lock(session, timeout);
- if (r > 0) {
- /* error */
+ msgtype = get_msg(session, timeout, msgid, &xml);
+ if (msgtype == NC_MSG_WOULDBLOCK) {
+ return NC_MSG_WOULDBLOCK;
+ }
+
+ if (msgtype == NC_MSG_REPLY) {
+ *reply = parse_reply(session->ctx, xml, rpc);
+ if (!(*reply)) {
return NC_MSG_ERROR;
- } else if (r < 0) {
- /* timeout */
- return NC_MSG_WOULDBLOCK;
}
-
- /* try to get message from the session's queue */
- if (session->notifs) {
- cont_r = session->replies;
- session->replies = cont_r->next;
-
- session_ti_unlock(session);
-
- *reply = cont_r->msg;
- free(cont_r);
-
- return NC_MSG_REPLY;
- }
-
- /* read message from wire */
- msgtype = nc_read_msg(session, timeout, &xml);
- if (msgtype == NC_MSG_NOTIF) {
- if (!session->notif) {
- session_ti_unlock(session);
- ERR("SESSION %u: Received Notification but session is not subscribed.", session->id);
- goto error;
- }
-
- /* create notification object */
- notif = malloc(sizeof *notif);
- notif->tree = lyd_parse_xml(session->ctx, xml, LYD_OPT_DESTRUCT);
- notif->root = xml;
-
- /* store the message for nc_recv_notif() */
- cont_n = &session->notifs;
- while(*cont_n) {
- cont_n = &((*cont_n)->next);
- }
- *cont_n = malloc(sizeof **cont_n);
- (*cont_n)->msg = notif;
- (*cont_n)->next = NULL;
- }
-
- session_ti_unlock(session);
-
- switch(msgtype) {
- case NC_MSG_REPLY:
- /* distinguish between data / ok / error reply */
- *reply = parse_reply(session->ctx, xml);
- if (!(*reply)) {
- goto error;
- }
- break;
- case NC_MSG_HELLO:
- ERR("SESSION %u: Received another <hello> message.", session->id);
- goto error;
- case NC_MSG_RPC:
- ERR("SESSION %u: Received <rpc> from NETCONF server.", session->id);
- goto error;
- default:
- /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
- * NC_MSG_NOTIF already handled before the switch;
- * NC_MSG_NONE is not returned by nc_read_msg()
- */
- break;
- }
-
- } while(msgtype == NC_MSG_NOTIF);
+ }
return msgtype;
-
-error:
-
- /* cleanup */
- lyxml_free_elem(session->ctx, xml);
-
- return NC_MSG_ERROR;
}
API NC_MSG_TYPE
nc_recv_notif(struct nc_session *session, int timeout, struct nc_notif **notif)
{
- int r;
struct lyxml_elem *xml;
- struct nc_notif_cont *cont_n;
- struct nc_reply_cont **cont_r;
- struct nc_reply *reply;
NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
if (!session || !notif) {
@@ -997,85 +1252,18 @@
return NC_MSG_ERROR;
}
- do {
- if (msgtype) {
- /* second run, wait and give a chance to nc_recv_reply() */
- usleep(TIMEOUT_STEP);
- timeout = timeout - (TIMEOUT_STEP);
- }
- r = session_ti_lock(session, timeout);
- if (r > 0) {
- /* error */
- return NC_MSG_ERROR;
- } else if (r < 0) {
- /* timeout */
- return NC_MSG_WOULDBLOCK;
- }
+ msgtype = get_msg(session, timeout, 0, &xml);
+ if (msgtype == NC_MSG_WOULDBLOCK) {
+ return NC_MSG_WOULDBLOCK;
+ }
- /* try to get message from the session's queue */
- if (session->notifs) {
- cont_n = session->notifs;
- session->notifs = cont_n->next;
-
- session_ti_unlock(session);
-
- *notif = cont_n->msg;
- free(cont_n);
-
- return NC_MSG_NOTIF;
- }
-
- /* read message from wire */
- msgtype = nc_read_msg(session, timeout, &xml);
- if (msgtype == NC_MSG_REPLY) {
- /* distinguish between data / ok / error reply */
- reply = parse_reply(session->ctx, xml);
- if (!reply) {
- goto error;
- }
-
- /* store the message for nc_recv_reply() */
- cont_r = &session->replies;
- while(*cont_r) {
- cont_r = &((*cont_r)->next);
- }
- *cont_r = malloc(sizeof **cont_r);
- (*cont_r)->msg = reply;
- (*cont_r)->next = NULL;
- }
-
- session_ti_unlock(session);
-
- switch(msgtype) {
- case NC_MSG_NOTIF:
- *notif = malloc(sizeof **notif);
- (*notif)->tree = lyd_parse_xml(session->ctx, xml, LYD_OPT_DESTRUCT);
- (*notif)->root = xml;
- break;
- case NC_MSG_HELLO:
- ERR("SESSION %u: Received another <hello> message.", session->id);
- goto error;
- case NC_MSG_RPC:
- ERR("SESSION %u: Received <rpc> from NETCONF server.", session->id);
- goto error;
- default:
- /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
- * NC_MSG_REPLY already handled before the switch;
- * NC_MSG_NONE is not returned by nc_read_msg()
- */
- break;
- }
-
- } while(msgtype == NC_MSG_NOTIF);
+ if (msgtype == NC_MSG_NOTIF) {
+ *notif = malloc(sizeof **notif);
+ (*notif)->tree = lyd_parse_xml(session->ctx, xml, LYD_OPT_DESTRUCT);
+ lyxml_free(session->ctx, xml);
+ }
return msgtype;
-
-error:
-
- /* cleanup */
- lyxml_free_elem(session->ctx, xml);
-
- return NC_MSG_ERROR;
}
static NC_MSG_TYPE
@@ -1104,7 +1292,7 @@
}
static NC_MSG_TYPE
-nc_send_rpc_(struct nc_session *session, struct lyd_node *op)
+nc_send_msg(struct nc_session *session, struct lyd_node *op)
{
int r;
@@ -1124,7 +1312,7 @@
}
API NC_MSG_TYPE
-nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc)
+nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
{
NC_MSG_TYPE r;
struct nc_rpc_generic *rpc_gen;
@@ -1142,10 +1330,11 @@
struct nc_rpc_getschema *rpc_gs;
struct nc_rpc_subscribe *rpc_sub;
struct lyd_node *data, *node;
- struct lys_module *ietfnc, *ietfncmon, *notifs;
+ const struct lys_module *ietfnc, *ietfncmon, *notifs;
char str[11];
+ uint64_t cur_msgid;
- if (!session || !rpc || rpc->type == NC_RPC_SERVER) {
+ if (!session || !rpc) {
ERR("%s: Invalid parameter", __func__);
return NC_MSG_ERROR;
} else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
@@ -1186,14 +1375,13 @@
return NC_MSG_ERROR;
}
if (rpc_gc->filter) {
- if (rpc_gc->filter->type == NC_FILTER_SUBTREE) {
- node = lyd_new_anyxml(data, ietfnc, "filter", rpc_gc->filter->data);
+ if (rpc_gc->filter[0] == '<') {
+ node = lyd_new_anyxml(data, ietfnc, "filter", rpc_gc->filter);
lyd_insert_attr(node, "type", "subtree");
- } else if (rpc_gc->filter->type == NC_FILTER_XPATH) {
+ } else {
node = lyd_new_anyxml(data, ietfnc, "filter", NULL);
- /* TODO - handle namespaces from XPATH query */
lyd_insert_attr(node, "type", "xpath");
- lyd_insert_attr(node, "select", rpc_gc->filter->data);
+ lyd_insert_attr(node, "select", rpc_gc->filter);
}
}
break;
@@ -1320,14 +1508,13 @@
data = lyd_new(NULL, ietfnc, "get");
if (rpc_g->filter) {
- if (rpc_g->filter->type == NC_FILTER_SUBTREE) {
- node = lyd_new_anyxml(data, ietfnc, "filter", rpc_g->filter->data);
+ if (rpc_g->filter[0] == '<') {
+ node = lyd_new_anyxml(data, ietfnc, "filter", rpc_g->filter);
lyd_insert_attr(node, "type", "subtree");
- } else if (rpc_g->filter->type == NC_FILTER_XPATH) {
+ } else {
node = lyd_new_anyxml(data, ietfnc, "filter", NULL);
- /* TODO - handle namespaces from XPATH query */
lyd_insert_attr(node, "type", "xpath");
- lyd_insert_attr(node, "select", rpc_g->filter->data);
+ lyd_insert_attr(node, "select", rpc_g->filter);
}
if (!node) {
lyd_free(data);
@@ -1460,14 +1647,13 @@
}
if (rpc_sub->filter) {
- if (rpc_sub->filter->type == NC_FILTER_SUBTREE) {
- node = lyd_new_anyxml(data, notifs, "filter", rpc_sub->filter->data);
+ if (rpc_sub->filter[0] == '<') {
+ node = lyd_new_anyxml(data, notifs, "filter", rpc_sub->filter);
lyd_insert_attr(node, "type", "subtree");
- } else if (rpc_sub->filter->type == NC_FILTER_XPATH) {
+ } else {
node = lyd_new_anyxml(data, notifs, "filter", NULL);
- /* TODO - handle namespaces from XPATH query */
lyd_insert_attr(node, "type", "xpath");
- lyd_insert_attr(node, "select", rpc_sub->filter->data);
+ lyd_insert_attr(node, "select", rpc_sub->filter);
}
if (!node) {
lyd_free(data);
@@ -1491,10 +1677,6 @@
}
}
break;
-
- case NC_RPC_SERVER:
- ERR("Internal (%s:%d)", __FILE__, __LINE__);
- return NC_MSG_ERROR;
}
if (lyd_validate(data, LYD_OPT_STRICT)) {
@@ -1502,16 +1684,23 @@
return NC_MSG_ERROR;
}
- r = session_ti_lock(session, 0);
+ r = session_ti_lock(session, timeout);
if (r != 0) {
/* error or blocking */
r = NC_MSG_WOULDBLOCK;
} else {
- /* send RPC */
- r = nc_send_rpc_(session, data);
+ /* send RPC, store its message ID */
+ r = nc_send_msg(session, data);
+ cur_msgid = session->msgid;
}
session_ti_unlock(session);
lyd_free(data);
- return r;
+
+ if (r != NC_MSG_RPC) {
+ return r;
+ }
+
+ *msgid = cur_msgid;
+ return NC_MSG_RPC;
}
diff --git a/src/session.h b/src/session.h
index e2bca47..330aed1 100644
--- a/src/session.h
+++ b/src/session.h
@@ -295,19 +295,6 @@
void nc_session_free(struct nc_session *session);
/**
- * @brief Receive NETCONF RPC.
- *
- * @param[in] session NETCONF session from which the function gets data. It must be the
- * server side session object.
- * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite
- * waiting and 0 for immediate return if data are not available on wire.
- * @param[out] notif Resulting object of NETCONF RPC.
- * @return NC_MSG_RPC for success, NC_MSG_WOULDBLOCK if timeout reached and NC_MSG_ERROR
- * when reading has failed.
- */
-NC_MSG_TYPE nc_recv_rpc(struct nc_session* session, int timeout, struct nc_rpc_server **rpc);
-
-/**
* @brief Receive NETCONF RPC reply.
*
* @param[in] session NETCONF session from which the function gets data. It must be the
@@ -318,7 +305,8 @@
* @return NC_MSG_REPLY for success, NC_MSG_WOULDBLOCK if timeout reached and NC_MSG_ERROR
* when reading has failed.
*/
-NC_MSG_TYPE nc_recv_reply(struct nc_session* session, int timeout, struct nc_reply **reply);
+NC_MSG_TYPE nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout,
+ struct nc_reply **reply);
/**
* @brief Receive NETCONF Notification.
@@ -342,6 +330,6 @@
* @return #NC_MSG_RPC on success, #NC_MSG_WOULDBLOCK in case of busy session
* (try to repeat the function call) and #NC_MSG_ERROR in case of error.
*/
-NC_MSG_TYPE nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc);
+NC_MSG_TYPE nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid);
#endif /* NC_SESSION_H_ */
diff --git a/src/session_p.h b/src/session_p.h
index c5eee5b..5b68ffb 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -113,19 +113,11 @@
#define NC_VERSION_10_ENDTAG_LEN 6
/**
- * @brief Container to serialize RPC reply objects
+ * @brief Container to serialize PRC messages
*/
-struct nc_reply_cont {
- struct nc_reply *msg;
- struct nc_reply_cont *next;
-};
-
-/**
- * @brief Container to serialize Notification objects
- */
-struct nc_notif_cont {
- struct nc_notif *msg;
- struct nc_notif_cont *next;
+struct nc_msg_cont {
+ struct lyxml_elem *msg;
+ struct nc_msg_cont *next;
};
/**
@@ -174,8 +166,8 @@
/* client side only data */
uint64_t msgid;
const char **cpblts; /**< list of server's capabilities on client side */
- struct nc_reply_cont *replies; /**< queue for RPC replies received instead of notifications */
- struct nc_notif_cont *notifs; /**< queue for notifications received instead of RPC reply */
+ struct nc_msg_cont *replies; /**< queue for RPC replies received instead of notifications */
+ struct nc_msg_cont *notifs; /**< queue for notifications received instead of RPC reply */
};
/**
diff --git a/src/session_ssh.c b/src/session_ssh.c
index 382cf9f..cea2d95 100644
--- a/src/session_ssh.c
+++ b/src/session_ssh.c
@@ -911,6 +911,7 @@
if (nc_handshake(session)) {
goto fail;
}
+ session->status = NC_STATUS_RUNNING;
/* check/fill libyang context */
if (session->flags & NC_SESSION_SHAREDCTX) {
@@ -928,7 +929,6 @@
session->port = port;
session->username = lydict_insert(ctx, username, 0);
- session->status = NC_STATUS_RUNNING;
return session;
fail:
@@ -1036,6 +1036,7 @@
if (nc_handshake(session)) {
goto fail;
}
+ session->status = NC_STATUS_RUNNING;
/* check/fill libyang context */
if (session->flags & NC_SESSION_SHAREDCTX) {
@@ -1059,7 +1060,6 @@
session->username = lydict_insert_zc(ctx, username);
}
- session->status = NC_STATUS_RUNNING;
return session;
fail:
@@ -1113,6 +1113,7 @@
if (nc_handshake(new_session)) {
goto fail;
}
+ new_session->status = NC_STATUS_RUNNING;
/* check/fill libyang context */
if (session->flags & NC_SESSION_SHAREDCTX) {
@@ -1130,8 +1131,6 @@
session->port = session->port;
session->username = lydict_insert(ctx, session->username, 0);
- new_session->status = NC_STATUS_RUNNING;
-
pthread_mutex_unlock(new_session->ti_lock);
/* append to the session ring list */
diff --git a/src/session_tls.c b/src/session_tls.c
index c195907..b2c8534 100644
--- a/src/session_tls.c
+++ b/src/session_tls.c
@@ -351,6 +351,7 @@
if (nc_handshake(session)) {
goto fail;
}
+ session->status = NC_STATUS_RUNNING;
/* check/fill libyang context */
if (session->flags & NC_SESSION_SHAREDCTX) {
@@ -368,7 +369,6 @@
session->port = port;
session->username = lydict_insert(ctx, username, 0);
- session->status = NC_STATUS_RUNNING;
return session;
fail:
@@ -419,6 +419,7 @@
if (nc_handshake(session)) {
goto fail;
}
+ session->status = NC_STATUS_RUNNING;
/* check/fill libyang context */
if (session->flags & NC_SESSION_SHAREDCTX) {
@@ -431,7 +432,6 @@
}
}
- session->status = NC_STATUS_RUNNING;
return session;
fail: