session UPDATE automatic schema-mount support
diff --git a/src/session_client.c b/src/session_client.c
index fd4c5e3..676df95 100644
--- a/src/session_client.c
+++ b/src/session_client.c
@@ -217,8 +217,32 @@
     pthread_setspecific(nc_client_context_key, new);
 }
 
+/**
+ * @brief Ext data callback for a context to provide schema mount data.
+ */
+static LY_ERR
+nc_ly_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
+{
+    struct nc_session *session = user_data;
+
+    if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) {
+        return LY_EINVAL;
+    }
+
+    if (!session->opts.client.ext_data) {
+        ERR(session, "Unable to parse mounted data, no operational schema-mounts data received from the server.");
+        return LY_ENOTFOUND;
+    }
+
+    /* return ext data */
+    *ext_data = session->opts.client.ext_data;
+    *ext_data_free = 0;
+
+    return LY_SUCCESS;
+}
+
 int
-nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
+nc_client_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
 {
     /* assign context (dicionary needed for handshake) */
     if (!ctx) {
@@ -236,6 +260,9 @@
 
         /* set callback for getting modules, if provided */
         ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
+
+        /* set ext data callback to avoid errors that no callback is set, the data are stored later, if any */
+        ly_ctx_set_ext_data_clb(ctx, nc_ly_ext_data_clb, session);
     } else {
         session->flags |= NC_SESSION_SHAREDCTX;
     }
@@ -762,48 +789,33 @@
 }
 
 /**
- * @brief Build server module info from ietf-yang-library data.
+ * @brief Retrieve yang-library and schema-mounts operational data from the server.
  *
  * @param[in] session NC session.
  * @param[in] has_get_data Whether get-data RPC is available or only get.
- * @param[out] result Server modules.
+ * @param[in] filter Filter to use.
+ * @param[out] oper_data Received data.
  * @return 0 on success.
  * @return -1 on error.
  */
 static int
-build_module_info_yl(struct nc_session *session, int has_get_data, struct module_info **result)
+get_oper_data(struct nc_session *session, int has_get_data, const char *filter, struct lyd_node **oper_data)
 {
     struct nc_rpc *rpc = NULL;
     struct lyd_node *op = NULL, *envp = NULL;
     struct lyd_node_any *data;
     NC_MSG_TYPE msg;
     uint64_t msgid;
-    struct ly_set *modules = NULL;
-    uint32_t u, v, submodules_count, feature_count;
-    struct lyd_node *iter, *child;
-    struct lys_module *mod;
     int ret = 0;
     const char *rpc_name;
 
-    /* get yang-library data from the server */
+    /* get data from the server */
     if (has_get_data) {
-        rpc_name = "get-data";
-        if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
-            rpc = nc_rpc_getdata("ietf-datastores:operational", "/ietf-yang-library:*", "false", NULL, 0, 0, 0, 0, 0,
-                    NC_PARAMTYPE_CONST);
-        } else {
-            rpc = nc_rpc_getdata("ietf-datastores:operational",
-                    "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", "false", NULL, 0, 0, 0, 0,
-                    0, NC_PARAMTYPE_CONST);
-        }
+        rpc_name = "<get-data>";
+        rpc = nc_rpc_getdata("ietf-datastores:operational", filter, "false", NULL, 0, 0, 0, 0, 0, NC_PARAMTYPE_CONST);
     } else {
-        rpc_name = "get";
-        if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
-            rpc = nc_rpc_get("/ietf-yang-library:*", 0, NC_PARAMTYPE_CONST);
-        } else {
-            rpc = nc_rpc_get("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", 0,
-                    NC_PARAMTYPE_CONST);
-        }
+        rpc_name = "<get>";
+        rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
     }
     if (!rpc) {
         goto cleanup;
@@ -813,7 +825,7 @@
         usleep(1000);
     }
     if (msg == NC_MSG_ERROR) {
-        WRN(session, "Failed to send request for yang-library data.");
+        WRN(session, "Failed to send %s RPC.", rpc_name);
         goto cleanup;
     }
 
