blob: 425d457b216a0c88188e7d0b86752cda14a899d9 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_client.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libnetconf2 session client functions
Michal Vasko086311b2016-01-08 09:53:11 +01005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
7 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01008 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01009 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010012 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010013 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010014 */
15
Radek Krejcifd5b6682017-06-13 15:52:53 +020016#define _GNU_SOURCE
Michal Vaskobbf52c82020-04-07 13:08:50 +020017
18#ifdef __linux__
19# include <sys/syscall.h>
20#endif
21
Michal Vaskob83a3fa2021-05-26 09:53:42 +020022#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010023#include <assert.h>
tadeas-vintrlik54f142a2021-08-23 10:36:18 +020024#include <ctype.h>
Michal Vasko086311b2016-01-08 09:53:11 +010025#include <errno.h>
26#include <fcntl.h>
27#include <netdb.h>
Radek Krejci4cf58ec2016-02-26 15:04:52 +010028#include <netinet/in.h>
Michal Vasko83ad17e2019-01-30 10:11:37 +010029#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020030#include <poll.h>
Michal Vasko086311b2016-01-08 09:53:11 +010031#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020032#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010033#include <stdlib.h>
34#include <string.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020035#include <sys/select.h>
Michal Vasko086311b2016-01-08 09:53:11 +010036#include <sys/socket.h>
37#include <sys/stat.h>
38#include <sys/types.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020039#include <sys/un.h>
Michal Vasko086311b2016-01-08 09:53:11 +010040#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010041
42#include <libyang/libyang.h>
43
Michal Vasko9e8ac262020-04-07 13:06:45 +020044#include "compat.h"
Michal Vasko086311b2016-01-08 09:53:11 +010045#include "libnetconf.h"
Michal Vaskoa8ad4482016-01-28 14:25:54 +010046#include "messages_client.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020047#include "session_client.h"
Michal Vasko086311b2016-01-08 09:53:11 +010048
Michal Vasko8a4e1462020-05-07 11:32:31 +020049#include "../modules/ietf_netconf@2013-09-29_yang.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020050#include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h"
Michal Vasko8a4e1462020-05-07 11:32:31 +020051
Michal Vasko80ef5d22016-01-18 09:21:02 +010052static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
53
Radek Krejci62aa0642017-05-25 16:33:49 +020054#ifdef NC_ENABLED_SSH
55int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
56char *sshauth_password(const char *username, const char *hostname, void *priv);
57char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020058char *sshauth_privkey_passphrase(const char *privkey_path, void *priv);
Radek Krejci62aa0642017-05-25 16:33:49 +020059#endif /* NC_ENABLED_SSH */
60
61static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
62static pthread_key_t nc_client_context_key;
63#ifdef __linux__
64static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020065 .opts.ka = {
66 .enabled = 1,
67 .idle_time = 1,
68 .max_probes = 10,
69 .probe_interval = 5
70 },
Radek Krejci62aa0642017-05-25 16:33:49 +020071#ifdef NC_ENABLED_SSH
72 .ssh_opts = {
roman41a11e42022-06-22 09:27:08 +020073 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Radek Krejci62aa0642017-05-25 16:33:49 +020074 .auth_hostkey_check = sshauth_hostkey_check,
75 .auth_password = sshauth_password,
76 .auth_interactive = sshauth_interactive,
77 .auth_privkey_passphrase = sshauth_privkey_passphrase
78 },
79 .ssh_ch_opts = {
80 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
81 .auth_hostkey_check = sshauth_hostkey_check,
82 .auth_password = sshauth_password,
83 .auth_interactive = sshauth_interactive,
84 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020085 },
Radek Krejci62aa0642017-05-25 16:33:49 +020086#endif /* NC_ENABLED_SSH */
87 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020088 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020089};
90#endif
91
92static void
93nc_client_context_free(void *ptr)
94{
95 struct nc_client_context *c = (struct nc_client_context *)ptr;
96
Radek Krejci5cebc6b2017-05-26 13:24:38 +020097 if (--(c->refcount)) {
98 /* still used */
99 return;
100 }
101
Radek Krejci62aa0642017-05-25 16:33:49 +0200102#ifdef __linux__
103 /* in __linux__ we use static memory in the main thread,
104 * so this check is for programs terminating the main()
105 * function by pthread_exit() :)
106 */
107 if (c != &context_main)
108#endif
109 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200110 /* for the main thread the same is done in nc_client_destroy() */
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400111 free(c->opts.schema_searchpath);
112
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200113#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400114 int i;
Michal Vasko292c5542023-02-01 14:33:17 +0100115
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400116 for (i = 0; i < c->opts.ch_bind_count; ++i) {
117 close(c->opts.ch_binds[i].sock);
118 free((char *)c->opts.ch_binds[i].address);
119 }
120 free(c->opts.ch_binds);
121 c->opts.ch_binds = NULL;
122 c->opts.ch_bind_count = 0;
Radek Krejci62aa0642017-05-25 16:33:49 +0200123#endif
124#ifdef NC_ENABLED_SSH
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400125 _nc_client_ssh_destroy_opts(&c->ssh_opts);
126 _nc_client_ssh_destroy_opts(&c->ssh_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200127#endif
128#ifdef NC_ENABLED_TLS
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400129 _nc_client_tls_destroy_opts(&c->tls_opts);
130 _nc_client_tls_destroy_opts(&c->tls_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200131#endif
132 free(c);
133 }
134}
135
136static void
137nc_client_context_createkey(void)
138{
139 int r;
140
141 /* initiate */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200142 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN) {}
Radek Krejci62aa0642017-05-25 16:33:49 +0200143 pthread_setspecific(nc_client_context_key, NULL);
144}
145
146struct nc_client_context *
147nc_client_context_location(void)
148{
149 struct nc_client_context *e;
150
151 pthread_once(&nc_client_context_once, nc_client_context_createkey);
152 e = pthread_getspecific(nc_client_context_key);
153 if (!e) {
154 /* prepare ly_err storage */
155#ifdef __linux__
156 if (getpid() == syscall(SYS_gettid)) {
157 /* main thread - use global variable instead of thread-specific variable. */
158 e = &context_main;
159 } else
160#endif /* __linux__ */
161 {
162 e = calloc(1, sizeof *e);
163 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200164 e->refcount = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200165#ifdef NC_ENABLED_SSH
166 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
roman41a11e42022-06-22 09:27:08 +0200167 e->ssh_opts.auth_pref[0].value = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200168 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
169 e->ssh_opts.auth_pref[1].value = 2;
170 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
roman41a11e42022-06-22 09:27:08 +0200171 e->ssh_opts.auth_pref[2].value = 3;
Radek Krejci62aa0642017-05-25 16:33:49 +0200172 e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check;
173 e->ssh_opts.auth_password = sshauth_password;
174 e->ssh_opts.auth_interactive = sshauth_interactive;
175 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
176
roman41a11e42022-06-22 09:27:08 +0200177 /* callhome settings are the same */
Radek Krejci62aa0642017-05-25 16:33:49 +0200178 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
179 e->ssh_ch_opts.auth_pref[0].value = 1;
180 e->ssh_ch_opts.auth_pref[1].value = 2;
181 e->ssh_ch_opts.auth_pref[2].value = 3;
182#endif /* NC_ENABLED_SSH */
183 }
184 pthread_setspecific(nc_client_context_key, e);
185 }
186
187 return e;
188}
189
190#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100191
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200192API void *
193nc_client_get_thread_context(void)
194{
195 return nc_client_context_location();
196}
197
198API void
199nc_client_set_thread_context(void *context)
200{
201 struct nc_client_context *old, *new;
202
203 if (!context) {
204 ERRARG(context);
205 return;
206 }
207
208 new = (struct nc_client_context *)context;
209 old = nc_client_context_location();
210 if (old == new) {
211 /* nothing to change */
212 return;
213 }
214
215 /* replace old by new, increase reference counter in the newly set context */
216 nc_client_context_free(old);
217 new->refcount++;
218 pthread_setspecific(nc_client_context_key, new);
219}
220
Michal Vasko78939072022-12-12 07:43:18 +0100221/**
222 * @brief Ext data callback for a context to provide schema mount data.
223 */
224static LY_ERR
225nc_ly_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
226{
227 struct nc_session *session = user_data;
228
229 if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) {
230 return LY_EINVAL;
231 }
232
233 if (!session->opts.client.ext_data) {
234 ERR(session, "Unable to parse mounted data, no operational schema-mounts data received from the server.");
235 return LY_ENOTFOUND;
236 }
237
238 /* return ext data */
239 *ext_data = session->opts.client.ext_data;
240 *ext_data_free = 0;
241
242 return LY_SUCCESS;
243}
244
Radek Krejcifd5b6682017-06-13 15:52:53 +0200245int
Michal Vasko78939072022-12-12 07:43:18 +0100246nc_client_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200247{
248 /* assign context (dicionary needed for handshake) */
249 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100250 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200251 return EXIT_FAILURE;
252 }
253
Michal Vasko5ca5d972022-09-14 13:51:31 +0200254 /* user path must be first, the first path is used to store modules retreived via get-schema */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200255 if (client_opts.schema_searchpath) {
256 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko5d0cdc32022-12-12 07:38:21 +0100257 }
Michal Vasko80227222022-12-12 09:05:24 +0100258 if (!access(NC_CLIENT_SEARCH_DIR, F_OK)) {
259 ly_ctx_set_searchdir(ctx, NC_CLIENT_SEARCH_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200260 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200261
Michal Vasko5ca5d972022-09-14 13:51:31 +0200262 /* set callback for getting modules, if provided */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200263 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
Michal Vasko78939072022-12-12 07:43:18 +0100264
265 /* set ext data callback to avoid errors that no callback is set, the data are stored later, if any */
266 ly_ctx_set_ext_data_clb(ctx, nc_ly_ext_data_clb, session);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200267 } else {
268 session->flags |= NC_SESSION_SHAREDCTX;
269 }
270
271 session->ctx = ctx;
272
273 return EXIT_SUCCESS;
274}
275
Michal Vasko086311b2016-01-08 09:53:11 +0100276API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100277nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100278{
Michal Vasko3031aae2016-01-27 16:07:18 +0100279 if (client_opts.schema_searchpath) {
280 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100281 }
Michal Vasko086311b2016-01-08 09:53:11 +0100282
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100283 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100284 client_opts.schema_searchpath = strdup(path);
285 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100286 ERRMEM;
287 return 1;
288 }
289 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100290 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100291 }
292
293 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100294}
295
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100296API const char *
297nc_client_get_schema_searchpath(void)
298{
299 return client_opts.schema_searchpath;
300}
301
Radek Krejcifd5b6682017-06-13 15:52:53 +0200302API int
303nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100304{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200305 client_opts.schema_clb = clb;
306 if (clb) {
307 client_opts.schema_clb_data = user_data;
308 } else {
309 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100310 }
311
312 return 0;
313}
314
Radek Krejcifd5b6682017-06-13 15:52:53 +0200315API ly_module_imp_clb
316nc_client_get_schema_callback(void **user_data)
317{
318 if (user_data) {
319 (*user_data) = client_opts.schema_clb_data;
320 }
321 return client_opts.schema_clb;
322}
323
Michal Vasko5ca5d972022-09-14 13:51:31 +0200324struct module_info {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200325 char *name;
326 char *revision;
Michal Vasko6ce8e822022-08-02 15:09:17 +0200327
Radek Krejci65ef6d52018-08-16 16:35:02 +0200328 struct {
329 char *name;
330 char *revision;
331 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100332 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200333 int implemented;
334};
335
336struct clb_data_s {
337 void *user_data;
338 ly_module_imp_clb user_clb;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200339 struct module_info *modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200340 struct nc_session *session;
341 int has_get_schema;
342};
343
Michal Vaskoc088f982021-10-05 12:23:07 +0200344/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200345 * @brief Retrieve YANG module content from a local file.
Michal Vaskoc088f982021-10-05 12:23:07 +0200346 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200347 * @param[in] name Module name.
348 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200349 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200350 * @param[out] format Module format.
351 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200352 */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200353static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200354retrieve_module_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200355 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100356{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200357 char *localfile = NULL;
358 FILE *f;
359 long length, l;
360 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100361
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200362 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200363 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
364 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200365 return NULL;
366 }
367 if (localfile) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200368 VRB(clb_data->session, "Reading module \"%s@%s\" from local file \"%s\".", name, rev ? rev : "<latest>",
Michal Vasko7502b602022-08-26 11:51:17 +0200369 localfile);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200370 f = fopen(localfile, "r");
371 if (!f) {
Michal Vasko7502b602022-08-26 11:51:17 +0200372 ERR(clb_data->session, "Unable to open file \"%s\" (%s).", localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200373 free(localfile);
374 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100375 }
Michal Vasko086311b2016-01-08 09:53:11 +0100376
Radek Krejci65ef6d52018-08-16 16:35:02 +0200377 fseek(f, 0, SEEK_END);
378 length = ftell(f);
Radek Krejci3f499a62018-08-17 10:16:57 +0200379 if (length < 0) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200380 ERR(clb_data->session, "Unable to get the size of module file \"%s\".", localfile);
Radek Krejci3f499a62018-08-17 10:16:57 +0200381 free(localfile);
382 fclose(f);
383 return NULL;
384 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200385 fseek(f, 0, SEEK_SET);
386
387 model_data = malloc(length + 1);
388 if (!model_data) {
389 ERRMEM;
390 } else if ((l = fread(model_data, 1, length, f)) != length) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200391 ERR(clb_data->session, "Reading module from \"%s\" failed (%d bytes read, but %d expected).", localfile, l,
Michal Vasko05532772021-06-03 12:12:38 +0200392 length);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200393 free(model_data);
394 model_data = NULL;
395 } else {
396 /* terminating NULL byte */
397 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100398 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200399 fclose(f);
400 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100401 }
402
Radek Krejci65ef6d52018-08-16 16:35:02 +0200403 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100404}
405
Michal Vaskoc088f982021-10-05 12:23:07 +0200406/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200407 * @brief Retrieve YANG module content from a reply to get-schema RPC.
Michal Vaskoc088f982021-10-05 12:23:07 +0200408 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200409 * @param[in] name Module name.
410 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200411 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200412 * @param[out] format Module format.
413 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200414 */
Michal Vasko086311b2016-01-08 09:53:11 +0100415static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200416retrieve_module_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200417 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100418{
Michal Vasko086311b2016-01-08 09:53:11 +0100419 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100420 struct lyd_node *envp = NULL, *op = NULL;
421 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100422 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100423 uint64_t msgid;
Michal Vasko7502b602022-08-26 11:51:17 +0200424 char *localfile = NULL, *envp_str = NULL, *model_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200425 FILE *f;
Michal Vasko086311b2016-01-08 09:53:11 +0100426
Michal Vasko5ca5d972022-09-14 13:51:31 +0200427 VRB(clb_data->session, "Reading module \"%s@%s\" from server via get-schema.", name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200428 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100429
Radek Krejci65ef6d52018-08-16 16:35:02 +0200430 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100431 usleep(1000);
432 }
433 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200434 ERR(clb_data->session, "Failed to send the <get-schema> RPC.");
Michal Vasko086311b2016-01-08 09:53:11 +0100435 nc_rpc_free(rpc);
436 return NULL;
437 }
438
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200439 do {
Michal Vasko77367452021-02-16 16:32:18 +0100440 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200441 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100442 nc_rpc_free(rpc);
443 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200444 ERR(clb_data->session, "Timeout for receiving reply to a <get-schema> expired.");
Michal Vasko77367452021-02-16 16:32:18 +0100445 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100446 } else if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200447 ERR(clb_data->session, "Failed to receive a reply to <get-schema>.");
Michal Vasko77367452021-02-16 16:32:18 +0100448 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100449 } else if (!op) {
Michal Vasko7502b602022-08-26 11:51:17 +0200450 assert(envp);
451 lyd_print_mem(&envp_str, envp, LYD_XML, 0);
452 WRN(clb_data->session, "Received an unexpected reply to <get-schema>:\n%s", envp_str);
453 free(envp_str);
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100454 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100455 }
456
Michal Vasko77367452021-02-16 16:32:18 +0100457 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Michal Vasko05532772021-06-03 12:12:38 +0200458 ERR(clb_data->session, "Unexpected data in reply to a <get-schema> RPC.");
Michal Vasko77367452021-02-16 16:32:18 +0100459 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200460 }
Michal Vasko77367452021-02-16 16:32:18 +0100461 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200462 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200463 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100464 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200465 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200466 break;
467 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100468 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200469 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200470 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200471 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200472 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100473 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200474 }
Michal Vasko086311b2016-01-08 09:53:11 +0100475
Radek Krejci488b0702018-08-29 14:47:15 +0200476 if (model_data && !model_data[0]) {
477 /* empty data */
478 free(model_data);
479 model_data = NULL;
480 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100481 if (!model_data) {
482 goto cleanup;
483 }
484
485 /* set format */
486 *format = LYS_IN_YANG;
Radek Krejci488b0702018-08-29 14:47:15 +0200487
Michal Vasko5ca5d972022-09-14 13:51:31 +0200488 /* try to store the model_data into local module repository */
Michal Vaskoa1721172022-12-12 09:06:53 +0100489 lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx), 0, name, rev, &localfile, NULL);
490 if (client_opts.schema_searchpath && !localfile) {
491 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
492 rev ? rev : "") == -1) {
493 ERRMEM;
494 } else {
495 f = fopen(localfile, "w");
496 if (!f) {
497 WRN(clb_data->session, "Unable to store \"%s\" as a local copy of module retrieved via <get-schema> (%s).",
498 localfile, strerror(errno));
Michal Vaskof945da52018-02-15 08:45:13 +0100499 } else {
Michal Vaskoa1721172022-12-12 09:06:53 +0100500 fputs(model_data, f);
501 fclose(f);
Michal Vaskof945da52018-02-15 08:45:13 +0100502 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200503 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200504 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100505 free(localfile);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200506
Michal Vasko77367452021-02-16 16:32:18 +0100507cleanup:
508 lyd_free_tree(envp);
509 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100510 return model_data;
511}
512
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200513static void
514free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200515{
516 free(data);
517 (void)user_data;
518}
519
Michal Vaskoc088f982021-10-05 12:23:07 +0200520/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200521 * @brief Retrieve YANG module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200522 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200523 * @param[in] mod_name Module name.
524 * @param[in] mod_rev Module revision.
Michal Vasko5e32c402022-01-12 14:05:09 +0100525 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200526 * @param[out] format Module format.
527 * @param[out] module_data Module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100528 * @param[out] free_module_data Callback for freeing @p module_data.
529 * @return LY_ERR value.
530 */
531static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200532retrieve_module_data(const char *mod_name, const char *mod_rev, void *user_data, LYS_INFORMAT *format,
Michal Vasko5e32c402022-01-12 14:05:09 +0100533 const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
534{
535 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
536 char *model_data = NULL;
537
Michal Vasko5e32c402022-01-12 14:05:09 +0100538 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200539 model_data = retrieve_module_data_localfile(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100540
541 /* 2. try to use <get-schema> */
542 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200543 model_data = retrieve_module_data_getschema(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100544 }
545
546 /* 3. try to use user callback */
547 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200548 VRB(clb_data->session, "Reading module \"%s@%s\" via user callback.", mod_name, mod_rev ? mod_rev : "<latest>");
Michal Vasko5e32c402022-01-12 14:05:09 +0100549 clb_data->user_clb(mod_name, mod_rev, NULL, NULL, clb_data->user_data, format, (const char **)&model_data,
550 free_module_data);
551 }
552
553 *free_module_data = free_with_user_data;
554 *module_data = model_data;
555 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
556}
557
558/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200559 * @brief Retrieve YANG import module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100560 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200561 * @param[in] mod_name Module name.
562 * @param[in] mod_rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200563 * @param[in] submod_name Optional submodule name.
564 * @param[in] sub_rev Submodule revision.
565 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200566 * @param[out] format Module format.
567 * @param[out] module_data Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200568 * @param[out] free_module_data Callback for freeing @p module_data.
569 * @return LY_ERR value.
570 */
Michal Vasko77367452021-02-16 16:32:18 +0100571static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200572retrieve_module_data_imp(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev,
Michal Vasko77367452021-02-16 16:32:18 +0100573 void *user_data, LYS_INFORMAT *format, const char **module_data,
574 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200575{
576 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
Michal Vasko5e32c402022-01-12 14:05:09 +0100577 uint32_t u, v, match = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200578 const char *name = NULL, *rev = NULL;
579 char *model_data = NULL;
580
Michal Vasko5ca5d972022-09-14 13:51:31 +0200581 /* get and check the final name and revision of the module to be retrieved */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200582 if (!mod_rev || !mod_rev[0]) {
583 /* newest revision requested - get the newest revision from the list of available modules on server */
584 match = 0;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200585 for (u = 0; clb_data->modules[u].name; ++u) {
586 if (strcmp(mod_name, clb_data->modules[u].name)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200587 continue;
588 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200589 if (!match || (strcmp(mod_rev, clb_data->modules[u].revision) > 0)) {
590 mod_rev = clb_data->modules[u].revision;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200591 }
592 match = u + 1;
593 }
594 if (!match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200595 /* valid situation if we are retrieving YANG 1.1 module and have only capabilities for now
Michal Vasko1d2665c2021-02-12 09:28:44 +0100596 * (when loading ietf-datastore for ietf-yang-library) */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200597 VRB(clb_data->session, "Unable to identify revision of the import module \"%s\" from "
Michal Vasko05532772021-06-03 12:12:38 +0200598 "the available server side information.", mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200599 }
600 }
601 if (submod_name) {
602 name = submod_name;
603 if (sub_rev) {
604 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200605 } else if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200606 if (!clb_data->modules[match - 1].submodules) {
Michal Vasko05532772021-06-03 12:12:38 +0200607 VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", "
Michal Vasko5ca5d972022-09-14 13:51:31 +0200608 "in import module \"%s\", from the available server side information.", submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200609 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200610 for (v = 0; clb_data->modules[match - 1].submodules[v].name; ++v) {
611 if (!strcmp(submod_name, clb_data->modules[match - 1].submodules[v].name)) {
612 rev = sub_rev = clb_data->modules[match - 1].submodules[v].revision;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200613 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200614 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200615 if (!rev) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200616 ERR(clb_data->session, "Requested submodule \"%s\" is not found in import module \"%s\" on server side.",
Michal Vasko05532772021-06-03 12:12:38 +0200617 submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100618 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200619 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200620 }
621 }
622 } else {
623 name = mod_name;
624 rev = mod_rev;
625 }
626
Radek Krejci65ef6d52018-08-16 16:35:02 +0200627 if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200628 /* we have enough information to avoid communication with server and try to get the module locally */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200629
630 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200631 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200632
633 /* 2. try to use <get-schema> */
634 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200635 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200636 }
637 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200638 /* we are unsure which revision of the module we should load, so first try to get
Radek Krejci65ef6d52018-08-16 16:35:02 +0200639 * the newest revision from the server via get-schema and only if the server does not
640 * implement get-schema, try to load the newest revision locally. This is imperfect
641 * solution, but there are situation when a client does not know what revision is
642 * actually implemented by the server. */
643
644 /* 1. try to use <get-schema> */
645 if (clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200646 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200647 }
648
649 /* 2. try to get data locally */
650 if (!model_data) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200651 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200652 }
653 }
654
655 /* 3. try to use user callback */
656 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200657 VRB(clb_data->session, "Reading module \"%s@%s\" via user callback.", name, rev ? rev : "<latest>");
Michal Vasko77367452021-02-16 16:32:18 +0100658 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
659 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200660 }
661
Jan Kundrát35972df2018-09-06 19:00:01 +0200662 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100663 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200664 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200665}
666
Michal Vaskoc088f982021-10-05 12:23:07 +0200667/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200668 * @brief Load a YANG module into context.
Michal Vaskoc088f982021-10-05 12:23:07 +0200669 *
670 * @param[in] session NC session.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200671 * @param[in] name Module name.
672 * @param[in] revision Module revision.
673 * @param[in] features Enabled module features.
674 * @param[in] modules Server module info built from capabilities.
675 * @param[in] user_clb User callback for retrieving module data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200676 * @param[in] user_data User data for @p user_clb.
677 * @param[in] has_get_schema Whether the server supports get-schema.
678 * @param[out] mod Loaded module.
679 * @return 0 on success.
680 * @return -1 on error.
681 */
Michal Vaskoceae0152018-02-14 16:03:59 +0100682static int
Michal Vasko5e32c402022-01-12 14:05:09 +0100683nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, const char **features,
Michal Vasko5ca5d972022-09-14 13:51:31 +0200684 struct module_info *modules, ly_module_imp_clb user_clb, void *user_data, int has_get_schema, struct lys_module **mod)
Michal Vaskoceae0152018-02-14 16:03:59 +0100685{
686 int ret = 0;
687 struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200688 const char *module_data = NULL;
Michal Vasko5e32c402022-01-12 14:05:09 +0100689 struct ly_in *in;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200690 LYS_INFORMAT format;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200691
692 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200693 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100694
Michal Vasko5e32c402022-01-12 14:05:09 +0100695 /* try to use a module from the context */
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200696 *mod = ly_ctx_get_module_implemented(session->ctx, name);
697 if (!*mod) {
698 if (revision) {
699 *mod = ly_ctx_get_module(session->ctx, name, revision);
700 } else {
Michal Vasko5e32c402022-01-12 14:05:09 +0100701 *mod = ly_ctx_get_module_latest(session->ctx, name);
702 }
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200703 } else if (revision && (!(*mod)->revision || strcmp((*mod)->revision, revision))) {
704 WRN(session, "Server implements module \"%s\" in revision \"%s\" but revision \"%s\" is already implemented"
705 " and will be used instead.", name, revision, (*mod)->revision ? (*mod)->revision : "<none>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200706 }
Michal Vasko5e32c402022-01-12 14:05:09 +0100707
Michal Vaskoceae0152018-02-14 16:03:59 +0100708 if (*mod) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100709 /* make the present module implemented and/or enable all its features */
710 if (lys_set_implemented(*mod, features)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200711 ERR(session, "Failed to implement module \"%s\".", (*mod)->name);
Michal Vasko5e32c402022-01-12 14:05:09 +0100712 ret = -1;
Michal Vaskoceae0152018-02-14 16:03:59 +0100713 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200714 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100715 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200716 clb_data.has_get_schema = has_get_schema;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200717 clb_data.modules = modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200718 clb_data.session = session;
719 clb_data.user_clb = user_clb;
720 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100721
722 /* clear all the errors and just collect them for now */
723 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200724 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100725
Radek Krejci65ef6d52018-08-16 16:35:02 +0200726 /* get module data */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200727 retrieve_module_data(name, revision, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100728
Radek Krejci65ef6d52018-08-16 16:35:02 +0200729 if (module_data) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100730 /* set import callback */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200731 ly_ctx_set_module_imp_clb(session->ctx, retrieve_module_data_imp, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100732
Michal Vasko5ca5d972022-09-14 13:51:31 +0200733 /* parse the module */
Michal Vasko5e32c402022-01-12 14:05:09 +0100734 ly_in_new_memory(module_data, &in);
735 lys_parse(session->ctx, in, format, features, mod);
736 ly_in_free(in, 0);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200737 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100738 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100739 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100740
Radek Krejci65ef6d52018-08-16 16:35:02 +0200741 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
742 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100743
Michal Vaskodbf7c272018-02-19 09:07:59 +0100744 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200745 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100746 if (!(*mod)) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100747 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100748 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100749 }
750 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200751 } else {
752 /* print only warnings */
Michal Vasko5e32c402022-01-12 14:05:09 +0100753 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200754 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100755 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200756 }
757 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100758 }
759
Michal Vaskodbf7c272018-02-19 09:07:59 +0100760 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100761 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100762 }
763
764 return ret;
765}
766
Radek Krejci65ef6d52018-08-16 16:35:02 +0200767static void
Michal Vasko5ca5d972022-09-14 13:51:31 +0200768free_module_info(struct module_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200769{
Michal Vasko77367452021-02-16 16:32:18 +0100770 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200771
Radek Krejci65ef6d52018-08-16 16:35:02 +0200772 if (!list) {
773 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200774 }
775
Radek Krejci65ef6d52018-08-16 16:35:02 +0200776 for (u = 0; list[u].name; ++u) {
777 free(list[u].name);
778 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100779 if (list[u].features) {
780 for (v = 0; list[u].features[v]; ++v) {
781 free(list[u].features[v]);
782 }
783 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200784 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200785 if (list[u].submodules) {
786 for (v = 0; list[u].submodules[v].name; ++v) {
787 free(list[u].submodules[v].name);
788 free(list[u].submodules[v].revision);
789 }
790 free(list[u].submodules);
791 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200792 }
793 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200794}
795
Michal Vaskoc088f982021-10-05 12:23:07 +0200796/**
Michal Vasko78939072022-12-12 07:43:18 +0100797 * @brief Retrieve yang-library and schema-mounts operational data from the server.
Michal Vaskoc088f982021-10-05 12:23:07 +0200798 *
799 * @param[in] session NC session.
800 * @param[in] has_get_data Whether get-data RPC is available or only get.
Michal Vasko78939072022-12-12 07:43:18 +0100801 * @param[in] filter Filter to use.
802 * @param[out] oper_data Received data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200803 * @return 0 on success.
804 * @return -1 on error.
805 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200806static int
Michal Vasko78939072022-12-12 07:43:18 +0100807get_oper_data(struct nc_session *session, int has_get_data, const char *filter, struct lyd_node **oper_data)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200808{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200809 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100810 struct lyd_node *op = NULL, *envp = NULL;
811 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200812 NC_MSG_TYPE msg;
813 uint64_t msgid;
Michal Vasko303263d2021-10-05 12:18:21 +0200814 int ret = 0;
815 const char *rpc_name;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200816
Michal Vasko78939072022-12-12 07:43:18 +0100817 /* get data from the server */
Michal Vasko303263d2021-10-05 12:18:21 +0200818 if (has_get_data) {
Michal Vasko78939072022-12-12 07:43:18 +0100819 rpc_name = "<get-data>";
820 rpc = nc_rpc_getdata("ietf-datastores:operational", filter, "false", NULL, 0, 0, 0, 0, 0, NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200821 } else {
Michal Vasko78939072022-12-12 07:43:18 +0100822 rpc_name = "<get>";
823 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200824 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200825 if (!rpc) {
826 goto cleanup;
827 }
828
829 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
830 usleep(1000);
831 }
832 if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100833 WRN(session, "Failed to send %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200834 goto cleanup;
835 }
836
837 do {
Michal Vasko77367452021-02-16 16:32:18 +0100838 lyd_free_tree(envp);
839 lyd_free_tree(op);
840
841 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200842 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200843 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko78939072022-12-12 07:43:18 +0100844 WRN(session, "Timeout for receiving reply to a %s RPC expired.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200845 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100846 } else if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100847 WRN(session, "Failed to receive a reply to %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200848 goto cleanup;
Jan Kundrát1d73a5a2022-03-30 16:54:49 +0200849 } else if (!op || !lyd_child(op) || !lyd_child(op)->schema || strcmp(lyd_child(op)->schema->name, "data")) {
Michal Vasko78939072022-12-12 07:43:18 +0100850 WRN(session, "Unexpected reply without data to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200851 goto cleanup;
852 }
853
Michal Vasko77367452021-02-16 16:32:18 +0100854 data = (struct lyd_node_any *)lyd_child(op);
855 if (data->value_type != LYD_ANYDATA_DATATREE) {
Michal Vasko78939072022-12-12 07:43:18 +0100856 WRN(session, "Unexpected data in reply to a %s RPC.", rpc_name);
Michal Vasko40528752021-06-21 15:41:51 +0200857 goto cleanup;
858 } else if (!data->value.tree) {
Michal Vasko78939072022-12-12 07:43:18 +0100859 WRN(session, "No data in reply to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200860 goto cleanup;
861 }
862
Michal Vasko78939072022-12-12 07:43:18 +0100863 *oper_data = data->value.tree;
864 data->value.tree = NULL;
865
866cleanup:
867 nc_rpc_free(rpc);
868 lyd_free_tree(envp);
869 lyd_free_tree(op);
870
871 if (session->status != NC_STATUS_RUNNING) {
872 /* something bad happened, discard the session */
873 ERR(session, "Invalid session, discarding.");
874 ret = -1;
875 }
876
877 return ret;
878}
879
880/**
881 * @brief Build server module info from ietf-yang-library data.
882 *
883 * @param[in] session NC session.
884 * @param[in] get_data_sup Whether get-data RPC is available or only get.
885 * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
886 * @param[out] result Server modules.
887 * @return 0 on success.
888 * @return -1 on error.
889 */
890static int
891build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup, struct module_info **result)
892{
893 struct ly_set *modules = NULL;
894 uint32_t u, v, submodules_count, feature_count;
895 struct lyd_node *iter, *child, *oper_data = NULL;
896 struct lys_module *mod;
897 int ret = 0;
898
899 /* get yang-library operational data */
900 if (xpath_sup) {
901 if (get_oper_data(session, get_data_sup, "/ietf-yang-library:*", &oper_data)) {
902 goto cleanup;
903 }
904 } else {
905 if (get_oper_data(session, get_data_sup,
906 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", &oper_data)) {
907 goto cleanup;
908 }
909 }
910 if (!oper_data) {
911 goto cleanup;
912 }
913
914 if (lyd_find_xpath(oper_data, "/ietf-yang-library:modules-state/module", &modules)) {
915 WRN(NULL, "No yang-library module information found.");
Radek Krejci2d088832018-08-21 13:40:14 +0200916 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200917 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200918
Michal Vasko77367452021-02-16 16:32:18 +0100919 (*result) = calloc(modules->count + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200920 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200921 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200922 ret = -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200923 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200924 }
925
Michal Vasko77367452021-02-16 16:32:18 +0100926 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200927 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100928 feature_count = 0;
929 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
930 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Michal Vasko5d9d5972021-06-09 14:17:01 +0200931 if (!iter->schema || (iter->schema->module != mod)) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200932 /* ignore node from other schemas (augments) */
933 continue;
934 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200935 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200936 /* ignore empty nodes */
937 continue;
938 }
939 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200940 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200941 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200942 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200943 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200944 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200945 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100946 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
947 if (!(*result)[u].features) {
948 ERRMEM;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200949 free_module_info(*result);
Michal Vasko77367452021-02-16 16:32:18 +0100950 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200951 ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +0100952 goto cleanup;
953 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200954 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100955 (*result)[u].features[feature_count + 1] = NULL;
956 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200957 } else if (!strcmp(iter->schema->name, "submodule")) {
958 submodules_count++;
959 }
960 }
961
962 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200963 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
964 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200965 ERRMEM;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200966 free_module_info(*result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200967 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200968 ret = -1;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200969 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200970 } else {
971 v = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100972 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
973 mod = modules->dnodes[u]->schema->module;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200974 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
Michal Vasko77367452021-02-16 16:32:18 +0100975 LY_LIST_FOR(lyd_child(iter), child) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200976 if (mod != child->schema->module) {
977 continue;
978 } else if (!strcmp(child->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200979 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200980 } else if (!strcmp(child->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200981 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200982 }
983 }
984 }
985 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200986 }
987 }
988 }
989
Radek Krejcifd5b6682017-06-13 15:52:53 +0200990cleanup:
Michal Vasko78939072022-12-12 07:43:18 +0100991 lyd_free_siblings(oper_data);
Michal Vasko77367452021-02-16 16:32:18 +0100992 ly_set_free(modules, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200993 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200994}
995
Michal Vaskoc088f982021-10-05 12:23:07 +0200996/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200997 * @brief Build server module info from received capabilities.
Michal Vaskoc088f982021-10-05 12:23:07 +0200998 *
999 * @param[in] cpblts Server capabilities.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001000 * @param[out] result Server modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001001 * @return 0 on success.
1002 * @return -1 on error.
1003 */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001004static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001005build_module_info_cpblts(char **cpblts, struct module_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001006{
Michal Vasko77367452021-02-16 16:32:18 +01001007 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001008 char *module_cpblt, *ptr, *ptr2;
1009
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001010 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +02001011 (*result) = calloc(u + 1, sizeof **result);
1012 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +02001013 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +02001014 return -1;
Radek Krejcic9390502018-08-17 10:23:13 +02001015 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001016
1017 for (u = v = 0; cpblts[u]; ++u) {
1018 module_cpblt = strstr(cpblts[u], "module=");
1019 /* this capability requires a module */
1020 if (!module_cpblt) {
1021 continue;
1022 }
1023
1024 /* get module's name */
1025 ptr = (char *)module_cpblt + 7;
1026 ptr2 = strchr(ptr, '&');
1027 if (!ptr2) {
1028 ptr2 = ptr + strlen(ptr);
1029 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001030 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001031
1032 /* get module's revision */
1033 ptr = strstr(module_cpblt, "revision=");
1034 if (ptr) {
1035 ptr += 9;
1036 ptr2 = strchr(ptr, '&');
1037 if (!ptr2) {
1038 ptr2 = ptr + strlen(ptr);
1039 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001040 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001041 }
1042
1043 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001044 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001045
1046 /* get module's features */
1047 ptr = strstr(module_cpblt, "features=");
1048 if (ptr) {
1049 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +01001050 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001051 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
1052 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +01001053 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1054 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1055 (*result)[v].features[feature_count + 1] = NULL;
1056 ++feature_count;
1057
Radek Krejci65ef6d52018-08-16 16:35:02 +02001058 ptr2 = ptr + 1;
1059 }
1060 }
1061 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +01001062 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1063 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1064 (*result)[v].features[feature_count + 1] = NULL;
1065 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001066 }
1067 ++v;
1068 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001069
Michal Vasko303263d2021-10-05 12:18:21 +02001070 return 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001071}
1072
Michal Vaskoc088f982021-10-05 12:23:07 +02001073/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001074 * @brief Fill client context based on server modules info.
Michal Vaskoc088f982021-10-05 12:23:07 +02001075 *
1076 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001077 * @param[in] modules Server modules info.
1078 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001079 * @param[in] user_data User data for @p user_clb.
1080 * @param[in] has_get_schema Whether server supports get-schema RPC.
1081 * @return 0 on success.
1082 * @return -1 on error.
1083 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001084static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001085nc_ctx_fill(struct nc_session *session, struct module_info *modules, ly_module_imp_clb user_clb, void *user_data,
Michal Vasko77367452021-02-16 16:32:18 +01001086 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001087{
Michal Vasko303263d2021-10-05 12:18:21 +02001088 int ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +01001089 struct lys_module *mod;
1090 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001091
1092 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +02001093 /* skip import-only modules */
1094 if (!modules[u].implemented) {
1095 continue;
1096 }
1097
Radek Krejci65ef6d52018-08-16 16:35:02 +02001098 /* we can continue even if it fails */
Michal Vasko5e32c402022-01-12 14:05:09 +01001099 nc_ctx_load_module(session, modules[u].name, modules[u].revision, (const char **)modules[u].features, modules,
1100 user_clb, user_data, has_get_schema, &mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001101
1102 if (!mod) {
1103 if (session->status != NC_STATUS_RUNNING) {
1104 /* something bad heppened, discard the session */
Michal Vasko05532772021-06-03 12:12:38 +02001105 ERR(session, "Invalid session, discarding.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001106 goto cleanup;
1107 }
1108
Michal Vasko5ca5d972022-09-14 13:51:31 +02001109 /* all loading ways failed, the module will be ignored in the received data */
1110 WRN(session, "Failed to load module \"%s@%s\".", modules[u].name, modules[u].revision ?
Michal Vasko05532772021-06-03 12:12:38 +02001111 modules[u].revision : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001112 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001113 }
Michal Vasko60f66602017-10-17 13:52:18 +02001114 }
1115
Michal Vasko77367452021-02-16 16:32:18 +01001116 /* success */
Michal Vasko303263d2021-10-05 12:18:21 +02001117 ret = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001118
1119cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +02001120 return ret;
1121}
1122
Michal Vaskoc088f982021-10-05 12:23:07 +02001123/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001124 * @brief Fill client context with ietf-netconf module.
Michal Vaskoc088f982021-10-05 12:23:07 +02001125 *
1126 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001127 * @param[in] modules Server module info.
1128 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001129 * @param[in] user_data User data for @p user_clb.
1130 * @param[in] has_get_schema Whether server supports get-schema RPC.
1131 * @return 0 on success.
1132 * @return -1 on error.
1133 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001134static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001135nc_ctx_fill_ietf_netconf(struct nc_session *session, struct module_info *modules, ly_module_imp_clb user_clb,
Michal Vasko77367452021-02-16 16:32:18 +01001136 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001137{
Michal Vasko77367452021-02-16 16:32:18 +01001138 uint32_t u;
Michal Vasko5e32c402022-01-12 14:05:09 +01001139 const char **features = NULL;
1140 struct ly_in *in;
Michal Vasko77367452021-02-16 16:32:18 +01001141 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001142
Michal Vasko5e32c402022-01-12 14:05:09 +01001143 /* find supported features (capabilities) in ietf-netconf */
1144 for (u = 0; modules[u].name; ++u) {
1145 if (!strcmp(modules[u].name, "ietf-netconf")) {
1146 assert(modules[u].implemented);
1147 features = (const char **)modules[u].features;
1148 break;
1149 }
1150 }
1151 if (!modules[u].name) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001152 ERR(session, "Base NETCONF module not supported by the server.");
Michal Vasko5e32c402022-01-12 14:05:09 +01001153 return -1;
1154 }
1155
Michal Vasko77367452021-02-16 16:32:18 +01001156 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vasko5e32c402022-01-12 14:05:09 +01001157 if (ietfnc) {
1158 /* make sure to enable all the features if already loaded */
1159 lys_set_implemented(ietfnc, features);
1160 } else {
1161 /* load the module */
1162 nc_ctx_load_module(session, "ietf-netconf", NULL, features, modules, user_clb, user_data, has_get_schema, &ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001163 if (!ietfnc) {
Michal Vasko5e32c402022-01-12 14:05:09 +01001164 ly_in_new_memory(ietf_netconf_2013_09_29_yang, &in);
1165 lys_parse(session->ctx, in, LYS_IN_YANG, features, &ietfnc);
1166 ly_in_free(in, 0);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001167 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001168 }
1169 if (!ietfnc) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001170 ERR(session, "Loading base NETCONF module failed.");
Michal Vasko303263d2021-10-05 12:18:21 +02001171 return -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001172 }
1173
Radek Krejci65ef6d52018-08-16 16:35:02 +02001174 return 0;
1175}
1176
Michal Vasko78939072022-12-12 07:43:18 +01001177/**
1178 * @brief Set client session context to support schema-mount if possible.
1179 *
1180 * @param[in] session NC session with the context to modify.
1181 * @param[in] get_data_sup Whether get-data RPC is available or only get.
1182 * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
1183 * @return 0 on success.
1184 * @return -1 on error.
1185 */
1186static int
1187nc_ctx_schema_mount(struct nc_session *session, int get_data_sup, int xpath_sup)
1188{
1189 int rc = 0;
1190 struct lyd_node *oper_data = NULL;
1191
1192 if (session->flags & NC_SESSION_SHAREDCTX) {
1193 /* context is already fully set up */
1194 goto cleanup;
1195 }
1196
1197 /* get yang-library and schema-mounts operational data */
1198 if (xpath_sup) {
1199 if ((rc = get_oper_data(session, get_data_sup, "/ietf-yang-library:* | /ietf-yang-schema-mount:*", &oper_data))) {
1200 goto cleanup;
1201 }
1202 } else {
1203 if ((rc = get_oper_data(session, get_data_sup,
1204 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>"
1205 "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"/>", &oper_data))) {
1206 goto cleanup;
1207 }
1208 }
1209
1210 if (!oper_data || lyd_find_path(oper_data, "/ietf-yang-schema-mount:schema-mounts", 0, NULL)) {
1211 /* no schema-mounts operational data */
1212 goto cleanup;
1213 }
1214
1215 /* validate the data for the parent reference prefixes to be resolved */
1216 if (lyd_validate_all(&oper_data, NULL, LYD_VALIDATE_PRESENT, NULL)) {
1217 ERR(session, "Invalid operational data received from the server (%s).", ly_errmsg(LYD_CTX(oper_data)));
1218 rc = -1;
1219 goto cleanup;
1220 }
1221
1222 /* store the data in the session */
1223 session->opts.client.ext_data = oper_data;
1224 oper_data = NULL;
1225
1226cleanup:
1227 lyd_free_siblings(oper_data);
1228 return rc;
1229}
1230
Michal Vasko086311b2016-01-08 09:53:11 +01001231int
1232nc_ctx_check_and_fill(struct nc_session *session)
1233{
Michal Vaskod6c6f852022-12-14 15:37:21 +01001234 int i, get_schema_support = 0, yanglib_support = 0, xpath_support = 0, nmda_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +01001235 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001236 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001237 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +02001238 char *revision;
Michal Vasko5ca5d972022-09-14 13:51:31 +02001239 struct module_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001240
Michal Vasko2e6defd2016-10-07 15:48:15 +02001241 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +01001242
Radek Krejci65ef6d52018-08-16 16:35:02 +02001243 /* store the original user's callback, we will be switching between local search, get-schema and user callback */
Radek Krejcifd5b6682017-06-13 15:52:53 +02001244 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001245
Radek Krejci65ef6d52018-08-16 16:35:02 +02001246 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
Michal Vasko5ca5d972022-09-14 13:51:31 +02001247 * modules only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +01001248 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001249
1250 /* our callback is set later with appropriate data */
1251 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
1252
1253 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001254 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001255 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001256 get_schema_support = 1 + i;
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001257 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001258 yanglib_support = 1 + i;
Michal Vasko78939072022-12-12 07:43:18 +01001259 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:xpath:1.0", 44)) {
1260 xpath_support = 1 + i;
Michal Vaskod6c6f852022-12-14 15:37:21 +01001261 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-nmda", 45)) {
1262 nmda_support = 1 + i;
Michal Vasko086311b2016-01-08 09:53:11 +01001263 }
1264 }
Michal Vaskod6c6f852022-12-14 15:37:21 +01001265 VRB(session, "Capability for <get-schema> support%s found.", get_schema_support ? "" : " not");
1266 VRB(session, "Capability for yang-library support%s found.", yanglib_support ? "" : " not");
1267 VRB(session, "Capability for XPath filter support%s found.", xpath_support ? "" : " not");
1268 VRB(session, "Capability for NMDA RPCs support%s found.", nmda_support ? "" : " not");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001269
Michal Vasko5ca5d972022-09-14 13:51:31 +02001270 /* get information about server's modules from capabilities list until we will have yang-library */
1271 if (build_module_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
1272 ERR(session, "Unable to get server module information from the <hello>'s capabilities.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001273 goto cleanup;
1274 }
1275
Michal Vasko086311b2016-01-08 09:53:11 +01001276 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001277 if (get_schema_support && lys_parse_mem(session->ctx, ietf_netconf_monitoring_2010_10_04_yang, LYS_IN_YANG, NULL)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001278 WRN(session, "Loading NETCONF monitoring module failed, cannot use <get-schema>.");
Michal Vasko8a4e1462020-05-07 11:32:31 +02001279 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001280 }
1281
1282 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001283 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001284 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001285 }
1286
Radek Krejci65ef6d52018-08-16 16:35:02 +02001287 /* get correct version of ietf-yang-library into context */
1288 if (yanglib_support) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001289 /* use get-schema to get server's ietf-yang-library */
Radek Krejcib1d250e2018-04-12 13:54:18 +02001290 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1291 if (!revision) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001292 WRN(session, "Loading NETCONF ietf-yang-library module failed, missing revision in NETCONF <hello> message.");
Michal Vasko05532772021-06-03 12:12:38 +02001293 WRN(session, "Unable to automatically use <get-schema>.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001294 yanglib_support = 0;
1295 } else {
1296 revision = strndup(&revision[9], 10);
Michal Vasko5e32c402022-01-12 14:05:09 +01001297 if (nc_ctx_load_module(session, "ietf-yang-library", revision, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001298 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001299 WRN(session, "Loading NETCONF ietf-yang-library module failed, unable to use it to learn all "
Michal Vasko05532772021-06-03 12:12:38 +02001300 "the supported modules.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001301 yanglib_support = 0;
1302 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001303 if (strcmp(revision, "2019-01-04") >= 0) {
1304 /* we also need ietf-datastores to be implemented */
Michal Vasko5e32c402022-01-12 14:05:09 +01001305 if (nc_ctx_load_module(session, "ietf-datastores", NULL, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001306 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001307 WRN(session, "Loading NETCONF ietf-datastores module failed, unable to use yang-library "
Michal Vasko05532772021-06-03 12:12:38 +02001308 "to learn all the supported modules.");
Michal Vasko0d877f92020-04-02 13:52:43 +02001309 yanglib_support = 0;
1310 }
1311 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001312 free(revision);
1313 }
1314 }
1315
Michal Vaskod6c6f852022-12-14 15:37:21 +01001316 /* ietf-netconf-nmda is needed to issue get-data */
1317 if (nmda_support && nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, NULL, server_modules, old_clb, old_data,
1318 get_schema_support, &mod)) {
1319 WRN(session, "Loading NMDA module failed, unable to use <get-data>.");
1320 nmda_support = 0;
1321 }
1322
Michal Vasko5ca5d972022-09-14 13:51:31 +02001323 /* prepare structured information about server's modules */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001324 if (yanglib_support) {
Michal Vaskod6c6f852022-12-14 15:37:21 +01001325 if (build_module_info_yl(session, nmda_support, xpath_support, &sm)) {
Radek Krejcif0633792018-08-20 15:12:09 +02001326 goto cleanup;
1327 } else if (!sm) {
Michal Vasko05532772021-06-03 12:12:38 +02001328 VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
Radek Krejcif0633792018-08-20 15:12:09 +02001329 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001330 /* prefer yang-library information, currently we have it from capabilities used for getting correct
Michal Vasko5ca5d972022-09-14 13:51:31 +02001331 * yang-library module */
1332 free_module_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001333 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001334 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001335 }
Michal Vaskoef578332016-01-25 13:20:09 +01001336
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001337 /* compile all modules at once to avoid invalid errors or warnings */
1338 ly_ctx_set_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
1339
1340 /* fill the context */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001341 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1342 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001343 }
1344
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001345 /* compile it */
1346 if (ly_ctx_compile(session->ctx)) {
1347 goto cleanup;
1348 }
1349
Michal Vasko78939072022-12-12 07:43:18 +01001350 /* set support for schema-mount, if possible */
Michal Vaskod6c6f852022-12-14 15:37:21 +01001351 if (nc_ctx_schema_mount(session, nmda_support, xpath_support)) {
Michal Vasko78939072022-12-12 07:43:18 +01001352 goto cleanup;
1353 }
1354
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001355 /* success */
Radek Krejcifd5b6682017-06-13 15:52:53 +02001356 ret = 0;
1357
Michal Vaskoeee99412016-11-21 10:19:43 +01001358 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001359 WRN(session, "Some modules failed to be loaded, any data from these modules (and any other unknown) will "
Michal Vasko05532772021-06-03 12:12:38 +02001360 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001361 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001362
1363cleanup:
Michal Vasko5ca5d972022-09-14 13:51:31 +02001364 free_module_info(server_modules);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001365
Radek Krejcifd5b6682017-06-13 15:52:53 +02001366 /* set user callback back */
1367 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001368 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001369 ly_ctx_unset_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001370
Michal Vaskoef578332016-01-25 13:20:09 +01001371 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001372}
1373
1374API struct nc_session *
1375nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1376{
Michal Vaskod083db62016-01-19 10:31:29 +01001377 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001378
Michal Vasko45e53ae2016-04-07 11:46:03 +02001379 if (fdin < 0) {
1380 ERRARG("fdin");
1381 return NULL;
1382 } else if (fdout < 0) {
1383 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001384 return NULL;
1385 }
1386
1387 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001388 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001389 if (!session) {
1390 ERRMEM;
1391 return NULL;
1392 }
1393 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001394
1395 /* transport specific data */
1396 session->ti_type = NC_TI_FD;
1397 session->ti.fd.in = fdin;
1398 session->ti.fd.out = fdout;
1399
Michal Vasko78939072022-12-12 07:43:18 +01001400 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001401 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001402 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001403 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001404
1405 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001406 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001407 goto fail;
1408 }
1409 session->status = NC_STATUS_RUNNING;
1410
Michal Vaskoef578332016-01-25 13:20:09 +01001411 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001412 goto fail;
1413 }
1414
1415 return session;
1416
1417fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001418 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001419 return NULL;
1420}
1421
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001422API struct nc_session *
1423nc_connect_unix(const char *address, struct ly_ctx *ctx)
1424{
1425 struct nc_session *session = NULL;
1426 struct sockaddr_un sun;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001427 struct passwd *pw, pw_buf;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001428 char *username;
1429 int sock = -1;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001430 char *buf = NULL;
1431 size_t buf_size = 0;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001432
1433 if (address == NULL) {
1434 ERRARG("address");
1435 return NULL;
1436 }
1437
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001438 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1439 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001440 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001441 goto fail;
1442 }
1443
1444 memset(&sun, 0, sizeof(sun));
1445 sun.sun_family = AF_UNIX;
1446 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1447
1448 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001449 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001450 goto fail;
1451 }
1452
1453 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001454 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001455 goto fail;
1456 }
1457
1458 /* prepare session structure */
1459 session = nc_new_session(NC_CLIENT, 0);
1460 if (!session) {
1461 ERRMEM;
1462 goto fail;
1463 }
1464 session->status = NC_STATUS_STARTING;
1465
1466 /* transport specific data */
1467 session->ti_type = NC_TI_UNIX;
1468 session->ti.unixsock.sock = sock;
1469 sock = -1; /* do not close sock in fail label anymore */
1470
Michal Vasko78939072022-12-12 07:43:18 +01001471 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001472 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001473 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001474 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001475
Michal Vasko93224072021-11-09 12:14:28 +01001476 session->path = strdup(address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001477
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001478 pw = nc_getpwuid(geteuid(), &pw_buf, &buf, &buf_size);
1479 if (!pw) {
1480 ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid());
1481 goto fail;
1482 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001483 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001484 free(buf);
1485 if (!username) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001486 ERRMEM;
1487 goto fail;
1488 }
Michal Vasko93224072021-11-09 12:14:28 +01001489 session->username = username;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001490
1491 /* NETCONF handshake */
1492 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1493 goto fail;
1494 }
1495 session->status = NC_STATUS_RUNNING;
1496
1497 if (nc_ctx_check_and_fill(session) == -1) {
1498 goto fail;
1499 }
1500
1501 return session;
1502
1503fail:
1504 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001505 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001506 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001507 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001508 return NULL;
1509}
1510
Michal Vasko056f53c2022-10-21 13:38:15 +02001511/**
1512 * @brief Try to connect a socket, optionally a pending one from a previous attempt.
1513 *
1514 * @param[in] timeout_ms Timeout in ms to wait for the connection to be fully established, -1 to block.
1515 * @param[in,out] sock_pending Optional previously created socked that was not fully connected yet. If provided and
1516 * connected, is set to -1.
1517 * @param[in] res Addrinfo resource to use when creating a new socket.
1518 * @param[in] ka Keepalives to set.
1519 * @return Connected socket or -1 on error.
Frank Rimpler9f838b02018-07-25 06:44:03 +00001520 */
1521static int
Michal Vasko056f53c2022-10-21 13:38:15 +02001522sock_connect(int timeout_ms, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001523{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001524 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001525 int sock = -1;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001526 fd_set wset;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001527 struct timeval ts;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001528 socklen_t len = sizeof(int);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001529 struct in_addr *addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001530 uint16_t port;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001531 char str[INET6_ADDRSTRLEN];
Michal Vasko086311b2016-01-08 09:53:11 +01001532
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001533 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001534 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001535 sock = *sock_pending;
1536 } else {
1537 assert(res);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001538 if (res->ai_family == AF_INET6) {
1539 addr = (struct in_addr *) &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001540 port = ntohs(((struct sockaddr_in6 *)res->ai_addr)->sin6_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001541 } else {
1542 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001543 port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001544 }
1545 if (!inet_ntop(res->ai_family, addr, str, res->ai_addrlen)) {
Michal Vasko05532772021-06-03 12:12:38 +02001546 WRN(NULL, "inet_ntop() failed (%s).", strerror(errno));
Michal Vasko5e8d0192019-06-24 19:19:49 +02001547 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001548 VRB(NULL, "Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001549 }
1550
1551 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001552 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1553 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001554 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001555 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001556 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001557 /* make the socket non-blocking */
1558 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001559 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001560 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001561 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001562 /* non-blocking connect! */
1563 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1564 if (errno != EINPROGRESS) {
1565 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001566 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001567 goto cleanup;
1568 }
1569 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001570 }
Michal Vasko7d965dc2018-03-12 14:39:23 +01001571
Frank Rimpler9f838b02018-07-25 06:44:03 +00001572 FD_ZERO(&wset);
1573 FD_SET(sock, &wset);
1574
Michal Vasko056f53c2022-10-21 13:38:15 +02001575 /* wait for some data on the socket */
1576 if (timeout_ms != -1) {
1577 ts.tv_sec = timeout_ms / 1000;
1578 ts.tv_usec = (timeout_ms % 1000) * 1000;
1579 }
1580 if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout_ms != -1) ? &ts : NULL)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001581 ERR(NULL, "select() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001582 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001583 }
1584
Michal Vasko5e8d0192019-06-24 19:19:49 +02001585 if (ret == 0) {
1586 /* there was a timeout */
Michal Vasko056f53c2022-10-21 13:38:15 +02001587 VRB(NULL, "Timed out after %d ms (%s).", timeout_ms, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001588 if (sock_pending) {
1589 /* no sock-close, we'll try it again */
1590 *sock_pending = sock;
1591 } else {
1592 close(sock);
1593 }
1594 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001595 }
Radek Krejci782041a2018-08-20 10:09:45 +02001596
1597 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001598 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001599 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001600 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001601 goto cleanup;
1602 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001603 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001604 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001605 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001606 errno = error;
1607 goto cleanup;
1608 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001609
1610 /* enable keep-alive */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001611 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001612 goto cleanup;
1613 }
1614
Michal Vasko056f53c2022-10-21 13:38:15 +02001615 /* connected */
1616 if (sock_pending) {
1617 *sock_pending = -1;
1618 }
Michal Vasko086311b2016-01-08 09:53:11 +01001619 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001620
Frank Rimpler9f838b02018-07-25 06:44:03 +00001621cleanup:
1622 if (sock_pending) {
1623 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001624 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001625 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001626 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001627}
1628
Frank Rimpler9f838b02018-07-25 06:44:03 +00001629int
Michal Vasko056f53c2022-10-21 13:38:15 +02001630nc_sock_connect(const char *host, uint16_t port, int timeout_ms, struct nc_keepalives *ka, int *sock_pending, char **ip_host)
Frank Rimpler9f838b02018-07-25 06:44:03 +00001631{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001632 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001633 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001634 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001635 char *buf, port_s[6]; /* length of string representation of short int */
Michal Vasko66032bc2019-01-22 15:03:12 +01001636 void *addr;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001637
Michal Vasko056f53c2022-10-21 13:38:15 +02001638 DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout_ms, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001639
1640 /* no pending socket */
1641 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001642 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001643 snprintf(port_s, 6, "%u", port);
1644 memset(&hints, 0, sizeof hints);
1645 hints.ai_family = AF_UNSPEC;
1646 hints.ai_socktype = SOCK_STREAM;
1647 hints.ai_protocol = IPPROTO_TCP;
1648 i = getaddrinfo(host, port_s, &hints, &res_list);
1649 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001650 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001651 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001652 }
1653
1654 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vasko056f53c2022-10-21 13:38:15 +02001655 sock = sock_connect(timeout_ms, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001656 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001657 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001658 /* try the next resource */
1659 continue;
1660 } else {
1661 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001662 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001663 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001664 }
Michal Vasko05532772021-06-03 12:12:38 +02001665 VRB(NULL, "Successfully connected to %s:%s over %s.", host, port_s, (res->ai_family == AF_INET6) ? "IPv6" : "IPv4");
Michal Vasko83ad17e2019-01-30 10:11:37 +01001666
1667 opt = 1;
1668 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001669 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001670 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001671 }
1672
Michal Vasko66032bc2019-01-22 15:03:12 +01001673 if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) {
1674 buf = malloc(INET6_ADDRSTRLEN);
1675 if (!buf) {
1676 ERRMEM;
Michal Vaskocb846632019-12-13 15:12:45 +01001677 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001678 }
1679 if (res->ai_family == AF_INET) {
1680 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1681 } else {
1682 addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1683 }
1684 if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +02001685 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
Michal Vasko66032bc2019-01-22 15:03:12 +01001686 free(buf);
Michal Vaskocb846632019-12-13 15:12:45 +01001687 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001688 }
1689
1690 *ip_host = buf;
1691 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001692 break;
1693 }
1694 freeaddrinfo(res_list);
1695
1696 } else {
1697 /* try to get a connection with the pending socket */
1698 assert(sock_pending);
Michal Vasko056f53c2022-10-21 13:38:15 +02001699 sock = sock_connect(timeout_ms, sock_pending, NULL, ka);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001700 }
1701
1702 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001703
1704error:
Michal Vasko0be85692021-03-02 08:04:57 +01001705 if (res_list) {
1706 freeaddrinfo(res_list);
1707 }
Michal Vaskocb846632019-12-13 15:12:45 +01001708 if (sock != -1) {
1709 close(sock);
1710 }
1711 if (sock_pending) {
1712 *sock_pending = -1;
1713 }
1714 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001715}
1716
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001717#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001718
Michal Vasko3031aae2016-01-27 16:07:18 +01001719int
Michal Vasko9d4cca52022-09-07 11:19:57 +02001720nc_client_ch_add_bind_listen(const char *address, uint16_t port, const char *hostname, NC_TRANSPORT_IMPL ti)
Michal Vasko3031aae2016-01-27 16:07:18 +01001721{
1722 int sock;
1723
Michal Vasko45e53ae2016-04-07 11:46:03 +02001724 if (!address) {
1725 ERRARG("address");
1726 return -1;
1727 } else if (!port) {
1728 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001729 return -1;
1730 }
1731
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001732 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001733 if (sock == -1) {
1734 return -1;
1735 }
1736
1737 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001738 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1739 if (!client_opts.ch_binds) {
1740 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001741 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001742 return -1;
1743 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001744
Michal Vasko9d4cca52022-09-07 11:19:57 +02001745 client_opts.ch_binds_aux = nc_realloc(client_opts.ch_binds_aux, client_opts.ch_bind_count * sizeof *client_opts.ch_binds_aux);
1746 if (!client_opts.ch_binds_aux) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001747 ERRMEM;
1748 close(sock);
1749 return -1;
1750 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001751 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].ti = ti;
1752 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].hostname = hostname ? strdup(hostname) : NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001753
Michal Vasko3031aae2016-01-27 16:07:18 +01001754 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
1755 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1756 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001757 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001758
1759 return 0;
1760}
1761
1762int
1763nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1764{
1765 uint32_t i;
1766 int ret = -1;
1767
1768 if (!address && !port && !ti) {
1769 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1770 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001771 free(client_opts.ch_binds[i].address);
1772
1773 free(client_opts.ch_binds_aux[i].hostname);
Michal Vasko3031aae2016-01-27 16:07:18 +01001774
1775 ret = 0;
1776 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001777 client_opts.ch_bind_count = 0;
1778
Michal Vasko3031aae2016-01-27 16:07:18 +01001779 free(client_opts.ch_binds);
1780 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001781
1782 free(client_opts.ch_binds_aux);
1783 client_opts.ch_binds_aux = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001784 } else {
1785 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001786 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1787 (!port || (client_opts.ch_binds[i].port == port)) &&
Michal Vasko9d4cca52022-09-07 11:19:57 +02001788 (!ti || (client_opts.ch_binds_aux[i].ti == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001789 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001790 free(client_opts.ch_binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +01001791
1792 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001793 if (!client_opts.ch_bind_count) {
1794 free(client_opts.ch_binds);
1795 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001796
1797 free(client_opts.ch_binds_aux);
1798 client_opts.ch_binds_aux = NULL;
Michal Vasko66c762a2016-10-13 10:34:14 +02001799 } else if (i < client_opts.ch_bind_count) {
Michal Vasko9d4cca52022-09-07 11:19:57 +02001800 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count],
1801 sizeof *client_opts.ch_binds);
1802
1803 memcpy(&client_opts.ch_binds_aux[i], &client_opts.ch_binds_aux[client_opts.ch_bind_count],
1804 sizeof *client_opts.ch_binds_aux);
Michal Vasko66c762a2016-10-13 10:34:14 +02001805 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001806
1807 ret = 0;
1808 }
1809 }
1810 }
1811
1812 return ret;
1813}
1814
1815API int
1816nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1817{
1818 int sock;
1819 char *host = NULL;
1820 uint16_t port, idx;
1821
Michal Vasko45e53ae2016-04-07 11:46:03 +02001822 if (!client_opts.ch_binds) {
1823 ERRINIT;
1824 return -1;
1825 } else if (!session) {
1826 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001827 return -1;
1828 }
1829
1830 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001831 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001832 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001833 return sock;
1834 }
1835
Radek Krejci53691be2016-02-22 13:58:37 +01001836#ifdef NC_ENABLED_SSH
Michal Vasko9d4cca52022-09-07 11:19:57 +02001837 if (client_opts.ch_binds_aux[idx].ti == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001838 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001839 } else
1840#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001841#ifdef NC_ENABLED_TLS
Michal Vasko9d4cca52022-09-07 11:19:57 +02001842 if (client_opts.ch_binds_aux[idx].ti == NC_TI_OPENSSL) {
1843 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT,
1844 client_opts.ch_binds_aux[idx].hostname);
Michal Vasko3d865d22016-01-28 16:00:53 +01001845 } else
1846#endif
1847 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001848 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001849 *session = NULL;
1850 }
1851
1852 free(host);
1853
1854 if (!(*session)) {
1855 return -1;
1856 }
1857
1858 return 1;
1859}
1860
Radek Krejci53691be2016-02-22 13:58:37 +01001861#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001862
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001863API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001864nc_session_get_cpblts(const struct nc_session *session)
1865{
1866 if (!session) {
1867 ERRARG("session");
1868 return NULL;
1869 }
1870
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001871 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001872}
1873
1874API const char *
1875nc_session_cpblt(const struct nc_session *session, const char *capab)
1876{
1877 int i, len;
1878
1879 if (!session) {
1880 ERRARG("session");
1881 return NULL;
1882 } else if (!capab) {
1883 ERRARG("capab");
1884 return NULL;
1885 }
1886
1887 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001888 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1889 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1890 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001891 }
1892 }
1893
1894 return NULL;
1895}
1896
Michal Vasko9cd26a82016-05-31 08:58:48 +02001897API int
1898nc_session_ntf_thread_running(const struct nc_session *session)
1899{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001900 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001901 ERRARG("session");
1902 return 0;
1903 }
1904
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02001905 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running);
Michal Vasko9cd26a82016-05-31 08:58:48 +02001906}
1907
Michal Vaskob7558c52016-02-26 15:04:19 +01001908API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001909nc_client_init(void)
1910{
1911 nc_init();
1912}
1913
1914API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001915nc_client_destroy(void)
1916{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001917 nc_client_set_schema_searchpath(NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001918#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001919 nc_client_ch_del_bind(NULL, 0, 0);
1920#endif
1921#ifdef NC_ENABLED_SSH
1922 nc_client_ssh_destroy_opts();
1923#endif
1924#ifdef NC_ENABLED_TLS
1925 nc_client_tls_destroy_opts();
1926#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001927 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001928}
1929
Michal Vasko77367452021-02-16 16:32:18 +01001930static NC_MSG_TYPE
1931recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001932{
Michal Vasko77367452021-02-16 16:32:18 +01001933 char *ptr;
1934 struct lyd_attr *attr;
1935 uint64_t cur_msgid;
1936
1937 assert(envp && !envp->schema);
1938
1939 /* find the message-id attribute */
1940 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1941 if (!strcmp(attr->name.name, "message-id")) {
1942 break;
1943 }
1944 }
1945
1946 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02001947 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01001948 return NC_MSG_REPLY_ERR_MSGID;
1949 }
1950
1951 cur_msgid = strtoul(attr->value, &ptr, 10);
1952 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02001953 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1954 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01001955 return NC_MSG_REPLY_ERR_MSGID;
1956 }
1957
1958 return NC_MSG_REPLY;
1959}
1960
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001961/**
1962 * @brief Used to roughly estimate the type of the message, does not actually parse or verify it.
1963 *
1964 * @param[in] session NETCONF session used to send error messages.
1965 * @param[in] msg Message to check for type.
1966 * @return NC_MSG_REPLY If format roughly matches a rpc-reply;
1967 * @return NC_MSG_NOTIF If format roughly matches a notification;
1968 * @return NC_MSG_ERROR If format is malformed or unrecognized.
1969 */
1970static NC_MSG_TYPE
1971get_msg_type(struct nc_session *session, struct ly_in *msg)
1972{
1973 const char *str, *end;
1974
1975 str = ly_in_memory(msg, NULL);
1976
1977 while (*str) {
1978 /* Skip whitespaces */
1979 while (isspace(*str)) {
1980 str++;
1981 }
1982
1983 if (*str == '<') {
1984 str++;
1985 if (!strncmp(str, "!--", 3)) {
1986 /* Skip comments */
1987 end = "-->";
1988 str = strstr(str, end);
1989 } else if (!strncmp(str, "?xml", 4)) {
1990 /* Skip xml declaration */
1991 end = "?>";
1992 str = strstr(str, end);
1993 } else if (!strncmp(str, "rpc-reply", 9)) {
1994 return NC_MSG_REPLY;
1995 } else if (!strncmp(str, "notification", 12)) {
1996 return NC_MSG_NOTIF;
1997 } else {
1998 ERR(session, "Unknown xml element '%.10s'.", str);
1999 return NC_MSG_ERROR;
2000 }
2001 if (!str) {
2002 /* No matching ending tag found */
2003 ERR(session, "No matching ending tag '%s' found in xml message.", end);
2004 return NC_MSG_ERROR;
2005 }
2006 str += strlen(end);
2007 } else {
2008 /* Not a valid xml */
2009 ERR(session, "Unexpected character '%c' in xml message.", *str);
2010 return NC_MSG_ERROR;
2011 }
2012 }
2013
2014 /* Unexpected end of message */
2015 ERR(session, "Unexpected end of xml message.");
2016 return NC_MSG_ERROR;
2017}
2018
2019/**
2020 * @brief Function to receive either replies or notifications.
2021 *
2022 * @param[in] session NETCONF session from which this function receives messages.
2023 * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite.
2024 * @param[in] expected Type of the message the caller desired.
2025 * @param[out] message If receiving a message succeeded this is the message, NULL otherwise.
2026 * @return NC_MSG_REPLY If a rpc-reply was received;
2027 * @return NC_MSG_NOTIF If a notification was received;
Michal Vaskofdba4a32022-01-05 12:13:53 +01002028 * @return NC_MSG_ERROR If any error occurred;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002029 * @return NC_MSG_WOULDBLOCK If the timeout was reached.
2030 */
2031static NC_MSG_TYPE
2032recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct ly_in **message)
2033{
2034 struct nc_msg_cont **cont_ptr;
2035 struct ly_in *msg = NULL;
2036 struct nc_msg_cont *cont, *prev;
2037 NC_MSG_TYPE ret = NC_MSG_ERROR;
2038 int r;
2039
2040 *message = NULL;
2041
2042 /* MSGS LOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002043 r = nc_session_client_msgs_lock(session, &timeout, __func__);
2044 if (!r) {
2045 ret = NC_MSG_WOULDBLOCK;
2046 goto cleanup;
2047 } else if (r == -1) {
2048 ret = NC_MSG_ERROR;
2049 goto cleanup;
2050 }
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002051
2052 /* Find the expected message in the buffer */
2053 prev = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002054 for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002055 prev = cont;
2056 }
2057
2058 if (cont) {
2059 /* Remove found message from buffer */
2060 if (prev) {
2061 prev->next = cont->next;
2062 } else {
2063 session->opts.client.msgs = cont->next;
2064 }
2065
2066 /* Use the buffer message */
2067 ret = cont->type;
2068 msg = cont->msg;
2069 free(cont);
Michal Vasko01130bd2021-08-26 11:47:38 +02002070 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002071 }
2072
2073 /* Read a message from the wire */
2074 r = nc_read_msg_poll_io(session, timeout, &msg);
2075 if (!r) {
2076 ret = NC_MSG_WOULDBLOCK;
Michal Vasko01130bd2021-08-26 11:47:38 +02002077 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002078 } else if (r == -1) {
2079 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02002080 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002081 }
2082
2083 /* Basic check to determine message type */
2084 ret = get_msg_type(session, msg);
2085 if (ret == NC_MSG_ERROR) {
Michal Vasko01130bd2021-08-26 11:47:38 +02002086 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002087 }
2088
2089 /* If received a message of different type store it in the buffer */
2090 if (ret != expected) {
2091 cont_ptr = &session->opts.client.msgs;
2092 while (*cont_ptr) {
2093 cont_ptr = &((*cont_ptr)->next);
2094 }
2095 *cont_ptr = malloc(sizeof **cont_ptr);
2096 if (!*cont_ptr) {
2097 ERRMEM;
2098 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02002099 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002100 }
2101 (*cont_ptr)->msg = msg;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002102 msg = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002103 (*cont_ptr)->type = ret;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002104 (*cont_ptr)->next = NULL;
2105 }
2106
Michal Vasko01130bd2021-08-26 11:47:38 +02002107cleanup_unlock:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002108 /* MSGS UNLOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002109 nc_session_client_msgs_unlock(session, __func__);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002110
Michal Vasko01130bd2021-08-26 11:47:38 +02002111cleanup:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002112 if (ret == expected) {
2113 *message = msg;
2114 } else {
2115 ly_in_free(msg, 1);
2116 }
2117 return ret;
2118}
2119
Michal Vasko77367452021-02-16 16:32:18 +01002120static NC_MSG_TYPE
2121recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
2122{
Michal Vasko77367452021-02-16 16:32:18 +01002123 LY_ERR lyrc;
2124 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002125 NC_MSG_TYPE ret = NC_MSG_ERROR;
2126
2127 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
2128
2129 *envp = NULL;
2130
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002131 /* Receive messages until a rpc-reply is found or a timeout or error reached */
2132 ret = recv_msg(session, timeout, NC_MSG_REPLY, &msg);
2133 if (ret != NC_MSG_REPLY) {
Michal Vasko77367452021-02-16 16:32:18 +01002134 goto cleanup;
2135 }
2136
2137 /* parse */
2138 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
2139 if (!lyrc) {
2140 ret = recv_reply_check_msgid(session, *envp, msgid);
2141 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002142 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002143 ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op)));
Michal Vasko9a108052022-04-01 12:06:54 +02002144 lyd_free_tree(*envp);
2145 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002146 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002147 goto cleanup;
2148 }
2149
Michal Vasko77367452021-02-16 16:32:18 +01002150cleanup:
2151 ly_in_free(msg, 1);
2152 return ret;
2153}
2154
2155static int
2156recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
2157{
Michal Vasko143aa142021-10-01 15:31:48 +02002158 LY_ERR lyrc = LY_SUCCESS;
Michal Vasko77367452021-02-16 16:32:18 +01002159 struct nc_rpc_act_generic *rpc_gen;
2160 struct ly_in *in;
2161 struct lyd_node *tree, *op2;
2162 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01002163 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002164
2165 switch (rpc->type) {
2166 case NC_RPC_ACT_GENERIC:
2167 rpc_gen = (struct nc_rpc_act_generic *)rpc;
2168 if (rpc_gen->has_data) {
2169 tree = rpc_gen->content.data;
2170
2171 /* find the operation node */
2172 lyrc = LY_EINVAL;
2173 LYD_TREE_DFS_BEGIN(tree, op2) {
2174 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
2175 lyrc = lyd_dup_single(op2, NULL, 0, op);
2176 break;
2177 }
2178 LYD_TREE_DFS_END(tree, op2);
2179 }
2180 } else {
2181 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2182 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
2183 ly_in_free(in, 0);
2184 if (lyrc) {
Michal Vasko9a108052022-04-01 12:06:54 +02002185 lyd_free_tree(tree);
Michal Vasko77367452021-02-16 16:32:18 +01002186 return -1;
2187 }
2188
2189 /* we want just the operation node */
2190 lyrc = lyd_dup_single(op2, NULL, 0, op);
2191
2192 lyd_free_tree(tree);
2193 }
2194 break;
2195 case NC_RPC_GETCONFIG:
2196 module_name = "ietf-netconf";
2197 rpc_name = "get-config";
2198 break;
2199 case NC_RPC_EDIT:
2200 module_name = "ietf-netconf";
2201 rpc_name = "edit-config";
2202 break;
2203 case NC_RPC_COPY:
2204 module_name = "ietf-netconf";
2205 rpc_name = "copy-config";
2206 break;
2207 case NC_RPC_DELETE:
2208 module_name = "ietf-netconf";
2209 rpc_name = "delete-config";
2210 break;
2211 case NC_RPC_LOCK:
2212 module_name = "ietf-netconf";
2213 rpc_name = "lock";
2214 break;
2215 case NC_RPC_UNLOCK:
2216 module_name = "ietf-netconf";
2217 rpc_name = "unlock";
2218 break;
2219 case NC_RPC_GET:
2220 module_name = "ietf-netconf";
2221 rpc_name = "get";
2222 break;
2223 case NC_RPC_KILL:
2224 module_name = "ietf-netconf";
2225 rpc_name = "kill-session";
2226 break;
2227 case NC_RPC_COMMIT:
2228 module_name = "ietf-netconf";
2229 rpc_name = "commit";
2230 break;
2231 case NC_RPC_DISCARD:
2232 module_name = "ietf-netconf";
2233 rpc_name = "discard-changes";
2234 break;
2235 case NC_RPC_CANCEL:
2236 module_name = "ietf-netconf";
2237 rpc_name = "cancel-commit";
2238 break;
2239 case NC_RPC_VALIDATE:
2240 module_name = "ietf-netconf";
2241 rpc_name = "validate";
2242 break;
2243 case NC_RPC_GETSCHEMA:
2244 module_name = "ietf-netconf-monitoring";
2245 rpc_name = "get-schema";
2246 break;
2247 case NC_RPC_SUBSCRIBE:
2248 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02002249 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01002250 break;
2251 case NC_RPC_GETDATA:
2252 module_name = "ietf-netconf-nmda";
2253 rpc_name = "get-data";
2254 break;
2255 case NC_RPC_EDITDATA:
2256 module_name = "ietf-netconf-nmda";
2257 rpc_name = "edit-data";
2258 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002259 case NC_RPC_ESTABLISHSUB:
2260 module_name = "ietf-subscribed-notifications";
2261 rpc_name = "establish-subscription";
2262 break;
2263 case NC_RPC_MODIFYSUB:
2264 module_name = "ietf-subscribed-notifications";
2265 rpc_name = "modify-subscription";
2266 break;
2267 case NC_RPC_DELETESUB:
2268 module_name = "ietf-subscribed-notifications";
2269 rpc_name = "delete-subscription";
2270 break;
2271 case NC_RPC_KILLSUB:
2272 module_name = "ietf-subscribed-notifications";
2273 rpc_name = "kill-subscription";
2274 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002275 case NC_RPC_ESTABLISHPUSH:
2276 module_name = "ietf-subscribed-notifications";
2277 rpc_name = "establish-subscription";
2278 module_check = "ietf-yang-push";
2279 break;
2280 case NC_RPC_MODIFYPUSH:
2281 module_name = "ietf-subscribed-notifications";
2282 rpc_name = "modify-subscription";
2283 module_check = "ietf-yang-push";
2284 break;
2285 case NC_RPC_RESYNCSUB:
2286 module_name = "ietf-yang-push";
2287 rpc_name = "resync-subscription";
2288 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002289 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01002290 lyrc = LY_EINT;
2291 break;
2292 }
2293
2294 if (module_name && rpc_name) {
2295 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
2296 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002297 ERR(session, "Missing \"%s\" module in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01002298 return -1;
2299 }
2300
2301 /* create the operation node */
2302 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
2303 }
Michal Vasko305faca2021-03-25 09:16:02 +01002304 if (module_check) {
2305 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002306 ERR(session, "Missing \"%s\" module in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01002307 return -1;
2308 }
2309 }
Michal Vasko77367452021-02-16 16:32:18 +01002310
2311 if (lyrc) {
2312 return -1;
2313 }
2314 return 0;
2315}
2316
2317API NC_MSG_TYPE
2318nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
2319 struct lyd_node **op)
2320{
2321 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002322
Michal Vasko45e53ae2016-04-07 11:46:03 +02002323 if (!session) {
2324 ERRARG("session");
2325 return NC_MSG_ERROR;
2326 } else if (!rpc) {
2327 ERRARG("rpc");
2328 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002329 } else if (!envp) {
2330 ERRARG("envp");
Michal Vasko45e53ae2016-04-07 11:46:03 +02002331 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002332 } else if (!op) {
2333 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002334 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002335 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002336 ERR(session, "Invalid session to receive RPC replies.");
Michal Vasko086311b2016-01-08 09:53:11 +01002337 return NC_MSG_ERROR;
2338 }
Michal Vasko77367452021-02-16 16:32:18 +01002339
2340 /* get a duplicate of the RPC node to append reply to */
2341 if (recv_reply_dup_rpc(session, rpc, op)) {
2342 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01002343 }
Michal Vasko086311b2016-01-08 09:53:11 +01002344
Michal Vasko77367452021-02-16 16:32:18 +01002345 /* receive a reply */
2346 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01002347
Michal Vasko77367452021-02-16 16:32:18 +01002348 /* do not return the RPC copy on error or if the reply includes no data */
2349 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
2350 lyd_free_tree(*op);
2351 *op = NULL;
2352 }
2353 return ret;
2354}
2355
2356static NC_MSG_TYPE
2357recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
2358{
Michal Vasko77367452021-02-16 16:32:18 +01002359 LY_ERR lyrc;
2360 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002361 NC_MSG_TYPE ret = NC_MSG_ERROR;
2362
2363 *op = NULL;
2364 *envp = NULL;
2365
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002366 /* Receive messages until a notification is found or a timeout or error reached */
2367 ret = recv_msg(session, timeout, NC_MSG_NOTIF, &msg);
2368 if (ret != NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002369 goto cleanup;
2370 }
2371
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002372 /* Parse */
Michal Vasko77367452021-02-16 16:32:18 +01002373 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2374 if (!lyrc) {
Michal Vasko77367452021-02-16 16:32:18 +01002375 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002376 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002377 ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx));
Michal Vasko9a108052022-04-01 12:06:54 +02002378 lyd_free_tree(*envp);
2379 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002380 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002381 goto cleanup;
2382 }
2383
Michal Vasko77367452021-02-16 16:32:18 +01002384cleanup:
2385 ly_in_free(msg, 1);
2386 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002387}
2388
2389API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002390nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002391{
Michal Vasko45e53ae2016-04-07 11:46:03 +02002392 if (!session) {
2393 ERRARG("session");
2394 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002395 } else if (!envp) {
2396 ERRARG("envp");
2397 return NC_MSG_ERROR;
2398 } else if (!op) {
2399 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002400 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002401 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002402 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002403 return NC_MSG_ERROR;
2404 }
2405
Michal Vasko77367452021-02-16 16:32:18 +01002406 /* receive a notification */
2407 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002408}
2409
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002410static void *
2411nc_recv_notif_thread(void *arg)
2412{
2413 struct nc_ntf_thread_arg *ntarg;
2414 struct nc_session *session;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002415 nc_notif_dispatch_clb notif_clb;
2416 void *user_data;
Michal Vasko743ff9f2022-10-20 10:03:13 +02002417
Michal Vaskoffb35e92022-10-20 09:07:25 +02002418 void (*free_data)(void *);
Michal Vasko77367452021-02-16 16:32:18 +01002419 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002420 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002421
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002422 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002423 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002424
2425 ntarg = (struct nc_ntf_thread_arg *)arg;
2426 session = ntarg->session;
2427 notif_clb = ntarg->notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002428 user_data = ntarg->user_data;
2429 free_data = ntarg->free_data;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002430 free(ntarg);
2431
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002432 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running)) {
Michal Vasko77367452021-02-16 16:32:18 +01002433 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002434 if (msgtype == NC_MSG_NOTIF) {
Michal Vaskoffb35e92022-10-20 09:07:25 +02002435 notif_clb(session, envp, op, user_data);
Michal Vasko77367452021-02-16 16:32:18 +01002436 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2437 lyd_free_tree(envp);
Michal Vasko0c0239c2023-02-01 14:31:06 +01002438 lyd_free_all(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002439 break;
2440 }
Michal Vasko77367452021-02-16 16:32:18 +01002441 lyd_free_tree(envp);
Michal Vasko0c0239c2023-02-01 14:31:06 +01002442 lyd_free_all(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002443 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002444 /* quit this thread once the session is broken */
2445 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002446 }
2447
2448 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2449 }
2450
Michal Vasko05532772021-06-03 12:12:38 +02002451 VRB(session, "Notification thread exit.");
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002452 ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoffb35e92022-10-20 09:07:25 +02002453 if (free_data) {
2454 free_data(user_data);
2455 }
2456
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002457 return NULL;
2458}
2459
2460API int
Michal Vaskoffb35e92022-10-20 09:07:25 +02002461nc_recv_notif_dispatch(struct nc_session *session, nc_notif_dispatch_clb notif_clb)
2462{
2463 return nc_recv_notif_dispatch_data(session, notif_clb, NULL, NULL);
2464}
2465
2466API int
2467nc_recv_notif_dispatch_data(struct nc_session *session, nc_notif_dispatch_clb notif_clb, void *user_data,
2468 void (*free_data)(void *))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002469{
2470 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002471 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002472 int ret;
2473
Michal Vasko45e53ae2016-04-07 11:46:03 +02002474 if (!session) {
2475 ERRARG("session");
2476 return -1;
2477 } else if (!notif_clb) {
2478 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002479 return -1;
2480 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002481 ERR(session, "Invalid session to receive Notifications.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002482 return -1;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002483 }
2484
2485 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002486 if (!ntarg) {
2487 ERRMEM;
2488 return -1;
2489 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002490 ntarg->session = session;
2491 ntarg->notif_clb = notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002492 ntarg->user_data = user_data;
2493 ntarg->free_data = free_data;
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002494 ATOMIC_INC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002495
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002496 /* just so that nc_recv_notif_thread() does not immediately exit */
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002497 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002498
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002499 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002500 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002501 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002502 free(ntarg);
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002503 if (ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count) == 1) {
2504 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 0);
2505 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002506 return -1;
2507 }
2508
2509 return 0;
2510}
2511
Michal Vasko77367452021-02-16 16:32:18 +01002512static const char *
2513nc_wd2str(NC_WD_MODE wd)
2514{
2515 switch (wd) {
2516 case NC_WD_ALL:
2517 return "report-all";
2518 case NC_WD_ALL_TAG:
2519 return "report-all-tagged";
2520 case NC_WD_TRIM:
2521 return "trim";
2522 case NC_WD_EXPLICIT:
2523 return "explicit";
2524 default:
2525 break;
2526 }
2527
2528 return NULL;
2529}
2530
Michal Vasko086311b2016-01-08 09:53:11 +01002531API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002532nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002533{
2534 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002535 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002536 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002537 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002538 struct nc_rpc_getconfig *rpc_gc;
2539 struct nc_rpc_edit *rpc_e;
2540 struct nc_rpc_copy *rpc_cp;
2541 struct nc_rpc_delete *rpc_del;
2542 struct nc_rpc_lock *rpc_lock;
2543 struct nc_rpc_get *rpc_g;
2544 struct nc_rpc_kill *rpc_k;
2545 struct nc_rpc_commit *rpc_com;
2546 struct nc_rpc_cancel *rpc_can;
2547 struct nc_rpc_validate *rpc_val;
2548 struct nc_rpc_getschema *rpc_gs;
2549 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002550 struct nc_rpc_getdata *rpc_getd;
2551 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002552 struct nc_rpc_establishsub *rpc_estsub;
2553 struct nc_rpc_modifysub *rpc_modsub;
2554 struct nc_rpc_deletesub *rpc_delsub;
2555 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002556 struct nc_rpc_establishpush *rpc_estpush;
2557 struct nc_rpc_modifypush *rpc_modpush;
2558 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002559 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002560 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002561 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002562 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002563 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002564 uint64_t cur_msgid;
2565
Michal Vasko45e53ae2016-04-07 11:46:03 +02002566 if (!session) {
2567 ERRARG("session");
2568 return NC_MSG_ERROR;
2569 } else if (!rpc) {
2570 ERRARG("rpc");
2571 return NC_MSG_ERROR;
2572 } else if (!msgid) {
2573 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002574 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002575 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002576 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002577 return NC_MSG_ERROR;
2578 }
2579
Michal Vaskoc1171a42019-11-05 12:06:46 +01002580 switch (rpc->type) {
2581 case NC_RPC_ACT_GENERIC:
2582 /* checked when parsing */
2583 break;
2584 case NC_RPC_GETCONFIG:
2585 case NC_RPC_EDIT:
2586 case NC_RPC_COPY:
2587 case NC_RPC_DELETE:
2588 case NC_RPC_LOCK:
2589 case NC_RPC_UNLOCK:
2590 case NC_RPC_GET:
2591 case NC_RPC_KILL:
2592 case NC_RPC_COMMIT:
2593 case NC_RPC_DISCARD:
2594 case NC_RPC_CANCEL:
2595 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002596 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002597 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002598 ERR(session, "Missing \"ietf-netconf\" module in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002599 return NC_MSG_ERROR;
2600 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002601 break;
2602 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002603 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002604 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002605 ERR(session, "Missing \"ietf-netconf-monitoring\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002606 return NC_MSG_ERROR;
2607 }
2608 break;
2609 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002610 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002611 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002612 ERR(session, "Missing \"notifications\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002613 return NC_MSG_ERROR;
2614 }
2615 break;
2616 case NC_RPC_GETDATA:
2617 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002618 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002619 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002620 ERR(session, "Missing \"ietf-netconf-nmda\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002621 return NC_MSG_ERROR;
2622 }
2623 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002624 case NC_RPC_ESTABLISHSUB:
2625 case NC_RPC_MODIFYSUB:
2626 case NC_RPC_DELETESUB:
2627 case NC_RPC_KILLSUB:
2628 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2629 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002630 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002631 return NC_MSG_ERROR;
2632 }
2633 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002634 case NC_RPC_ESTABLISHPUSH:
2635 case NC_RPC_MODIFYPUSH:
2636 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2637 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002638 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002639 return NC_MSG_ERROR;
2640 }
2641 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2642 if (!mod2) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002643 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002644 return NC_MSG_ERROR;
2645 }
2646 break;
2647 case NC_RPC_RESYNCSUB:
2648 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2649 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002650 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002651 return NC_MSG_ERROR;
2652 }
2653 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002654 case NC_RPC_UNKNOWN:
2655 ERRINT;
2656 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002657 }
2658
Michal Vaskoab9deb62021-05-27 11:37:00 +02002659#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2660
Michal Vasko086311b2016-01-08 09:53:11 +01002661 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002662 case NC_RPC_ACT_GENERIC:
2663 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002664
2665 if (rpc_gen->has_data) {
2666 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002667 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002668 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002669 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2670 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2671 ly_in_free(in, 0);
2672 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002673 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002674 }
Michal Vasko086311b2016-01-08 09:53:11 +01002675 }
2676 break;
2677
2678 case NC_RPC_GETCONFIG:
2679 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2680
Michal Vaskoab9deb62021-05-27 11:37:00 +02002681 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2682 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2683 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002684 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002685 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002686 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node));
2687 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002688 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002689 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2690 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2691 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002692 }
2693 }
2694
2695 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002696 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002697 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002698 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002699 lyrc = LY_ENOTFOUND;
2700 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002701 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002702 CHECK_LYRC_BREAK(lyd_new_term(data, ietfncwd, "with-defaults", nc_wd2str(rpc_gc->wd_mode), 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002703 }
2704 break;
2705
2706 case NC_RPC_EDIT:
2707 rpc_e = (struct nc_rpc_edit *)rpc;
2708
Michal Vaskoab9deb62021-05-27 11:37:00 +02002709 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2710 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2711 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002712
2713 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002714 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_e->default_op], 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002715 }
Michal Vasko086311b2016-01-08 09:53:11 +01002716 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002717 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "test-option", rpcedit_testopt2str[rpc_e->test_opt], 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002718 }
Michal Vasko086311b2016-01-08 09:53:11 +01002719 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002720 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "error-option", rpcedit_erropt2str[rpc_e->error_opt], 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002721 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002722 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002723 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "config", rpc_e->edit_cont, 0, LYD_ANYDATA_XML, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002724 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002725 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002726 }
2727 break;
2728
2729 case NC_RPC_COPY:
2730 rpc_cp = (struct nc_rpc_copy *)rpc;
2731
Michal Vaskoab9deb62021-05-27 11:37:00 +02002732 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2733 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002734 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002735 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002736 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002737 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002738 }
2739
Michal Vaskoab9deb62021-05-27 11:37:00 +02002740 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002741 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002742 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002743 CHECK_LYRC_BREAK(lyd_new_any(cont, mod, "config", rpc_cp->url_config_src, 0, LYD_ANYDATA_XML, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002744 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002745 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002746 }
2747 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002748 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002749 }
2750
2751 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002752 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002753 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002754 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002755 lyrc = LY_ENOTFOUND;
2756 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002757 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002758 CHECK_LYRC_BREAK(lyd_new_term(data, ietfncwd, "with-defaults", nc_wd2str(rpc_cp->wd_mode), 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002759 }
2760 break;
2761
2762 case NC_RPC_DELETE:
2763 rpc_del = (struct nc_rpc_delete *)rpc;
2764
Michal Vaskoab9deb62021-05-27 11:37:00 +02002765 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2766 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002767 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002768 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002769 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002770 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002771 }
2772 break;
2773
2774 case NC_RPC_LOCK:
2775 rpc_lock = (struct nc_rpc_lock *)rpc;
2776
Michal Vaskoab9deb62021-05-27 11:37:00 +02002777 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2778 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2779 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002780 break;
2781
2782 case NC_RPC_UNLOCK:
2783 rpc_lock = (struct nc_rpc_lock *)rpc;
2784
Michal Vaskoab9deb62021-05-27 11:37:00 +02002785 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2786 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2787 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002788 break;
2789
2790 case NC_RPC_GET:
2791 rpc_g = (struct nc_rpc_get *)rpc;
2792
Michal Vaskoab9deb62021-05-27 11:37:00 +02002793 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002794 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002795 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002796 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node));
2797 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002798 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002799 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2800 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2801 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002802 }
2803 }
2804
2805 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002806 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002807 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002808 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002809 lyrc = LY_ENOTFOUND;
2810 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002811 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002812 CHECK_LYRC_BREAK(lyd_new_term(data, ietfncwd, "with-defaults", nc_wd2str(rpc_g->wd_mode), 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002813 }
2814 break;
2815
2816 case NC_RPC_KILL:
2817 rpc_k = (struct nc_rpc_kill *)rpc;
2818
Michal Vaskoab9deb62021-05-27 11:37:00 +02002819 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002820 sprintf(str, "%u", rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002821 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002822 break;
2823
2824 case NC_RPC_COMMIT:
2825 rpc_com = (struct nc_rpc_commit *)rpc;
2826
Michal Vaskoab9deb62021-05-27 11:37:00 +02002827 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002828 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002829 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002830 }
2831
2832 if (rpc_com->confirm_timeout) {
2833 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002834 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002835 }
Michal Vasko086311b2016-01-08 09:53:11 +01002836 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002837 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002838 }
Michal Vasko086311b2016-01-08 09:53:11 +01002839 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002840 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002841 }
2842 break;
2843
2844 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002845 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002846 break;
2847
2848 case NC_RPC_CANCEL:
2849 rpc_can = (struct nc_rpc_cancel *)rpc;
2850
Michal Vaskoab9deb62021-05-27 11:37:00 +02002851 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002852 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002853 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002854 }
2855 break;
2856
2857 case NC_RPC_VALIDATE:
2858 rpc_val = (struct nc_rpc_validate *)rpc;
2859
Michal Vaskoab9deb62021-05-27 11:37:00 +02002860 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2861 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002862 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002863 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002864 CHECK_LYRC_BREAK(lyd_new_any(cont, mod, "config", rpc_val->url_config_src, 0, LYD_ANYDATA_XML, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002865 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002866 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002867 }
2868 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002869 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002870 }
2871 break;
2872
2873 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002874 rpc_gs = (struct nc_rpc_getschema *)rpc;
2875
Michal Vaskoab9deb62021-05-27 11:37:00 +02002876 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2877 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002878 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002879 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002880 }
2881 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002882 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002883 }
2884 break;
2885
2886 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002887 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2888
Michal Vaskoab9deb62021-05-27 11:37:00 +02002889 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002890 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002891 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002892 }
2893
2894 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002895 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002896 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node));
2897 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002898 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002899 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2900 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2901 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002902 }
2903 }
Michal Vasko086311b2016-01-08 09:53:11 +01002904 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002905 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002906 }
Michal Vasko086311b2016-01-08 09:53:11 +01002907 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002908 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002909 }
2910 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002911
2912 case NC_RPC_GETDATA:
2913 rpc_getd = (struct nc_rpc_getdata *)rpc;
2914
Michal Vaskoab9deb62021-05-27 11:37:00 +02002915 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2916 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2917
Michal Vaskoc1171a42019-11-05 12:06:46 +01002918 if (rpc_getd->filter) {
2919 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002920 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "subtree-filter", rpc_getd->filter, 0, LYD_ANYDATA_XML, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002921 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002922 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002923 }
2924 }
2925 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002926 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002927 }
2928 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002929 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2930 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002931 }
2932 if (rpc_getd->max_depth) {
2933 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002934 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002935 }
2936 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002937 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002938 }
2939
2940 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002941 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002942 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-defaults", nc_wd2str(rpc_getd->wd_mode), 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002943 }
2944 break;
2945
2946 case NC_RPC_EDITDATA:
2947 rpc_editd = (struct nc_rpc_editdata *)rpc;
2948
Michal Vaskoab9deb62021-05-27 11:37:00 +02002949 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2950 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002951
2952 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002953 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2954 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002955 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002956 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002957 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "config", rpc_editd->edit_cont, 0, LYD_ANYDATA_XML, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002958 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002959 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002960 }
2961 break;
2962
Michal Vasko96f247a2021-03-15 13:32:10 +01002963 case NC_RPC_ESTABLISHSUB:
2964 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2965
Michal Vaskoab9deb62021-05-27 11:37:00 +02002966 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002967
2968 if (rpc_estsub->filter) {
2969 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002970 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML,
2971 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002972 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002973 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002974 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002975 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002976 }
2977 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002978 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002979
2980 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002981 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002982 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002983 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002984 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002985 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002986 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002987 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002988 }
2989 break;
2990
2991 case NC_RPC_MODIFYSUB:
2992 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2993
Michal Vaskoab9deb62021-05-27 11:37:00 +02002994 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002995
2996 sprintf(str, "%u", rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002997 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002998
2999 if (rpc_modsub->filter) {
3000 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003001 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML,
3002 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003003 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003004 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003005 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003006 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003007 }
3008 }
Michal Vasko96f247a2021-03-15 13:32:10 +01003009 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003010 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003011 }
3012 break;
3013
3014 case NC_RPC_DELETESUB:
3015 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
3016
Michal Vaskoab9deb62021-05-27 11:37:00 +02003017 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003018
3019 sprintf(str, "%u", rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003020 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003021 break;
3022
3023 case NC_RPC_KILLSUB:
3024 rpc_killsub = (struct nc_rpc_killsub *)rpc;
3025
Michal Vaskoab9deb62021-05-27 11:37:00 +02003026 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003027
3028 sprintf(str, "%u", rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003029 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003030 break;
3031
Michal Vasko305faca2021-03-25 09:16:02 +01003032 case NC_RPC_ESTABLISHPUSH:
3033 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
3034
Michal Vaskoab9deb62021-05-27 11:37:00 +02003035 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
3036 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003037
3038 if (rpc_estpush->filter) {
3039 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003040 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0,
3041 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003042 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003043 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003044 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003045 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003046 }
3047 }
3048
3049 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003050 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003051 }
Michal Vasko305faca2021-03-25 09:16:02 +01003052 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003053 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003054 }
3055
3056 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003057 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003058 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003059 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003060 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003061 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003062 }
3063 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003064 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003065 if (rpc_estpush->dampening_period) {
3066 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003067 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003068 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02003069 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
3070 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003071 if (rpc_estpush->excluded_change) {
3072 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003073 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
3074 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003075 }
3076 }
3077 }
3078 break;
3079
3080 case NC_RPC_MODIFYPUSH:
3081 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
3082
Michal Vaskoab9deb62021-05-27 11:37:00 +02003083 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01003084
3085 sprintf(str, "%u", rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003086 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
3087 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003088
3089 if (rpc_modpush->filter) {
3090 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003091 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0,
3092 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003093 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003094 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003095 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003096 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003097 }
3098 }
Michal Vasko305faca2021-03-25 09:16:02 +01003099 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003100 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003101 }
3102
3103 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003104 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003105 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003106 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003107 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003108 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003109 }
3110 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003111 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003112 if (rpc_modpush->dampening_period) {
3113 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003114 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003115 }
3116 }
3117 break;
3118
3119 case NC_RPC_RESYNCSUB:
3120 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
3121
Michal Vaskoab9deb62021-05-27 11:37:00 +02003122 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01003123 sprintf(str, "%u", rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003124 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003125 break;
3126
Michal Vasko96f247a2021-03-15 13:32:10 +01003127 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01003128 ERRINT;
3129 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01003130 }
3131
Michal Vaskoab9deb62021-05-27 11:37:00 +02003132#undef CHECK_LYRC_BREAK
3133
3134 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02003135 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02003136 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02003137 return NC_MSG_ERROR;
3138 }
3139
Michal Vasko131120a2018-05-29 15:44:02 +02003140 /* send RPC, store its message ID */
3141 r = nc_send_msg_io(session, timeout, data);
3142 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003143
Radek Krejcib4b19062018-02-07 16:33:06 +01003144 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01003145 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01003146 }
Michal Vasko086311b2016-01-08 09:53:11 +01003147
Michal Vasko131120a2018-05-29 15:44:02 +02003148 if (r == NC_MSG_RPC) {
3149 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003150 }
Michal Vasko131120a2018-05-29 15:44:02 +02003151 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01003152}
Michal Vaskode2946c2017-01-12 12:19:26 +01003153
3154API void
3155nc_client_session_set_not_strict(struct nc_session *session)
3156{
3157 if (session->side != NC_CLIENT) {
3158 ERRARG("session");
3159 return;
3160 }
3161
3162 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
3163}