@@ -824,27 +836,78 @@
         msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
     } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
     if (msg == NC_MSG_WOULDBLOCK) {
-        WRN(session, "Timeout for receiving reply to a <%s> yang-library data expired.", rpc_name);
+        WRN(session, "Timeout for receiving reply to a %s RPC expired.", rpc_name);
         goto cleanup;
     } else if (msg == NC_MSG_ERROR) {
-        WRN(session, "Failed to receive a reply to <%s> of yang-library data.", rpc_name);
+        WRN(session, "Failed to receive a reply to %s RPC.", rpc_name);
         goto cleanup;
     } else if (!op || !lyd_child(op) || !lyd_child(op)->schema || strcmp(lyd_child(op)->schema->name, "data")) {
-        WRN(session, "Unexpected reply without data to a yang-library <%s> RPC.", rpc_name);
+        WRN(session, "Unexpected reply without data to a %s RPC.", rpc_name);
         goto cleanup;
     }
 
     data = (struct lyd_node_any *)lyd_child(op);
     if (data->value_type != LYD_ANYDATA_DATATREE) {
-        WRN(session, "Unexpected data in reply to a yang-library <%s> RPC.", rpc_name);
+        WRN(session, "Unexpected data in reply to a %s RPC.", rpc_name);
         goto cleanup;
     } else if (!data->value.tree) {
-        WRN(session, "No data in reply to a yang-library <%s> RPC.", rpc_name);
+        WRN(session, "No data in reply to a %s RPC.", rpc_name);
         goto cleanup;
     }
 
-    if (lyd_find_xpath(data->value.tree, "/ietf-yang-library:modules-state/module", &modules)) {
-        WRN(session, "No module information in reply to a yang-library <%s> RPC.", rpc_name);
+    *oper_data = data->value.tree;
+    data->value.tree = NULL;
+
+cleanup:
+    nc_rpc_free(rpc);
+    lyd_free_tree(envp);
+    lyd_free_tree(op);
+
+    if (session->status != NC_STATUS_RUNNING) {
+        /* something bad happened, discard the session */
+        ERR(session, "Invalid session, discarding.");
+        ret = -1;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Build server module info from ietf-yang-library data.
+ *
+ * @param[in] session NC session.
+ * @param[in] get_data_sup Whether get-data RPC is available or only get.
+ * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
+ * @param[out] result Server modules.
+ * @return 0 on success.
+ * @return -1 on error.
+ */
+static int
+build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup, struct module_info **result)
+{
+    struct ly_set *modules = NULL;
+    uint32_t u, v, submodules_count, feature_count;
+    struct lyd_node *iter, *child, *oper_data = NULL;
+    struct lys_module *mod;
+    int ret = 0;
+
+    /* get yang-library operational data */
+    if (xpath_sup) {
+        if (get_oper_data(session, get_data_sup, "/ietf-yang-library:*", &oper_data)) {
+            goto cleanup;
+        }
+    } else {
+        if (get_oper_data(session, get_data_sup,
+                "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", &oper_data)) {
+            goto cleanup;
+        }
+    }
+    if (!oper_data) {
+        goto cleanup;
+    }
+
+    if (lyd_find_xpath(oper_data, "/ietf-yang-library:modules-state/module", &modules)) {
+        WRN(NULL, "No yang-library module information found.");
         goto cleanup;
     }
 
@@ -920,17 +983,8 @@
     }
 
 cleanup:
-    nc_rpc_free(rpc);
-    lyd_free_tree(envp);
-    lyd_free_tree(op);
+    lyd_free_siblings(oper_data);
     ly_set_free(modules, NULL);
-
-    if (session->status != NC_STATUS_RUNNING) {
-        /* something bad happened, discard the session */
-        ERR(session, "Invalid session, discarding.");
-        ret = -1;
-    }
-
     return ret;
 }
 
@@ -1115,10 +1169,64 @@
     return 0;
 }
 
+/**
+ * @brief Set client session context to support schema-mount if possible.
+ *
+ * @param[in] session NC session with the context to modify.
+ * @param[in] get_data_sup Whether get-data RPC is available or only get.
+ * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
+ * @return 0 on success.
+ * @return -1 on error.
+ */
+static int
+nc_ctx_schema_mount(struct nc_session *session, int get_data_sup, int xpath_sup)
+{
+    int rc = 0;
+    struct lyd_node *oper_data = NULL;
+
+    if (session->flags & NC_SESSION_SHAREDCTX) {
+        /* context is already fully set up */
+        goto cleanup;
+    }
+
+    /* get yang-library and schema-mounts operational data */
+    if (xpath_sup) {
+        if ((rc = get_oper_data(session, get_data_sup, "/ietf-yang-library:* | /ietf-yang-schema-mount:*", &oper_data))) {
+            goto cleanup;
+        }
+    } else {
+        if ((rc = get_oper_data(session, get_data_sup,
+                "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>"
+                "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"/>", &oper_data))) {
+            goto cleanup;
+        }
+    }
+
+    if (!oper_data || lyd_find_path(oper_data, "/ietf-yang-schema-mount:schema-mounts", 0, NULL)) {
+        /* no schema-mounts operational data */
+        goto cleanup;
+    }
+
+    /* validate the data for the parent reference prefixes to be resolved */
+    if (lyd_validate_all(&oper_data, NULL, LYD_VALIDATE_PRESENT, NULL)) {
+        ERR(session, "Invalid operational data received from the server (%s).", ly_errmsg(LYD_CTX(oper_data)));
+        rc = -1;
+        goto cleanup;
+    }
+
+    /* store the data in the session */
+    session->opts.client.ext_data = oper_data;
+    oper_data = NULL;
+
+cleanup:
+    lyd_free_siblings(oper_data);
+    return rc;
+}
+
 int
 nc_ctx_check_and_fill(struct nc_session *session)
 {
-    int i, get_schema_support = 0, yanglib_support = 0, get_data_support = 0, ret = -1;
+    int i, get_schema_support = 0, yanglib_support = 0, get_data_support = 0, xpath_support = 0, ret = -1;
     ly_module_imp_clb old_clb = NULL;
     void *old_data = NULL;
     struct lys_module *mod = NULL;
@@ -1141,14 +1249,10 @@
     for (i = 0; session->opts.client.cpblts[i]; ++i) {
         if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
             get_schema_support = 1 + i;
-            if (yanglib_support) {
-                break;
-            }
         } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
             yanglib_support = 1 + i;
-            if (get_schema_support) {
-                break;
-            }
+        } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:xpath:1.0", 44)) {
+            xpath_support = 1 + i;
         }
     }
     if (get_schema_support) {
@@ -1219,7 +1323,7 @@
 
     /* prepare structured information about server's modules */
     if (yanglib_support) {
-        if (build_module_info_yl(session, get_data_support, &sm)) {
+        if (build_module_info_yl(session, get_data_support, xpath_support, &sm)) {
             goto cleanup;
         } else if (!sm) {
             VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
@@ -1244,6 +1348,11 @@
         goto cleanup;
     }
 
+    /* set support for schema-mount, if possible */
+    if (nc_ctx_schema_mount(session, get_data_support, xpath_support)) {
+        goto cleanup;
+    }
+
     /* success */
     ret = 0;
 
@@ -1289,7 +1398,7 @@
     session->ti.fd.in = fdin;
     session->ti.fd.out = fdout;
 
-    if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
+    if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
         goto fail;
     }
     ctx = session->ctx;
@@ -1360,7 +1469,7 @@
     session->ti.unixsock.sock = sock;
     sock = -1; /* do not close sock in fail label anymore */
 
-    if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
+    if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
         goto fail;
     }
     ctx = session->ctx;