blob: c850a8475795ccbca44770f7e064e2e95685543e [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
Radek Krejci62aa0642017-05-25 16:33:49 +020055char *sshauth_password(const char *username, const char *hostname, void *priv);
56char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020057char *sshauth_privkey_passphrase(const char *privkey_path, void *priv);
Radek Krejci62aa0642017-05-25 16:33:49 +020058#endif /* NC_ENABLED_SSH */
59
60static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
61static pthread_key_t nc_client_context_key;
62#ifdef __linux__
63static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020064 .opts.ka = {
65 .enabled = 1,
66 .idle_time = 1,
67 .max_probes = 10,
68 .probe_interval = 5
69 },
Radek Krejci62aa0642017-05-25 16:33:49 +020070#ifdef NC_ENABLED_SSH
71 .ssh_opts = {
roman41a11e42022-06-22 09:27:08 +020072 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Radek Krejci62aa0642017-05-25 16:33:49 +020073 .auth_password = sshauth_password,
74 .auth_interactive = sshauth_interactive,
75 .auth_privkey_passphrase = sshauth_privkey_passphrase
76 },
77 .ssh_ch_opts = {
78 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Radek Krejci62aa0642017-05-25 16:33:49 +020079 .auth_password = sshauth_password,
80 .auth_interactive = sshauth_interactive,
81 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020082 },
Radek Krejci62aa0642017-05-25 16:33:49 +020083#endif /* NC_ENABLED_SSH */
84 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020085 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020086};
87#endif
88
89static void
90nc_client_context_free(void *ptr)
91{
92 struct nc_client_context *c = (struct nc_client_context *)ptr;
93
Radek Krejci5cebc6b2017-05-26 13:24:38 +020094 if (--(c->refcount)) {
95 /* still used */
96 return;
97 }
98
Radek Krejci62aa0642017-05-25 16:33:49 +020099#ifdef __linux__
100 /* in __linux__ we use static memory in the main thread,
101 * so this check is for programs terminating the main()
102 * function by pthread_exit() :)
103 */
104 if (c != &context_main)
105#endif
106 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200107 /* for the main thread the same is done in nc_client_destroy() */
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400108 free(c->opts.schema_searchpath);
109
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200110#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400111 int i;
Michal Vasko292c5542023-02-01 14:33:17 +0100112
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400113 for (i = 0; i < c->opts.ch_bind_count; ++i) {
114 close(c->opts.ch_binds[i].sock);
115 free((char *)c->opts.ch_binds[i].address);
116 }
117 free(c->opts.ch_binds);
118 c->opts.ch_binds = NULL;
119 c->opts.ch_bind_count = 0;
Radek Krejci62aa0642017-05-25 16:33:49 +0200120#endif
121#ifdef NC_ENABLED_SSH
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400122 _nc_client_ssh_destroy_opts(&c->ssh_opts);
123 _nc_client_ssh_destroy_opts(&c->ssh_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200124#endif
125#ifdef NC_ENABLED_TLS
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400126 _nc_client_tls_destroy_opts(&c->tls_opts);
127 _nc_client_tls_destroy_opts(&c->tls_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200128#endif
129 free(c);
130 }
131}
132
133static void
134nc_client_context_createkey(void)
135{
136 int r;
137
138 /* initiate */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200139 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN) {}
Radek Krejci62aa0642017-05-25 16:33:49 +0200140 pthread_setspecific(nc_client_context_key, NULL);
141}
142
143struct nc_client_context *
144nc_client_context_location(void)
145{
146 struct nc_client_context *e;
147
148 pthread_once(&nc_client_context_once, nc_client_context_createkey);
149 e = pthread_getspecific(nc_client_context_key);
150 if (!e) {
151 /* prepare ly_err storage */
152#ifdef __linux__
153 if (getpid() == syscall(SYS_gettid)) {
154 /* main thread - use global variable instead of thread-specific variable. */
155 e = &context_main;
156 } else
157#endif /* __linux__ */
158 {
159 e = calloc(1, sizeof *e);
160 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200161 e->refcount = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200162#ifdef NC_ENABLED_SSH
163 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
roman41a11e42022-06-22 09:27:08 +0200164 e->ssh_opts.auth_pref[0].value = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200165 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
166 e->ssh_opts.auth_pref[1].value = 2;
167 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
roman41a11e42022-06-22 09:27:08 +0200168 e->ssh_opts.auth_pref[2].value = 3;
Radek Krejci62aa0642017-05-25 16:33:49 +0200169 e->ssh_opts.auth_password = sshauth_password;
170 e->ssh_opts.auth_interactive = sshauth_interactive;
171 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
172
roman41a11e42022-06-22 09:27:08 +0200173 /* callhome settings are the same */
Radek Krejci62aa0642017-05-25 16:33:49 +0200174 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
175 e->ssh_ch_opts.auth_pref[0].value = 1;
176 e->ssh_ch_opts.auth_pref[1].value = 2;
177 e->ssh_ch_opts.auth_pref[2].value = 3;
178#endif /* NC_ENABLED_SSH */
179 }
180 pthread_setspecific(nc_client_context_key, e);
181 }
182
183 return e;
184}
185
186#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100187
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200188API void *
189nc_client_get_thread_context(void)
190{
191 return nc_client_context_location();
192}
193
194API void
195nc_client_set_thread_context(void *context)
196{
197 struct nc_client_context *old, *new;
198
199 if (!context) {
200 ERRARG(context);
201 return;
202 }
203
204 new = (struct nc_client_context *)context;
205 old = nc_client_context_location();
206 if (old == new) {
207 /* nothing to change */
208 return;
209 }
210
211 /* replace old by new, increase reference counter in the newly set context */
212 nc_client_context_free(old);
213 new->refcount++;
214 pthread_setspecific(nc_client_context_key, new);
215}
216
Michal Vasko78939072022-12-12 07:43:18 +0100217/**
218 * @brief Ext data callback for a context to provide schema mount data.
219 */
220static LY_ERR
221nc_ly_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
222{
223 struct nc_session *session = user_data;
224
225 if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) {
226 return LY_EINVAL;
227 }
228
229 if (!session->opts.client.ext_data) {
230 ERR(session, "Unable to parse mounted data, no operational schema-mounts data received from the server.");
231 return LY_ENOTFOUND;
232 }
233
234 /* return ext data */
235 *ext_data = session->opts.client.ext_data;
236 *ext_data_free = 0;
237
238 return LY_SUCCESS;
239}
240
Radek Krejcifd5b6682017-06-13 15:52:53 +0200241int
Michal Vasko78939072022-12-12 07:43:18 +0100242nc_client_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200243{
244 /* assign context (dicionary needed for handshake) */
245 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100246 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200247 return EXIT_FAILURE;
248 }
249
Michal Vasko5ca5d972022-09-14 13:51:31 +0200250 /* user path must be first, the first path is used to store modules retreived via get-schema */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200251 if (client_opts.schema_searchpath) {
252 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko5d0cdc32022-12-12 07:38:21 +0100253 }
Michal Vasko80227222022-12-12 09:05:24 +0100254 if (!access(NC_CLIENT_SEARCH_DIR, F_OK)) {
255 ly_ctx_set_searchdir(ctx, NC_CLIENT_SEARCH_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200256 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200257
Michal Vasko5ca5d972022-09-14 13:51:31 +0200258 /* set callback for getting modules, if provided */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200259 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
Michal Vasko78939072022-12-12 07:43:18 +0100260
261 /* set ext data callback to avoid errors that no callback is set, the data are stored later, if any */
262 ly_ctx_set_ext_data_clb(ctx, nc_ly_ext_data_clb, session);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200263 } else {
264 session->flags |= NC_SESSION_SHAREDCTX;
265 }
266
267 session->ctx = ctx;
268
269 return EXIT_SUCCESS;
270}
271
Michal Vasko086311b2016-01-08 09:53:11 +0100272API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100273nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100274{
Michal Vasko3031aae2016-01-27 16:07:18 +0100275 if (client_opts.schema_searchpath) {
276 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100277 }
Michal Vasko086311b2016-01-08 09:53:11 +0100278
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100279 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100280 client_opts.schema_searchpath = strdup(path);
281 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100282 ERRMEM;
283 return 1;
284 }
285 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100286 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100287 }
288
289 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100290}
291
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100292API const char *
293nc_client_get_schema_searchpath(void)
294{
295 return client_opts.schema_searchpath;
296}
297
Radek Krejcifd5b6682017-06-13 15:52:53 +0200298API int
299nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100300{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200301 client_opts.schema_clb = clb;
302 if (clb) {
303 client_opts.schema_clb_data = user_data;
304 } else {
305 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100306 }
307
308 return 0;
309}
310
Radek Krejcifd5b6682017-06-13 15:52:53 +0200311API ly_module_imp_clb
312nc_client_get_schema_callback(void **user_data)
313{
314 if (user_data) {
315 (*user_data) = client_opts.schema_clb_data;
316 }
317 return client_opts.schema_clb;
318}
319
Michal Vaskoa272e092023-07-10 14:34:03 +0200320API void
321nc_client_set_new_session_context_autofill(int enabled)
322{
323 client_opts.auto_context_fill_disabled = !enabled;
324}
325
Michal Vasko5ca5d972022-09-14 13:51:31 +0200326struct module_info {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200327 char *name;
328 char *revision;
Michal Vasko6ce8e822022-08-02 15:09:17 +0200329
Radek Krejci65ef6d52018-08-16 16:35:02 +0200330 struct {
331 char *name;
332 char *revision;
333 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100334 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200335 int implemented;
336};
337
338struct clb_data_s {
339 void *user_data;
340 ly_module_imp_clb user_clb;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200341 struct module_info *modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200342 struct nc_session *session;
343 int has_get_schema;
344};
345
Michal Vaskoc088f982021-10-05 12:23:07 +0200346/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200347 * @brief Retrieve YANG module content from a local file.
Michal Vaskoc088f982021-10-05 12:23:07 +0200348 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200349 * @param[in] name Module name.
350 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200351 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200352 * @param[out] format Module format.
353 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200354 */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200355static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200356retrieve_module_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200357 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100358{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200359 char *localfile = NULL;
360 FILE *f;
361 long length, l;
362 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100363
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200364 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200365 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
366 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200367 return NULL;
368 }
369 if (localfile) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200370 VRB(clb_data->session, "Reading module \"%s@%s\" from local file \"%s\".", name, rev ? rev : "<latest>",
Michal Vasko7502b602022-08-26 11:51:17 +0200371 localfile);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200372 f = fopen(localfile, "r");
373 if (!f) {
Michal Vasko7502b602022-08-26 11:51:17 +0200374 ERR(clb_data->session, "Unable to open file \"%s\" (%s).", localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200375 free(localfile);
376 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100377 }
Michal Vasko086311b2016-01-08 09:53:11 +0100378
Radek Krejci65ef6d52018-08-16 16:35:02 +0200379 fseek(f, 0, SEEK_END);
380 length = ftell(f);
Radek Krejci3f499a62018-08-17 10:16:57 +0200381 if (length < 0) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200382 ERR(clb_data->session, "Unable to get the size of module file \"%s\".", localfile);
Radek Krejci3f499a62018-08-17 10:16:57 +0200383 free(localfile);
384 fclose(f);
385 return NULL;
386 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200387 fseek(f, 0, SEEK_SET);
388
389 model_data = malloc(length + 1);
390 if (!model_data) {
391 ERRMEM;
392 } else if ((l = fread(model_data, 1, length, f)) != length) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200393 ERR(clb_data->session, "Reading module from \"%s\" failed (%d bytes read, but %d expected).", localfile, l,
Michal Vasko05532772021-06-03 12:12:38 +0200394 length);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200395 free(model_data);
396 model_data = NULL;
397 } else {
398 /* terminating NULL byte */
399 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100400 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200401 fclose(f);
402 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100403 }
404
Radek Krejci65ef6d52018-08-16 16:35:02 +0200405 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100406}
407
Michal Vaskoc088f982021-10-05 12:23:07 +0200408/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200409 * @brief Retrieve YANG module content from a reply to get-schema RPC.
Michal Vaskoc088f982021-10-05 12:23:07 +0200410 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200411 * @param[in] name Module name.
412 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200413 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200414 * @param[out] format Module format.
415 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200416 */
Michal Vasko086311b2016-01-08 09:53:11 +0100417static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200418retrieve_module_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200419 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100420{
Michal Vasko086311b2016-01-08 09:53:11 +0100421 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100422 struct lyd_node *envp = NULL, *op = NULL;
423 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100424 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100425 uint64_t msgid;
Michal Vasko7502b602022-08-26 11:51:17 +0200426 char *localfile = NULL, *envp_str = NULL, *model_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200427 FILE *f;
Michal Vasko086311b2016-01-08 09:53:11 +0100428
Michal Vasko5ca5d972022-09-14 13:51:31 +0200429 VRB(clb_data->session, "Reading module \"%s@%s\" from server via get-schema.", name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200430 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100431
Radek Krejci65ef6d52018-08-16 16:35:02 +0200432 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100433 usleep(1000);
434 }
435 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200436 ERR(clb_data->session, "Failed to send the <get-schema> RPC.");
Michal Vasko086311b2016-01-08 09:53:11 +0100437 nc_rpc_free(rpc);
438 return NULL;
439 }
440
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200441 do {
Michal Vasko77367452021-02-16 16:32:18 +0100442 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200443 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100444 nc_rpc_free(rpc);
445 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200446 ERR(clb_data->session, "Timeout for receiving reply to a <get-schema> expired.");
Michal Vasko77367452021-02-16 16:32:18 +0100447 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100448 } else if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200449 ERR(clb_data->session, "Failed to receive a reply to <get-schema>.");
Michal Vasko77367452021-02-16 16:32:18 +0100450 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100451 } else if (!op) {
Michal Vasko7502b602022-08-26 11:51:17 +0200452 assert(envp);
453 lyd_print_mem(&envp_str, envp, LYD_XML, 0);
454 WRN(clb_data->session, "Received an unexpected reply to <get-schema>:\n%s", envp_str);
455 free(envp_str);
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100456 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100457 }
458
Michal Vasko77367452021-02-16 16:32:18 +0100459 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Michal Vasko05532772021-06-03 12:12:38 +0200460 ERR(clb_data->session, "Unexpected data in reply to a <get-schema> RPC.");
Michal Vasko77367452021-02-16 16:32:18 +0100461 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200462 }
Michal Vasko77367452021-02-16 16:32:18 +0100463 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200464 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200465 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100466 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200467 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200468 break;
469 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100470 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200471 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200472 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200473 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200474 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100475 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200476 }
Michal Vasko086311b2016-01-08 09:53:11 +0100477
Radek Krejci488b0702018-08-29 14:47:15 +0200478 if (model_data && !model_data[0]) {
479 /* empty data */
480 free(model_data);
481 model_data = NULL;
482 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100483 if (!model_data) {
484 goto cleanup;
485 }
486
487 /* set format */
488 *format = LYS_IN_YANG;
Radek Krejci488b0702018-08-29 14:47:15 +0200489
Michal Vasko5ca5d972022-09-14 13:51:31 +0200490 /* try to store the model_data into local module repository */
Michal Vaskoa1721172022-12-12 09:06:53 +0100491 lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx), 0, name, rev, &localfile, NULL);
492 if (client_opts.schema_searchpath && !localfile) {
493 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
494 rev ? rev : "") == -1) {
495 ERRMEM;
496 } else {
497 f = fopen(localfile, "w");
498 if (!f) {
499 WRN(clb_data->session, "Unable to store \"%s\" as a local copy of module retrieved via <get-schema> (%s).",
500 localfile, strerror(errno));
Michal Vaskof945da52018-02-15 08:45:13 +0100501 } else {
Michal Vaskoa1721172022-12-12 09:06:53 +0100502 fputs(model_data, f);
503 fclose(f);
Michal Vaskof945da52018-02-15 08:45:13 +0100504 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200505 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200506 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100507 free(localfile);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200508
Michal Vasko77367452021-02-16 16:32:18 +0100509cleanup:
510 lyd_free_tree(envp);
511 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100512 return model_data;
513}
514
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200515static void
516free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200517{
518 free(data);
519 (void)user_data;
520}
521
Michal Vaskoc088f982021-10-05 12:23:07 +0200522/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200523 * @brief Retrieve YANG module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200524 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200525 * @param[in] mod_name Module name.
526 * @param[in] mod_rev Module revision.
Michal Vasko5e32c402022-01-12 14:05:09 +0100527 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200528 * @param[out] format Module format.
529 * @param[out] module_data Module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100530 * @param[out] free_module_data Callback for freeing @p module_data.
531 * @return LY_ERR value.
532 */
533static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200534retrieve_module_data(const char *mod_name, const char *mod_rev, void *user_data, LYS_INFORMAT *format,
Michal Vasko5e32c402022-01-12 14:05:09 +0100535 const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
536{
537 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
538 char *model_data = NULL;
539
Michal Vasko5e32c402022-01-12 14:05:09 +0100540 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200541 model_data = retrieve_module_data_localfile(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100542
543 /* 2. try to use <get-schema> */
544 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200545 model_data = retrieve_module_data_getschema(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100546 }
547
548 /* 3. try to use user callback */
549 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200550 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 +0100551 clb_data->user_clb(mod_name, mod_rev, NULL, NULL, clb_data->user_data, format, (const char **)&model_data,
552 free_module_data);
553 }
554
555 *free_module_data = free_with_user_data;
556 *module_data = model_data;
557 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
558}
559
560/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200561 * @brief Retrieve YANG import module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100562 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200563 * @param[in] mod_name Module name.
564 * @param[in] mod_rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200565 * @param[in] submod_name Optional submodule name.
566 * @param[in] sub_rev Submodule revision.
567 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200568 * @param[out] format Module format.
569 * @param[out] module_data Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200570 * @param[out] free_module_data Callback for freeing @p module_data.
571 * @return LY_ERR value.
572 */
Michal Vasko77367452021-02-16 16:32:18 +0100573static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200574retrieve_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 +0100575 void *user_data, LYS_INFORMAT *format, const char **module_data,
576 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200577{
578 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
Michal Vasko5e32c402022-01-12 14:05:09 +0100579 uint32_t u, v, match = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200580 const char *name = NULL, *rev = NULL;
581 char *model_data = NULL;
582
Michal Vasko5ca5d972022-09-14 13:51:31 +0200583 /* get and check the final name and revision of the module to be retrieved */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200584 if (!mod_rev || !mod_rev[0]) {
585 /* newest revision requested - get the newest revision from the list of available modules on server */
586 match = 0;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200587 for (u = 0; clb_data->modules[u].name; ++u) {
588 if (strcmp(mod_name, clb_data->modules[u].name)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200589 continue;
590 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200591 if (!match || (strcmp(mod_rev, clb_data->modules[u].revision) > 0)) {
592 mod_rev = clb_data->modules[u].revision;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200593 }
594 match = u + 1;
595 }
596 if (!match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200597 /* valid situation if we are retrieving YANG 1.1 module and have only capabilities for now
Michal Vasko1d2665c2021-02-12 09:28:44 +0100598 * (when loading ietf-datastore for ietf-yang-library) */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200599 VRB(clb_data->session, "Unable to identify revision of the import module \"%s\" from "
Michal Vasko05532772021-06-03 12:12:38 +0200600 "the available server side information.", mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200601 }
602 }
603 if (submod_name) {
604 name = submod_name;
605 if (sub_rev) {
606 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200607 } else if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200608 if (!clb_data->modules[match - 1].submodules) {
Michal Vasko05532772021-06-03 12:12:38 +0200609 VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", "
Michal Vasko5ca5d972022-09-14 13:51:31 +0200610 "in import module \"%s\", from the available server side information.", submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200611 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200612 for (v = 0; clb_data->modules[match - 1].submodules[v].name; ++v) {
613 if (!strcmp(submod_name, clb_data->modules[match - 1].submodules[v].name)) {
614 rev = sub_rev = clb_data->modules[match - 1].submodules[v].revision;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200615 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200616 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200617 if (!rev) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200618 ERR(clb_data->session, "Requested submodule \"%s\" is not found in import module \"%s\" on server side.",
Michal Vasko05532772021-06-03 12:12:38 +0200619 submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100620 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200621 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200622 }
623 }
624 } else {
625 name = mod_name;
626 rev = mod_rev;
627 }
628
Radek Krejci65ef6d52018-08-16 16:35:02 +0200629 if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200630 /* we have enough information to avoid communication with server and try to get the module locally */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200631
632 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200633 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200634
635 /* 2. try to use <get-schema> */
636 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200637 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200638 }
639 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200640 /* we are unsure which revision of the module we should load, so first try to get
Radek Krejci65ef6d52018-08-16 16:35:02 +0200641 * the newest revision from the server via get-schema and only if the server does not
642 * implement get-schema, try to load the newest revision locally. This is imperfect
643 * solution, but there are situation when a client does not know what revision is
644 * actually implemented by the server. */
645
646 /* 1. try to use <get-schema> */
647 if (clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200648 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200649 }
650
651 /* 2. try to get data locally */
652 if (!model_data) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200653 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200654 }
655 }
656
657 /* 3. try to use user callback */
658 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200659 VRB(clb_data->session, "Reading module \"%s@%s\" via user callback.", name, rev ? rev : "<latest>");
Michal Vasko77367452021-02-16 16:32:18 +0100660 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
661 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200662 }
663
Jan Kundrát35972df2018-09-06 19:00:01 +0200664 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100665 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200666 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200667}
668
Michal Vaskoc088f982021-10-05 12:23:07 +0200669/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200670 * @brief Load a YANG module into context.
Michal Vaskoc088f982021-10-05 12:23:07 +0200671 *
672 * @param[in] session NC session.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200673 * @param[in] name Module name.
674 * @param[in] revision Module revision.
675 * @param[in] features Enabled module features.
676 * @param[in] modules Server module info built from capabilities.
677 * @param[in] user_clb User callback for retrieving module data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200678 * @param[in] user_data User data for @p user_clb.
679 * @param[in] has_get_schema Whether the server supports get-schema.
680 * @param[out] mod Loaded module.
681 * @return 0 on success.
682 * @return -1 on error.
683 */
Michal Vaskoceae0152018-02-14 16:03:59 +0100684static int
Michal Vasko5e32c402022-01-12 14:05:09 +0100685nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, const char **features,
Michal Vasko5ca5d972022-09-14 13:51:31 +0200686 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 +0100687{
688 int ret = 0;
689 struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200690 const char *module_data = NULL;
Michal Vasko5e32c402022-01-12 14:05:09 +0100691 struct ly_in *in;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200692 LYS_INFORMAT format;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200693
694 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200695 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100696
Michal Vasko5e32c402022-01-12 14:05:09 +0100697 /* try to use a module from the context */
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200698 *mod = ly_ctx_get_module_implemented(session->ctx, name);
699 if (!*mod) {
700 if (revision) {
701 *mod = ly_ctx_get_module(session->ctx, name, revision);
702 } else {
Michal Vasko5e32c402022-01-12 14:05:09 +0100703 *mod = ly_ctx_get_module_latest(session->ctx, name);
704 }
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200705 } else if (revision && (!(*mod)->revision || strcmp((*mod)->revision, revision))) {
706 WRN(session, "Server implements module \"%s\" in revision \"%s\" but revision \"%s\" is already implemented"
707 " and will be used instead.", name, revision, (*mod)->revision ? (*mod)->revision : "<none>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200708 }
Michal Vasko5e32c402022-01-12 14:05:09 +0100709
Michal Vaskoceae0152018-02-14 16:03:59 +0100710 if (*mod) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100711 /* make the present module implemented and/or enable all its features */
712 if (lys_set_implemented(*mod, features)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200713 ERR(session, "Failed to implement module \"%s\".", (*mod)->name);
Michal Vasko5e32c402022-01-12 14:05:09 +0100714 ret = -1;
Michal Vaskoceae0152018-02-14 16:03:59 +0100715 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200716 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100717 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200718 clb_data.has_get_schema = has_get_schema;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200719 clb_data.modules = modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200720 clb_data.session = session;
721 clb_data.user_clb = user_clb;
722 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100723
724 /* clear all the errors and just collect them for now */
725 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200726 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100727
Radek Krejci65ef6d52018-08-16 16:35:02 +0200728 /* get module data */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200729 retrieve_module_data(name, revision, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100730
Radek Krejci65ef6d52018-08-16 16:35:02 +0200731 if (module_data) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100732 /* set import callback */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200733 ly_ctx_set_module_imp_clb(session->ctx, retrieve_module_data_imp, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100734
Michal Vasko5ca5d972022-09-14 13:51:31 +0200735 /* parse the module */
Michal Vasko5e32c402022-01-12 14:05:09 +0100736 ly_in_new_memory(module_data, &in);
737 lys_parse(session->ctx, in, format, features, mod);
738 ly_in_free(in, 0);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200739 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100740 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100741 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100742
Radek Krejci65ef6d52018-08-16 16:35:02 +0200743 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
744 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100745
Michal Vaskodbf7c272018-02-19 09:07:59 +0100746 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200747 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100748 if (!(*mod)) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100749 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100750 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100751 }
752 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200753 } else {
754 /* print only warnings */
Michal Vasko5e32c402022-01-12 14:05:09 +0100755 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200756 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100757 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200758 }
759 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100760 }
761
Michal Vaskodbf7c272018-02-19 09:07:59 +0100762 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100763 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100764 }
765
766 return ret;
767}
768
Radek Krejci65ef6d52018-08-16 16:35:02 +0200769static void
Michal Vasko5ca5d972022-09-14 13:51:31 +0200770free_module_info(struct module_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200771{
Michal Vasko77367452021-02-16 16:32:18 +0100772 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200773
Radek Krejci65ef6d52018-08-16 16:35:02 +0200774 if (!list) {
775 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200776 }
777
Radek Krejci65ef6d52018-08-16 16:35:02 +0200778 for (u = 0; list[u].name; ++u) {
779 free(list[u].name);
780 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100781 if (list[u].features) {
782 for (v = 0; list[u].features[v]; ++v) {
783 free(list[u].features[v]);
784 }
785 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200786 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200787 if (list[u].submodules) {
788 for (v = 0; list[u].submodules[v].name; ++v) {
789 free(list[u].submodules[v].name);
790 free(list[u].submodules[v].revision);
791 }
792 free(list[u].submodules);
793 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200794 }
795 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200796}
797
Michal Vaskoc088f982021-10-05 12:23:07 +0200798/**
Michal Vasko78939072022-12-12 07:43:18 +0100799 * @brief Retrieve yang-library and schema-mounts operational data from the server.
Michal Vaskoc088f982021-10-05 12:23:07 +0200800 *
801 * @param[in] session NC session.
802 * @param[in] has_get_data Whether get-data RPC is available or only get.
Michal Vasko78939072022-12-12 07:43:18 +0100803 * @param[in] filter Filter to use.
804 * @param[out] oper_data Received data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200805 * @return 0 on success.
806 * @return -1 on error.
807 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200808static int
Michal Vasko78939072022-12-12 07:43:18 +0100809get_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 +0200810{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200811 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100812 struct lyd_node *op = NULL, *envp = NULL;
813 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200814 NC_MSG_TYPE msg;
815 uint64_t msgid;
Michal Vasko303263d2021-10-05 12:18:21 +0200816 int ret = 0;
817 const char *rpc_name;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200818
Michal Vasko78939072022-12-12 07:43:18 +0100819 /* get data from the server */
Michal Vasko303263d2021-10-05 12:18:21 +0200820 if (has_get_data) {
Michal Vasko78939072022-12-12 07:43:18 +0100821 rpc_name = "<get-data>";
822 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 +0200823 } else {
Michal Vasko78939072022-12-12 07:43:18 +0100824 rpc_name = "<get>";
825 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200826 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200827 if (!rpc) {
828 goto cleanup;
829 }
830
831 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
832 usleep(1000);
833 }
834 if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100835 WRN(session, "Failed to send %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200836 goto cleanup;
837 }
838
839 do {
Michal Vasko77367452021-02-16 16:32:18 +0100840 lyd_free_tree(envp);
841 lyd_free_tree(op);
842
843 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200844 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200845 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko78939072022-12-12 07:43:18 +0100846 WRN(session, "Timeout for receiving reply to a %s RPC expired.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200847 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100848 } else if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100849 WRN(session, "Failed to receive a reply to %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200850 goto cleanup;
Jan Kundrát1d73a5a2022-03-30 16:54:49 +0200851 } else if (!op || !lyd_child(op) || !lyd_child(op)->schema || strcmp(lyd_child(op)->schema->name, "data")) {
Michal Vasko78939072022-12-12 07:43:18 +0100852 WRN(session, "Unexpected reply without data to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200853 goto cleanup;
854 }
855
Michal Vasko77367452021-02-16 16:32:18 +0100856 data = (struct lyd_node_any *)lyd_child(op);
857 if (data->value_type != LYD_ANYDATA_DATATREE) {
Michal Vasko78939072022-12-12 07:43:18 +0100858 WRN(session, "Unexpected data in reply to a %s RPC.", rpc_name);
Michal Vasko40528752021-06-21 15:41:51 +0200859 goto cleanup;
860 } else if (!data->value.tree) {
Michal Vasko78939072022-12-12 07:43:18 +0100861 WRN(session, "No data in reply to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200862 goto cleanup;
863 }
864
Michal Vasko78939072022-12-12 07:43:18 +0100865 *oper_data = data->value.tree;
866 data->value.tree = NULL;
867
868cleanup:
869 nc_rpc_free(rpc);
870 lyd_free_tree(envp);
871 lyd_free_tree(op);
872
873 if (session->status != NC_STATUS_RUNNING) {
874 /* something bad happened, discard the session */
875 ERR(session, "Invalid session, discarding.");
876 ret = -1;
877 }
878
879 return ret;
880}
881
882/**
883 * @brief Build server module info from ietf-yang-library data.
884 *
885 * @param[in] session NC session.
886 * @param[in] get_data_sup Whether get-data RPC is available or only get.
887 * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
888 * @param[out] result Server modules.
889 * @return 0 on success.
890 * @return -1 on error.
891 */
892static int
893build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup, struct module_info **result)
894{
895 struct ly_set *modules = NULL;
896 uint32_t u, v, submodules_count, feature_count;
897 struct lyd_node *iter, *child, *oper_data = NULL;
898 struct lys_module *mod;
899 int ret = 0;
900
901 /* get yang-library operational data */
902 if (xpath_sup) {
903 if (get_oper_data(session, get_data_sup, "/ietf-yang-library:*", &oper_data)) {
904 goto cleanup;
905 }
906 } else {
907 if (get_oper_data(session, get_data_sup,
908 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", &oper_data)) {
909 goto cleanup;
910 }
911 }
912 if (!oper_data) {
913 goto cleanup;
914 }
915
916 if (lyd_find_xpath(oper_data, "/ietf-yang-library:modules-state/module", &modules)) {
917 WRN(NULL, "No yang-library module information found.");
Radek Krejci2d088832018-08-21 13:40:14 +0200918 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200919 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200920
Michal Vasko77367452021-02-16 16:32:18 +0100921 (*result) = calloc(modules->count + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200922 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200923 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200924 ret = -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200925 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200926 }
927
Michal Vasko77367452021-02-16 16:32:18 +0100928 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200929 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100930 feature_count = 0;
931 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
932 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Michal Vasko5d9d5972021-06-09 14:17:01 +0200933 if (!iter->schema || (iter->schema->module != mod)) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200934 /* ignore node from other schemas (augments) */
935 continue;
936 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200937 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200938 /* ignore empty nodes */
939 continue;
940 }
941 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200942 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200943 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200944 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200945 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200946 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200947 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100948 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
949 if (!(*result)[u].features) {
950 ERRMEM;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200951 free_module_info(*result);
Michal Vasko77367452021-02-16 16:32:18 +0100952 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200953 ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +0100954 goto cleanup;
955 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200956 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100957 (*result)[u].features[feature_count + 1] = NULL;
958 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200959 } else if (!strcmp(iter->schema->name, "submodule")) {
960 submodules_count++;
961 }
962 }
963
964 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200965 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
966 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200967 ERRMEM;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200968 free_module_info(*result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200969 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200970 ret = -1;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200971 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200972 } else {
973 v = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100974 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
975 mod = modules->dnodes[u]->schema->module;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200976 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
Michal Vasko77367452021-02-16 16:32:18 +0100977 LY_LIST_FOR(lyd_child(iter), child) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200978 if (mod != child->schema->module) {
979 continue;
980 } else if (!strcmp(child->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200981 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200982 } else if (!strcmp(child->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200983 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200984 }
985 }
986 }
987 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200988 }
989 }
990 }
991
Radek Krejcifd5b6682017-06-13 15:52:53 +0200992cleanup:
Michal Vasko78939072022-12-12 07:43:18 +0100993 lyd_free_siblings(oper_data);
Michal Vasko77367452021-02-16 16:32:18 +0100994 ly_set_free(modules, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200995 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200996}
997
Michal Vaskoc088f982021-10-05 12:23:07 +0200998/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200999 * @brief Build server module info from received capabilities.
Michal Vaskoc088f982021-10-05 12:23:07 +02001000 *
1001 * @param[in] cpblts Server capabilities.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001002 * @param[out] result Server modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001003 * @return 0 on success.
1004 * @return -1 on error.
1005 */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001006static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001007build_module_info_cpblts(char **cpblts, struct module_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001008{
Michal Vasko77367452021-02-16 16:32:18 +01001009 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001010 char *module_cpblt, *ptr, *ptr2;
1011
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001012 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +02001013 (*result) = calloc(u + 1, sizeof **result);
1014 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +02001015 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +02001016 return -1;
Radek Krejcic9390502018-08-17 10:23:13 +02001017 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001018
1019 for (u = v = 0; cpblts[u]; ++u) {
1020 module_cpblt = strstr(cpblts[u], "module=");
1021 /* this capability requires a module */
1022 if (!module_cpblt) {
1023 continue;
1024 }
1025
1026 /* get module's name */
1027 ptr = (char *)module_cpblt + 7;
1028 ptr2 = strchr(ptr, '&');
1029 if (!ptr2) {
1030 ptr2 = ptr + strlen(ptr);
1031 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001032 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001033
1034 /* get module's revision */
1035 ptr = strstr(module_cpblt, "revision=");
1036 if (ptr) {
1037 ptr += 9;
1038 ptr2 = strchr(ptr, '&');
1039 if (!ptr2) {
1040 ptr2 = ptr + strlen(ptr);
1041 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001042 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001043 }
1044
1045 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001046 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001047
1048 /* get module's features */
1049 ptr = strstr(module_cpblt, "features=");
1050 if (ptr) {
1051 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +01001052 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001053 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
1054 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +01001055 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1056 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1057 (*result)[v].features[feature_count + 1] = NULL;
1058 ++feature_count;
1059
Radek Krejci65ef6d52018-08-16 16:35:02 +02001060 ptr2 = ptr + 1;
1061 }
1062 }
1063 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +01001064 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1065 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1066 (*result)[v].features[feature_count + 1] = NULL;
1067 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001068 }
1069 ++v;
1070 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001071
Michal Vasko303263d2021-10-05 12:18:21 +02001072 return 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001073}
1074
Michal Vaskoc088f982021-10-05 12:23:07 +02001075/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001076 * @brief Fill client context based on server modules info.
Michal Vaskoc088f982021-10-05 12:23:07 +02001077 *
1078 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001079 * @param[in] modules Server modules info.
1080 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001081 * @param[in] user_data User data for @p user_clb.
1082 * @param[in] has_get_schema Whether server supports get-schema RPC.
1083 * @return 0 on success.
1084 * @return -1 on error.
1085 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001086static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001087nc_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 +01001088 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001089{
Michal Vasko303263d2021-10-05 12:18:21 +02001090 int ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +01001091 struct lys_module *mod;
1092 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001093
1094 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +02001095 /* skip import-only modules */
1096 if (!modules[u].implemented) {
1097 continue;
1098 }
1099
Radek Krejci65ef6d52018-08-16 16:35:02 +02001100 /* we can continue even if it fails */
Michal Vasko5e32c402022-01-12 14:05:09 +01001101 nc_ctx_load_module(session, modules[u].name, modules[u].revision, (const char **)modules[u].features, modules,
1102 user_clb, user_data, has_get_schema, &mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001103
1104 if (!mod) {
1105 if (session->status != NC_STATUS_RUNNING) {
1106 /* something bad heppened, discard the session */
Michal Vasko05532772021-06-03 12:12:38 +02001107 ERR(session, "Invalid session, discarding.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001108 goto cleanup;
1109 }
1110
Michal Vasko5ca5d972022-09-14 13:51:31 +02001111 /* all loading ways failed, the module will be ignored in the received data */
1112 WRN(session, "Failed to load module \"%s@%s\".", modules[u].name, modules[u].revision ?
Michal Vasko05532772021-06-03 12:12:38 +02001113 modules[u].revision : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001114 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001115 }
Michal Vasko60f66602017-10-17 13:52:18 +02001116 }
1117
Michal Vasko77367452021-02-16 16:32:18 +01001118 /* success */
Michal Vasko303263d2021-10-05 12:18:21 +02001119 ret = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001120
1121cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +02001122 return ret;
1123}
1124
Michal Vaskoc088f982021-10-05 12:23:07 +02001125/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001126 * @brief Fill client context with ietf-netconf module.
Michal Vaskoc088f982021-10-05 12:23:07 +02001127 *
1128 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001129 * @param[in] modules Server module info.
1130 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001131 * @param[in] user_data User data for @p user_clb.
1132 * @param[in] has_get_schema Whether server supports get-schema RPC.
1133 * @return 0 on success.
1134 * @return -1 on error.
1135 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001136static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001137nc_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 +01001138 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001139{
Michal Vasko77367452021-02-16 16:32:18 +01001140 uint32_t u;
Michal Vasko5e32c402022-01-12 14:05:09 +01001141 const char **features = NULL;
1142 struct ly_in *in;
Michal Vasko77367452021-02-16 16:32:18 +01001143 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001144
Michal Vasko5e32c402022-01-12 14:05:09 +01001145 /* find supported features (capabilities) in ietf-netconf */
1146 for (u = 0; modules[u].name; ++u) {
1147 if (!strcmp(modules[u].name, "ietf-netconf")) {
1148 assert(modules[u].implemented);
1149 features = (const char **)modules[u].features;
1150 break;
1151 }
1152 }
1153 if (!modules[u].name) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001154 ERR(session, "Base NETCONF module not supported by the server.");
Michal Vasko5e32c402022-01-12 14:05:09 +01001155 return -1;
1156 }
1157
Michal Vasko77367452021-02-16 16:32:18 +01001158 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vasko5e32c402022-01-12 14:05:09 +01001159 if (ietfnc) {
1160 /* make sure to enable all the features if already loaded */
1161 lys_set_implemented(ietfnc, features);
1162 } else {
1163 /* load the module */
1164 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 +02001165 if (!ietfnc) {
Michal Vasko5e32c402022-01-12 14:05:09 +01001166 ly_in_new_memory(ietf_netconf_2013_09_29_yang, &in);
1167 lys_parse(session->ctx, in, LYS_IN_YANG, features, &ietfnc);
1168 ly_in_free(in, 0);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001169 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001170 }
1171 if (!ietfnc) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001172 ERR(session, "Loading base NETCONF module failed.");
Michal Vasko303263d2021-10-05 12:18:21 +02001173 return -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001174 }
1175
Radek Krejci65ef6d52018-08-16 16:35:02 +02001176 return 0;
1177}
1178
Michal Vasko78939072022-12-12 07:43:18 +01001179/**
1180 * @brief Set client session context to support schema-mount if possible.
1181 *
1182 * @param[in] session NC session with the context to modify.
1183 * @param[in] get_data_sup Whether get-data RPC is available or only get.
1184 * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
1185 * @return 0 on success.
1186 * @return -1 on error.
1187 */
1188static int
1189nc_ctx_schema_mount(struct nc_session *session, int get_data_sup, int xpath_sup)
1190{
1191 int rc = 0;
1192 struct lyd_node *oper_data = NULL;
1193
1194 if (session->flags & NC_SESSION_SHAREDCTX) {
1195 /* context is already fully set up */
1196 goto cleanup;
1197 }
1198
1199 /* get yang-library and schema-mounts operational data */
1200 if (xpath_sup) {
1201 if ((rc = get_oper_data(session, get_data_sup, "/ietf-yang-library:* | /ietf-yang-schema-mount:*", &oper_data))) {
1202 goto cleanup;
1203 }
1204 } else {
1205 if ((rc = get_oper_data(session, get_data_sup,
1206 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>"
1207 "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"/>", &oper_data))) {
1208 goto cleanup;
1209 }
1210 }
1211
1212 if (!oper_data || lyd_find_path(oper_data, "/ietf-yang-schema-mount:schema-mounts", 0, NULL)) {
1213 /* no schema-mounts operational data */
1214 goto cleanup;
1215 }
1216
1217 /* validate the data for the parent reference prefixes to be resolved */
1218 if (lyd_validate_all(&oper_data, NULL, LYD_VALIDATE_PRESENT, NULL)) {
1219 ERR(session, "Invalid operational data received from the server (%s).", ly_errmsg(LYD_CTX(oper_data)));
1220 rc = -1;
1221 goto cleanup;
1222 }
1223
1224 /* store the data in the session */
1225 session->opts.client.ext_data = oper_data;
1226 oper_data = NULL;
1227
1228cleanup:
1229 lyd_free_siblings(oper_data);
1230 return rc;
1231}
1232
Michal Vasko086311b2016-01-08 09:53:11 +01001233int
1234nc_ctx_check_and_fill(struct nc_session *session)
1235{
Michal Vaskod6c6f852022-12-14 15:37:21 +01001236 int i, get_schema_support = 0, yanglib_support = 0, xpath_support = 0, nmda_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +01001237 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001238 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001239 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +02001240 char *revision;
Michal Vasko5ca5d972022-09-14 13:51:31 +02001241 struct module_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001242
Michal Vasko2e6defd2016-10-07 15:48:15 +02001243 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +01001244
Michal Vaskoa272e092023-07-10 14:34:03 +02001245 if (client_opts.auto_context_fill_disabled) {
1246 VRB(session, "Context of the new session is left only with the default YANG modules.");
1247 return 0;
1248 }
1249
Radek Krejci65ef6d52018-08-16 16:35:02 +02001250 /* 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 +02001251 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001252
Radek Krejci65ef6d52018-08-16 16:35:02 +02001253 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
Michal Vasko5ca5d972022-09-14 13:51:31 +02001254 * modules only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +01001255 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001256
1257 /* our callback is set later with appropriate data */
1258 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
1259
1260 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001261 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001262 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001263 get_schema_support = 1 + i;
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001264 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001265 yanglib_support = 1 + i;
Michal Vasko78939072022-12-12 07:43:18 +01001266 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:xpath:1.0", 44)) {
1267 xpath_support = 1 + i;
Michal Vaskod6c6f852022-12-14 15:37:21 +01001268 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-nmda", 45)) {
1269 nmda_support = 1 + i;
Michal Vasko086311b2016-01-08 09:53:11 +01001270 }
1271 }
Michal Vaskod6c6f852022-12-14 15:37:21 +01001272 VRB(session, "Capability for <get-schema> support%s found.", get_schema_support ? "" : " not");
1273 VRB(session, "Capability for yang-library support%s found.", yanglib_support ? "" : " not");
1274 VRB(session, "Capability for XPath filter support%s found.", xpath_support ? "" : " not");
1275 VRB(session, "Capability for NMDA RPCs support%s found.", nmda_support ? "" : " not");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001276
Michal Vasko5ca5d972022-09-14 13:51:31 +02001277 /* get information about server's modules from capabilities list until we will have yang-library */
1278 if (build_module_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
1279 ERR(session, "Unable to get server module information from the <hello>'s capabilities.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001280 goto cleanup;
1281 }
1282
Michal Vasko086311b2016-01-08 09:53:11 +01001283 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001284 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 +02001285 WRN(session, "Loading NETCONF monitoring module failed, cannot use <get-schema>.");
Michal Vasko8a4e1462020-05-07 11:32:31 +02001286 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001287 }
1288
1289 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001290 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001291 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001292 }
1293
Radek Krejci65ef6d52018-08-16 16:35:02 +02001294 /* get correct version of ietf-yang-library into context */
1295 if (yanglib_support) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001296 /* use get-schema to get server's ietf-yang-library */
Radek Krejcib1d250e2018-04-12 13:54:18 +02001297 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1298 if (!revision) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001299 WRN(session, "Loading NETCONF ietf-yang-library module failed, missing revision in NETCONF <hello> message.");
Michal Vasko05532772021-06-03 12:12:38 +02001300 WRN(session, "Unable to automatically use <get-schema>.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001301 yanglib_support = 0;
1302 } else {
1303 revision = strndup(&revision[9], 10);
Michal Vasko5e32c402022-01-12 14:05:09 +01001304 if (nc_ctx_load_module(session, "ietf-yang-library", revision, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001305 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001306 WRN(session, "Loading NETCONF ietf-yang-library module failed, unable to use it to learn all "
Michal Vasko05532772021-06-03 12:12:38 +02001307 "the supported modules.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001308 yanglib_support = 0;
1309 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001310 if (strcmp(revision, "2019-01-04") >= 0) {
1311 /* we also need ietf-datastores to be implemented */
Michal Vasko5e32c402022-01-12 14:05:09 +01001312 if (nc_ctx_load_module(session, "ietf-datastores", NULL, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001313 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001314 WRN(session, "Loading NETCONF ietf-datastores module failed, unable to use yang-library "
Michal Vasko05532772021-06-03 12:12:38 +02001315 "to learn all the supported modules.");
Michal Vasko0d877f92020-04-02 13:52:43 +02001316 yanglib_support = 0;
1317 }
1318 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001319 free(revision);
1320 }
1321 }
1322
Michal Vaskod6c6f852022-12-14 15:37:21 +01001323 /* ietf-netconf-nmda is needed to issue get-data */
1324 if (nmda_support && nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, NULL, server_modules, old_clb, old_data,
1325 get_schema_support, &mod)) {
1326 WRN(session, "Loading NMDA module failed, unable to use <get-data>.");
1327 nmda_support = 0;
1328 }
1329
Michal Vasko5ca5d972022-09-14 13:51:31 +02001330 /* prepare structured information about server's modules */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001331 if (yanglib_support) {
Michal Vaskod6c6f852022-12-14 15:37:21 +01001332 if (build_module_info_yl(session, nmda_support, xpath_support, &sm)) {
Radek Krejcif0633792018-08-20 15:12:09 +02001333 goto cleanup;
1334 } else if (!sm) {
Michal Vasko05532772021-06-03 12:12:38 +02001335 VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
Radek Krejcif0633792018-08-20 15:12:09 +02001336 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001337 /* prefer yang-library information, currently we have it from capabilities used for getting correct
Michal Vasko5ca5d972022-09-14 13:51:31 +02001338 * yang-library module */
1339 free_module_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001340 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001341 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001342 }
Michal Vaskoef578332016-01-25 13:20:09 +01001343
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001344 /* compile all modules at once to avoid invalid errors or warnings */
1345 ly_ctx_set_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
1346
1347 /* fill the context */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001348 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1349 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001350 }
1351
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001352 /* compile it */
1353 if (ly_ctx_compile(session->ctx)) {
1354 goto cleanup;
1355 }
1356
Michal Vasko78939072022-12-12 07:43:18 +01001357 /* set support for schema-mount, if possible */
Michal Vaskod6c6f852022-12-14 15:37:21 +01001358 if (nc_ctx_schema_mount(session, nmda_support, xpath_support)) {
Michal Vasko78939072022-12-12 07:43:18 +01001359 goto cleanup;
1360 }
1361
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001362 /* success */
Radek Krejcifd5b6682017-06-13 15:52:53 +02001363 ret = 0;
1364
Michal Vaskoeee99412016-11-21 10:19:43 +01001365 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001366 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 +02001367 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001368 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001369
1370cleanup:
Michal Vasko5ca5d972022-09-14 13:51:31 +02001371 free_module_info(server_modules);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001372
Radek Krejcifd5b6682017-06-13 15:52:53 +02001373 /* set user callback back */
1374 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001375 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001376 ly_ctx_unset_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001377
Michal Vaskoef578332016-01-25 13:20:09 +01001378 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001379}
1380
1381API struct nc_session *
1382nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1383{
Michal Vaskod083db62016-01-19 10:31:29 +01001384 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001385
Michal Vasko45e53ae2016-04-07 11:46:03 +02001386 if (fdin < 0) {
1387 ERRARG("fdin");
1388 return NULL;
1389 } else if (fdout < 0) {
1390 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001391 return NULL;
1392 }
1393
1394 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001395 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001396 if (!session) {
1397 ERRMEM;
1398 return NULL;
1399 }
1400 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001401
1402 /* transport specific data */
1403 session->ti_type = NC_TI_FD;
1404 session->ti.fd.in = fdin;
1405 session->ti.fd.out = fdout;
1406
Michal Vasko78939072022-12-12 07:43:18 +01001407 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001408 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001409 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001410 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001411
1412 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001413 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001414 goto fail;
1415 }
1416 session->status = NC_STATUS_RUNNING;
1417
Michal Vaskoef578332016-01-25 13:20:09 +01001418 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001419 goto fail;
1420 }
1421
1422 return session;
1423
1424fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001425 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001426 return NULL;
1427}
1428
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001429API struct nc_session *
1430nc_connect_unix(const char *address, struct ly_ctx *ctx)
1431{
1432 struct nc_session *session = NULL;
1433 struct sockaddr_un sun;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001434 struct passwd *pw, pw_buf;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001435 char *username;
1436 int sock = -1;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001437 char *buf = NULL;
1438 size_t buf_size = 0;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001439
1440 if (address == NULL) {
1441 ERRARG("address");
1442 return NULL;
1443 }
1444
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001445 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1446 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001447 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001448 goto fail;
1449 }
1450
1451 memset(&sun, 0, sizeof(sun));
1452 sun.sun_family = AF_UNIX;
1453 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1454
1455 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001456 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001457 goto fail;
1458 }
1459
1460 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001461 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001462 goto fail;
1463 }
1464
1465 /* prepare session structure */
1466 session = nc_new_session(NC_CLIENT, 0);
1467 if (!session) {
1468 ERRMEM;
1469 goto fail;
1470 }
1471 session->status = NC_STATUS_STARTING;
1472
1473 /* transport specific data */
1474 session->ti_type = NC_TI_UNIX;
1475 session->ti.unixsock.sock = sock;
1476 sock = -1; /* do not close sock in fail label anymore */
1477
Michal Vasko78939072022-12-12 07:43:18 +01001478 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001479 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001480 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001481 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001482
Michal Vasko93224072021-11-09 12:14:28 +01001483 session->path = strdup(address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001484
romanf6e32012023-04-24 15:51:26 +02001485 pw = nc_getpw(geteuid(), NULL, &pw_buf, &buf, &buf_size);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001486 if (!pw) {
1487 ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid());
1488 goto fail;
1489 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001490 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001491 free(buf);
1492 if (!username) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001493 ERRMEM;
1494 goto fail;
1495 }
Michal Vasko93224072021-11-09 12:14:28 +01001496 session->username = username;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001497
1498 /* NETCONF handshake */
1499 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1500 goto fail;
1501 }
1502 session->status = NC_STATUS_RUNNING;
1503
1504 if (nc_ctx_check_and_fill(session) == -1) {
1505 goto fail;
1506 }
1507
1508 return session;
1509
1510fail:
1511 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001512 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001513 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001514 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001515 return NULL;
1516}
1517
Michal Vasko056f53c2022-10-21 13:38:15 +02001518/**
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001519 * @brief Convert socket IP address to string.
1520 *
1521 * @param[in] saddr Sockaddr to convert.
1522 * @param[out] str_ip String IP address.
Michal Vaskoc07d9762023-03-27 10:32:18 +02001523 * @param[out] port Optional port.
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001524 * @return 0 on success.
1525 * @return -1 on error.
1526 */
1527static int
Michal Vaskoc07d9762023-03-27 10:32:18 +02001528nc_saddr2str(const struct sockaddr *saddr, char **str_ip, uint16_t *port)
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001529{
1530 void *addr;
1531 socklen_t str_len;
1532
1533 assert((saddr->sa_family == AF_INET) || (saddr->sa_family == AF_INET6));
1534
1535 str_len = (saddr->sa_family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN;
1536 *str_ip = malloc(str_len);
1537 if (!*str_ip) {
1538 ERRMEM;
1539 return -1;
1540 }
1541
1542 if (saddr->sa_family == AF_INET) {
Michal Vaskoc0f85ca2023-03-27 10:19:00 +02001543 addr = &((struct sockaddr_in *)saddr)->sin_addr;
Michal Vaskoc07d9762023-03-27 10:32:18 +02001544 if (port) {
1545 *port = ntohs(((struct sockaddr_in *)saddr)->sin_port);
1546 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001547 } else {
Michal Vaskoc0f85ca2023-03-27 10:19:00 +02001548 addr = &((struct sockaddr_in6 *)saddr)->sin6_addr;
Michal Vaskoc07d9762023-03-27 10:32:18 +02001549 if (port) {
1550 *port = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
1551 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001552 }
1553 if (!inet_ntop(saddr->sa_family, addr, *str_ip, str_len)) {
1554 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
1555 free(*str_ip);
1556 return -1;
1557 }
1558
1559 return 0;
1560}
1561
1562/**
Michal Vasko056f53c2022-10-21 13:38:15 +02001563 * @brief Try to connect a socket, optionally a pending one from a previous attempt.
1564 *
1565 * @param[in] timeout_ms Timeout in ms to wait for the connection to be fully established, -1 to block.
1566 * @param[in,out] sock_pending Optional previously created socked that was not fully connected yet. If provided and
1567 * connected, is set to -1.
1568 * @param[in] res Addrinfo resource to use when creating a new socket.
1569 * @param[in] ka Keepalives to set.
1570 * @return Connected socket or -1 on error.
Frank Rimpler9f838b02018-07-25 06:44:03 +00001571 */
1572static int
Michal Vasko056f53c2022-10-21 13:38:15 +02001573sock_connect(int timeout_ms, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001574{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001575 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001576 int sock = -1;
Michal Vaskob9336672023-10-11 09:27:30 +02001577 struct pollfd fds = {0};
Frank Rimpler9f838b02018-07-25 06:44:03 +00001578 socklen_t len = sizeof(int);
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001579 uint16_t port;
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001580 char *str;
Michal Vasko086311b2016-01-08 09:53:11 +01001581
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001582 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001583 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001584 sock = *sock_pending;
1585 } else {
1586 assert(res);
Michal Vaskoc07d9762023-03-27 10:32:18 +02001587 if (nc_saddr2str(res->ai_addr, &str, &port)) {
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001588 return -1;
1589 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001590 VRB(NULL, "Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port);
1591 free(str);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001592
1593 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001594 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1595 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001596 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001597 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001598 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001599 /* make the socket non-blocking */
1600 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001601 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001602 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001603 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001604 /* non-blocking connect! */
1605 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1606 if (errno != EINPROGRESS) {
1607 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001608 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001609 goto cleanup;
1610 }
1611 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001612 }
Michal Vasko7d965dc2018-03-12 14:39:23 +01001613
Michal Vaskob9336672023-10-11 09:27:30 +02001614 fds.fd = sock;
1615 fds.events = POLLOUT;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001616
Michal Vaskob9336672023-10-11 09:27:30 +02001617 /* wait until we can write data to the socket */
1618 ret = poll(&fds, 1, timeout_ms);
1619 if (ret == -1) {
1620 /* error */
1621 ERR(NULL, "poll() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001622 goto cleanup;
Michal Vaskob9336672023-10-11 09:27:30 +02001623 } else if (ret == 0) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001624 /* there was a timeout */
Michal Vasko056f53c2022-10-21 13:38:15 +02001625 VRB(NULL, "Timed out after %d ms (%s).", timeout_ms, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001626 if (sock_pending) {
1627 /* no sock-close, we'll try it again */
1628 *sock_pending = sock;
1629 } else {
1630 close(sock);
1631 }
1632 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001633 }
Radek Krejci782041a2018-08-20 10:09:45 +02001634
1635 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001636 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001637 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001638 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001639 goto cleanup;
1640 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001641 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001642 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001643 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001644 errno = error;
1645 goto cleanup;
1646 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001647
1648 /* enable keep-alive */
romanc1d2b092023-02-02 08:58:27 +01001649 if (nc_sock_configure_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001650 goto cleanup;
1651 }
1652
Michal Vasko056f53c2022-10-21 13:38:15 +02001653 /* connected */
1654 if (sock_pending) {
1655 *sock_pending = -1;
1656 }
Michal Vasko086311b2016-01-08 09:53:11 +01001657 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001658
Frank Rimpler9f838b02018-07-25 06:44:03 +00001659cleanup:
1660 if (sock_pending) {
1661 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001662 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001663 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001664 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001665}
1666
Frank Rimpler9f838b02018-07-25 06:44:03 +00001667int
Michal Vasko056f53c2022-10-21 13:38:15 +02001668nc_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 +00001669{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001670 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001671 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001672 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001673 char port_s[6]; /* length of string representation of short int */
1674 struct sockaddr_storage saddr;
1675 socklen_t addr_len = sizeof saddr;
1676
1677 *ip_host = NULL;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001678
Michal Vasko056f53c2022-10-21 13:38:15 +02001679 DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout_ms, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001680
1681 /* no pending socket */
1682 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001683 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001684 snprintf(port_s, 6, "%u", port);
1685 memset(&hints, 0, sizeof hints);
1686 hints.ai_family = AF_UNSPEC;
1687 hints.ai_socktype = SOCK_STREAM;
1688 hints.ai_protocol = IPPROTO_TCP;
1689 i = getaddrinfo(host, port_s, &hints, &res_list);
1690 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001691 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001692 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001693 }
1694
1695 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vasko056f53c2022-10-21 13:38:15 +02001696 sock = sock_connect(timeout_ms, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001697 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001698 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001699 /* try the next resource */
1700 continue;
1701 } else {
1702 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001703 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001704 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001705 }
Michal Vasko05532772021-06-03 12:12:38 +02001706 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 +01001707
1708 opt = 1;
1709 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001710 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001711 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001712 }
1713
Michal Vaskoc07d9762023-03-27 10:32:18 +02001714 if (nc_saddr2str(res->ai_addr, ip_host, NULL)) {
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001715 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001716 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001717 break;
1718 }
1719 freeaddrinfo(res_list);
1720
1721 } else {
1722 /* try to get a connection with the pending socket */
1723 assert(sock_pending);
Michal Vasko056f53c2022-10-21 13:38:15 +02001724 sock = sock_connect(timeout_ms, sock_pending, NULL, ka);
Tie.Liao9bca73d2023-03-23 17:25:00 +01001725
1726 if (sock > 0) {
Barbaros Tokaoglu96da5912023-06-19 18:57:05 +03001727 if (getpeername(sock, (struct sockaddr *)&saddr, &addr_len)) {
1728 ERR(NULL, "getpeername failed (%s).", strerror(errno));
Tie.Liao9bca73d2023-03-23 17:25:00 +01001729 goto error;
1730 }
1731
Michal Vaskoc07d9762023-03-27 10:32:18 +02001732 if (nc_saddr2str((struct sockaddr *)&saddr, ip_host, NULL)) {
Tie.Liao9bca73d2023-03-23 17:25:00 +01001733 goto error;
1734 }
Tie.Liao9bca73d2023-03-23 17:25:00 +01001735 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001736 }
1737
1738 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001739
1740error:
Michal Vasko0be85692021-03-02 08:04:57 +01001741 if (res_list) {
1742 freeaddrinfo(res_list);
1743 }
Michal Vaskocb846632019-12-13 15:12:45 +01001744 if (sock != -1) {
1745 close(sock);
1746 }
1747 if (sock_pending) {
1748 *sock_pending = -1;
1749 }
1750 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001751}
1752
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001753#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001754
Michal Vasko3031aae2016-01-27 16:07:18 +01001755int
Michal Vasko9d4cca52022-09-07 11:19:57 +02001756nc_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 +01001757{
1758 int sock;
1759
Michal Vasko45e53ae2016-04-07 11:46:03 +02001760 if (!address) {
1761 ERRARG("address");
1762 return -1;
1763 } else if (!port) {
1764 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001765 return -1;
1766 }
1767
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001768 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001769 if (sock == -1) {
1770 return -1;
1771 }
1772
1773 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001774 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1775 if (!client_opts.ch_binds) {
1776 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001777 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001778 return -1;
1779 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001780
Michal Vasko9d4cca52022-09-07 11:19:57 +02001781 client_opts.ch_binds_aux = nc_realloc(client_opts.ch_binds_aux, client_opts.ch_bind_count * sizeof *client_opts.ch_binds_aux);
1782 if (!client_opts.ch_binds_aux) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001783 ERRMEM;
1784 close(sock);
1785 return -1;
1786 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001787 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].ti = ti;
1788 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].hostname = hostname ? strdup(hostname) : NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001789
Michal Vasko3031aae2016-01-27 16:07:18 +01001790 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
1791 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1792 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001793 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001794
1795 return 0;
1796}
1797
1798int
1799nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1800{
1801 uint32_t i;
1802 int ret = -1;
1803
1804 if (!address && !port && !ti) {
1805 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1806 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001807 free(client_opts.ch_binds[i].address);
1808
1809 free(client_opts.ch_binds_aux[i].hostname);
Michal Vasko3031aae2016-01-27 16:07:18 +01001810
1811 ret = 0;
1812 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001813 client_opts.ch_bind_count = 0;
1814
Michal Vasko3031aae2016-01-27 16:07:18 +01001815 free(client_opts.ch_binds);
1816 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001817
1818 free(client_opts.ch_binds_aux);
1819 client_opts.ch_binds_aux = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001820 } else {
1821 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001822 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1823 (!port || (client_opts.ch_binds[i].port == port)) &&
Michal Vasko9d4cca52022-09-07 11:19:57 +02001824 (!ti || (client_opts.ch_binds_aux[i].ti == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001825 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001826 free(client_opts.ch_binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +01001827
1828 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001829 if (!client_opts.ch_bind_count) {
1830 free(client_opts.ch_binds);
1831 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001832
1833 free(client_opts.ch_binds_aux);
1834 client_opts.ch_binds_aux = NULL;
Michal Vasko66c762a2016-10-13 10:34:14 +02001835 } else if (i < client_opts.ch_bind_count) {
Michal Vasko9d4cca52022-09-07 11:19:57 +02001836 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count],
1837 sizeof *client_opts.ch_binds);
1838
1839 memcpy(&client_opts.ch_binds_aux[i], &client_opts.ch_binds_aux[client_opts.ch_bind_count],
1840 sizeof *client_opts.ch_binds_aux);
Michal Vasko66c762a2016-10-13 10:34:14 +02001841 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001842
1843 ret = 0;
1844 }
1845 }
1846 }
1847
1848 return ret;
1849}
1850
1851API int
1852nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1853{
1854 int sock;
1855 char *host = NULL;
1856 uint16_t port, idx;
1857
Michal Vasko45e53ae2016-04-07 11:46:03 +02001858 if (!client_opts.ch_binds) {
1859 ERRINIT;
1860 return -1;
1861 } else if (!session) {
1862 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001863 return -1;
1864 }
1865
1866 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 +01001867 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001868 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001869 return sock;
1870 }
1871
Radek Krejci53691be2016-02-22 13:58:37 +01001872#ifdef NC_ENABLED_SSH
Michal Vasko9d4cca52022-09-07 11:19:57 +02001873 if (client_opts.ch_binds_aux[idx].ti == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001874 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001875 } else
1876#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001877#ifdef NC_ENABLED_TLS
Michal Vasko9d4cca52022-09-07 11:19:57 +02001878 if (client_opts.ch_binds_aux[idx].ti == NC_TI_OPENSSL) {
1879 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT,
1880 client_opts.ch_binds_aux[idx].hostname);
Michal Vasko3d865d22016-01-28 16:00:53 +01001881 } else
1882#endif
1883 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001884 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001885 *session = NULL;
1886 }
1887
1888 free(host);
1889
1890 if (!(*session)) {
1891 return -1;
1892 }
1893
1894 return 1;
1895}
1896
Radek Krejci53691be2016-02-22 13:58:37 +01001897#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001898
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001899API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001900nc_session_get_cpblts(const struct nc_session *session)
1901{
1902 if (!session) {
1903 ERRARG("session");
1904 return NULL;
1905 }
1906
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001907 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001908}
1909
1910API const char *
1911nc_session_cpblt(const struct nc_session *session, const char *capab)
1912{
1913 int i, len;
1914
1915 if (!session) {
1916 ERRARG("session");
1917 return NULL;
1918 } else if (!capab) {
1919 ERRARG("capab");
1920 return NULL;
1921 }
1922
1923 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001924 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1925 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1926 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001927 }
1928 }
1929
1930 return NULL;
1931}
1932
Michal Vasko9cd26a82016-05-31 08:58:48 +02001933API int
1934nc_session_ntf_thread_running(const struct nc_session *session)
1935{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001936 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001937 ERRARG("session");
1938 return 0;
1939 }
1940
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02001941 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running);
Michal Vasko9cd26a82016-05-31 08:58:48 +02001942}
1943
Michal Vaskob7558c52016-02-26 15:04:19 +01001944API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001945nc_client_init(void)
1946{
1947 nc_init();
1948}
1949
1950API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001951nc_client_destroy(void)
1952{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001953 nc_client_set_schema_searchpath(NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001954#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001955 nc_client_ch_del_bind(NULL, 0, 0);
1956#endif
1957#ifdef NC_ENABLED_SSH
1958 nc_client_ssh_destroy_opts();
1959#endif
1960#ifdef NC_ENABLED_TLS
1961 nc_client_tls_destroy_opts();
1962#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001963 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001964}
1965
Michal Vasko77367452021-02-16 16:32:18 +01001966static NC_MSG_TYPE
1967recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001968{
Michal Vasko77367452021-02-16 16:32:18 +01001969 char *ptr;
1970 struct lyd_attr *attr;
1971 uint64_t cur_msgid;
1972
1973 assert(envp && !envp->schema);
1974
1975 /* find the message-id attribute */
1976 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1977 if (!strcmp(attr->name.name, "message-id")) {
1978 break;
1979 }
1980 }
1981
1982 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02001983 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01001984 return NC_MSG_REPLY_ERR_MSGID;
1985 }
1986
1987 cur_msgid = strtoul(attr->value, &ptr, 10);
1988 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02001989 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1990 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01001991 return NC_MSG_REPLY_ERR_MSGID;
1992 }
1993
1994 return NC_MSG_REPLY;
1995}
1996
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001997/**
1998 * @brief Used to roughly estimate the type of the message, does not actually parse or verify it.
1999 *
2000 * @param[in] session NETCONF session used to send error messages.
2001 * @param[in] msg Message to check for type.
2002 * @return NC_MSG_REPLY If format roughly matches a rpc-reply;
2003 * @return NC_MSG_NOTIF If format roughly matches a notification;
2004 * @return NC_MSG_ERROR If format is malformed or unrecognized.
2005 */
2006static NC_MSG_TYPE
2007get_msg_type(struct nc_session *session, struct ly_in *msg)
2008{
2009 const char *str, *end;
2010
2011 str = ly_in_memory(msg, NULL);
2012
2013 while (*str) {
2014 /* Skip whitespaces */
2015 while (isspace(*str)) {
2016 str++;
2017 }
2018
2019 if (*str == '<') {
2020 str++;
2021 if (!strncmp(str, "!--", 3)) {
2022 /* Skip comments */
2023 end = "-->";
2024 str = strstr(str, end);
2025 } else if (!strncmp(str, "?xml", 4)) {
2026 /* Skip xml declaration */
2027 end = "?>";
2028 str = strstr(str, end);
2029 } else if (!strncmp(str, "rpc-reply", 9)) {
2030 return NC_MSG_REPLY;
2031 } else if (!strncmp(str, "notification", 12)) {
2032 return NC_MSG_NOTIF;
2033 } else {
2034 ERR(session, "Unknown xml element '%.10s'.", str);
2035 return NC_MSG_ERROR;
2036 }
2037 if (!str) {
2038 /* No matching ending tag found */
2039 ERR(session, "No matching ending tag '%s' found in xml message.", end);
2040 return NC_MSG_ERROR;
2041 }
2042 str += strlen(end);
2043 } else {
2044 /* Not a valid xml */
2045 ERR(session, "Unexpected character '%c' in xml message.", *str);
2046 return NC_MSG_ERROR;
2047 }
2048 }
2049
2050 /* Unexpected end of message */
2051 ERR(session, "Unexpected end of xml message.");
2052 return NC_MSG_ERROR;
2053}
2054
2055/**
2056 * @brief Function to receive either replies or notifications.
2057 *
2058 * @param[in] session NETCONF session from which this function receives messages.
2059 * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite.
2060 * @param[in] expected Type of the message the caller desired.
2061 * @param[out] message If receiving a message succeeded this is the message, NULL otherwise.
2062 * @return NC_MSG_REPLY If a rpc-reply was received;
2063 * @return NC_MSG_NOTIF If a notification was received;
Michal Vaskofdba4a32022-01-05 12:13:53 +01002064 * @return NC_MSG_ERROR If any error occurred;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002065 * @return NC_MSG_WOULDBLOCK If the timeout was reached.
2066 */
2067static NC_MSG_TYPE
2068recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct ly_in **message)
2069{
2070 struct nc_msg_cont **cont_ptr;
2071 struct ly_in *msg = NULL;
2072 struct nc_msg_cont *cont, *prev;
2073 NC_MSG_TYPE ret = NC_MSG_ERROR;
2074 int r;
2075
2076 *message = NULL;
2077
2078 /* MSGS LOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002079 r = nc_session_client_msgs_lock(session, &timeout, __func__);
2080 if (!r) {
2081 ret = NC_MSG_WOULDBLOCK;
2082 goto cleanup;
2083 } else if (r == -1) {
2084 ret = NC_MSG_ERROR;
2085 goto cleanup;
2086 }
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002087
2088 /* Find the expected message in the buffer */
2089 prev = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002090 for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002091 prev = cont;
2092 }
2093
2094 if (cont) {
2095 /* Remove found message from buffer */
2096 if (prev) {
2097 prev->next = cont->next;
2098 } else {
2099 session->opts.client.msgs = cont->next;
2100 }
2101
2102 /* Use the buffer message */
2103 ret = cont->type;
2104 msg = cont->msg;
2105 free(cont);
Michal Vasko01130bd2021-08-26 11:47:38 +02002106 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002107 }
2108
2109 /* Read a message from the wire */
2110 r = nc_read_msg_poll_io(session, timeout, &msg);
2111 if (!r) {
2112 ret = NC_MSG_WOULDBLOCK;
Michal Vasko01130bd2021-08-26 11:47:38 +02002113 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002114 } else if (r == -1) {
2115 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02002116 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002117 }
2118
2119 /* Basic check to determine message type */
2120 ret = get_msg_type(session, msg);
2121 if (ret == NC_MSG_ERROR) {
Michal Vasko01130bd2021-08-26 11:47:38 +02002122 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002123 }
2124
2125 /* If received a message of different type store it in the buffer */
2126 if (ret != expected) {
2127 cont_ptr = &session->opts.client.msgs;
2128 while (*cont_ptr) {
2129 cont_ptr = &((*cont_ptr)->next);
2130 }
2131 *cont_ptr = malloc(sizeof **cont_ptr);
2132 if (!*cont_ptr) {
2133 ERRMEM;
2134 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02002135 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002136 }
2137 (*cont_ptr)->msg = msg;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002138 msg = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002139 (*cont_ptr)->type = ret;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002140 (*cont_ptr)->next = NULL;
2141 }
2142
Michal Vasko01130bd2021-08-26 11:47:38 +02002143cleanup_unlock:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002144 /* MSGS UNLOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002145 nc_session_client_msgs_unlock(session, __func__);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002146
Michal Vasko01130bd2021-08-26 11:47:38 +02002147cleanup:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002148 if (ret == expected) {
2149 *message = msg;
2150 } else {
2151 ly_in_free(msg, 1);
2152 }
2153 return ret;
2154}
2155
Michal Vasko77367452021-02-16 16:32:18 +01002156static NC_MSG_TYPE
2157recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
2158{
Michal Vasko77367452021-02-16 16:32:18 +01002159 LY_ERR lyrc;
2160 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002161 NC_MSG_TYPE ret = NC_MSG_ERROR;
2162
2163 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
2164
2165 *envp = NULL;
2166
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002167 /* Receive messages until a rpc-reply is found or a timeout or error reached */
2168 ret = recv_msg(session, timeout, NC_MSG_REPLY, &msg);
2169 if (ret != NC_MSG_REPLY) {
Michal Vasko77367452021-02-16 16:32:18 +01002170 goto cleanup;
2171 }
2172
2173 /* parse */
2174 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
2175 if (!lyrc) {
2176 ret = recv_reply_check_msgid(session, *envp, msgid);
2177 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002178 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002179 ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op)));
Michal Vasko9a108052022-04-01 12:06:54 +02002180 lyd_free_tree(*envp);
2181 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002182 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002183 goto cleanup;
2184 }
2185
Michal Vasko77367452021-02-16 16:32:18 +01002186cleanup:
2187 ly_in_free(msg, 1);
2188 return ret;
2189}
2190
2191static int
2192recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
2193{
Michal Vasko143aa142021-10-01 15:31:48 +02002194 LY_ERR lyrc = LY_SUCCESS;
Michal Vasko77367452021-02-16 16:32:18 +01002195 struct nc_rpc_act_generic *rpc_gen;
2196 struct ly_in *in;
2197 struct lyd_node *tree, *op2;
2198 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01002199 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002200
2201 switch (rpc->type) {
2202 case NC_RPC_ACT_GENERIC:
2203 rpc_gen = (struct nc_rpc_act_generic *)rpc;
2204 if (rpc_gen->has_data) {
2205 tree = rpc_gen->content.data;
2206
2207 /* find the operation node */
2208 lyrc = LY_EINVAL;
2209 LYD_TREE_DFS_BEGIN(tree, op2) {
2210 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
2211 lyrc = lyd_dup_single(op2, NULL, 0, op);
2212 break;
2213 }
2214 LYD_TREE_DFS_END(tree, op2);
2215 }
2216 } else {
2217 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2218 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
2219 ly_in_free(in, 0);
2220 if (lyrc) {
Michal Vasko9a108052022-04-01 12:06:54 +02002221 lyd_free_tree(tree);
Michal Vasko77367452021-02-16 16:32:18 +01002222 return -1;
2223 }
2224
2225 /* we want just the operation node */
2226 lyrc = lyd_dup_single(op2, NULL, 0, op);
2227
2228 lyd_free_tree(tree);
2229 }
2230 break;
2231 case NC_RPC_GETCONFIG:
2232 module_name = "ietf-netconf";
2233 rpc_name = "get-config";
2234 break;
2235 case NC_RPC_EDIT:
2236 module_name = "ietf-netconf";
2237 rpc_name = "edit-config";
2238 break;
2239 case NC_RPC_COPY:
2240 module_name = "ietf-netconf";
2241 rpc_name = "copy-config";
2242 break;
2243 case NC_RPC_DELETE:
2244 module_name = "ietf-netconf";
2245 rpc_name = "delete-config";
2246 break;
2247 case NC_RPC_LOCK:
2248 module_name = "ietf-netconf";
2249 rpc_name = "lock";
2250 break;
2251 case NC_RPC_UNLOCK:
2252 module_name = "ietf-netconf";
2253 rpc_name = "unlock";
2254 break;
2255 case NC_RPC_GET:
2256 module_name = "ietf-netconf";
2257 rpc_name = "get";
2258 break;
2259 case NC_RPC_KILL:
2260 module_name = "ietf-netconf";
2261 rpc_name = "kill-session";
2262 break;
2263 case NC_RPC_COMMIT:
2264 module_name = "ietf-netconf";
2265 rpc_name = "commit";
2266 break;
2267 case NC_RPC_DISCARD:
2268 module_name = "ietf-netconf";
2269 rpc_name = "discard-changes";
2270 break;
2271 case NC_RPC_CANCEL:
2272 module_name = "ietf-netconf";
2273 rpc_name = "cancel-commit";
2274 break;
2275 case NC_RPC_VALIDATE:
2276 module_name = "ietf-netconf";
2277 rpc_name = "validate";
2278 break;
2279 case NC_RPC_GETSCHEMA:
2280 module_name = "ietf-netconf-monitoring";
2281 rpc_name = "get-schema";
2282 break;
2283 case NC_RPC_SUBSCRIBE:
2284 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02002285 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01002286 break;
2287 case NC_RPC_GETDATA:
2288 module_name = "ietf-netconf-nmda";
2289 rpc_name = "get-data";
2290 break;
2291 case NC_RPC_EDITDATA:
2292 module_name = "ietf-netconf-nmda";
2293 rpc_name = "edit-data";
2294 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002295 case NC_RPC_ESTABLISHSUB:
2296 module_name = "ietf-subscribed-notifications";
2297 rpc_name = "establish-subscription";
2298 break;
2299 case NC_RPC_MODIFYSUB:
2300 module_name = "ietf-subscribed-notifications";
2301 rpc_name = "modify-subscription";
2302 break;
2303 case NC_RPC_DELETESUB:
2304 module_name = "ietf-subscribed-notifications";
2305 rpc_name = "delete-subscription";
2306 break;
2307 case NC_RPC_KILLSUB:
2308 module_name = "ietf-subscribed-notifications";
2309 rpc_name = "kill-subscription";
2310 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002311 case NC_RPC_ESTABLISHPUSH:
2312 module_name = "ietf-subscribed-notifications";
2313 rpc_name = "establish-subscription";
2314 module_check = "ietf-yang-push";
2315 break;
2316 case NC_RPC_MODIFYPUSH:
2317 module_name = "ietf-subscribed-notifications";
2318 rpc_name = "modify-subscription";
2319 module_check = "ietf-yang-push";
2320 break;
2321 case NC_RPC_RESYNCSUB:
2322 module_name = "ietf-yang-push";
2323 rpc_name = "resync-subscription";
2324 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002325 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01002326 lyrc = LY_EINT;
2327 break;
2328 }
2329
2330 if (module_name && rpc_name) {
2331 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
2332 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002333 ERR(session, "Missing \"%s\" module in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01002334 return -1;
2335 }
2336
2337 /* create the operation node */
2338 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
2339 }
Michal Vasko305faca2021-03-25 09:16:02 +01002340 if (module_check) {
2341 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002342 ERR(session, "Missing \"%s\" module in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01002343 return -1;
2344 }
2345 }
Michal Vasko77367452021-02-16 16:32:18 +01002346
2347 if (lyrc) {
2348 return -1;
2349 }
2350 return 0;
2351}
2352
2353API NC_MSG_TYPE
2354nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
2355 struct lyd_node **op)
2356{
2357 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002358
Michal Vasko45e53ae2016-04-07 11:46:03 +02002359 if (!session) {
2360 ERRARG("session");
2361 return NC_MSG_ERROR;
2362 } else if (!rpc) {
2363 ERRARG("rpc");
2364 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002365 } else if (!envp) {
2366 ERRARG("envp");
Michal Vasko45e53ae2016-04-07 11:46:03 +02002367 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002368 } else if (!op) {
2369 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002370 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002371 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002372 ERR(session, "Invalid session to receive RPC replies.");
Michal Vasko086311b2016-01-08 09:53:11 +01002373 return NC_MSG_ERROR;
2374 }
Michal Vasko77367452021-02-16 16:32:18 +01002375
2376 /* get a duplicate of the RPC node to append reply to */
2377 if (recv_reply_dup_rpc(session, rpc, op)) {
2378 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01002379 }
Michal Vasko086311b2016-01-08 09:53:11 +01002380
Michal Vasko77367452021-02-16 16:32:18 +01002381 /* receive a reply */
2382 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01002383
Michal Vasko77367452021-02-16 16:32:18 +01002384 /* do not return the RPC copy on error or if the reply includes no data */
2385 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
2386 lyd_free_tree(*op);
2387 *op = NULL;
2388 }
2389 return ret;
2390}
2391
2392static NC_MSG_TYPE
2393recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
2394{
Michal Vasko77367452021-02-16 16:32:18 +01002395 LY_ERR lyrc;
2396 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002397 NC_MSG_TYPE ret = NC_MSG_ERROR;
2398
2399 *op = NULL;
2400 *envp = NULL;
2401
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002402 /* Receive messages until a notification is found or a timeout or error reached */
2403 ret = recv_msg(session, timeout, NC_MSG_NOTIF, &msg);
2404 if (ret != NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002405 goto cleanup;
2406 }
2407
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002408 /* Parse */
Michal Vasko77367452021-02-16 16:32:18 +01002409 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2410 if (!lyrc) {
Michal Vasko77367452021-02-16 16:32:18 +01002411 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002412 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002413 ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx));
Michal Vasko9a108052022-04-01 12:06:54 +02002414 lyd_free_tree(*envp);
2415 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002416 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002417 goto cleanup;
2418 }
2419
Michal Vasko77367452021-02-16 16:32:18 +01002420cleanup:
2421 ly_in_free(msg, 1);
2422 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002423}
2424
2425API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002426nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002427{
Michal Vasko45e53ae2016-04-07 11:46:03 +02002428 if (!session) {
2429 ERRARG("session");
2430 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002431 } else if (!envp) {
2432 ERRARG("envp");
2433 return NC_MSG_ERROR;
2434 } else if (!op) {
2435 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002436 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002437 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002438 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002439 return NC_MSG_ERROR;
2440 }
2441
Michal Vasko77367452021-02-16 16:32:18 +01002442 /* receive a notification */
2443 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002444}
2445
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002446static void *
2447nc_recv_notif_thread(void *arg)
2448{
2449 struct nc_ntf_thread_arg *ntarg;
2450 struct nc_session *session;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002451 nc_notif_dispatch_clb notif_clb;
2452 void *user_data;
Michal Vasko743ff9f2022-10-20 10:03:13 +02002453
Michal Vaskoffb35e92022-10-20 09:07:25 +02002454 void (*free_data)(void *);
Michal Vasko77367452021-02-16 16:32:18 +01002455 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002456 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002457
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002458 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002459 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002460
2461 ntarg = (struct nc_ntf_thread_arg *)arg;
2462 session = ntarg->session;
2463 notif_clb = ntarg->notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002464 user_data = ntarg->user_data;
2465 free_data = ntarg->free_data;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002466 free(ntarg);
2467
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002468 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running)) {
Michal Vasko77367452021-02-16 16:32:18 +01002469 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002470 if (msgtype == NC_MSG_NOTIF) {
Michal Vaskoffb35e92022-10-20 09:07:25 +02002471 notif_clb(session, envp, op, user_data);
Michal Vasko77367452021-02-16 16:32:18 +01002472 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2473 lyd_free_tree(envp);
Michal Vasko0c0239c2023-02-01 14:31:06 +01002474 lyd_free_all(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002475 break;
2476 }
Michal Vasko77367452021-02-16 16:32:18 +01002477 lyd_free_tree(envp);
Michal Vasko0c0239c2023-02-01 14:31:06 +01002478 lyd_free_all(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002479 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002480 /* quit this thread once the session is broken */
2481 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002482 }
2483
2484 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2485 }
2486
Michal Vasko05532772021-06-03 12:12:38 +02002487 VRB(session, "Notification thread exit.");
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002488 ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoffb35e92022-10-20 09:07:25 +02002489 if (free_data) {
2490 free_data(user_data);
2491 }
2492
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002493 return NULL;
2494}
2495
2496API int
Michal Vaskoffb35e92022-10-20 09:07:25 +02002497nc_recv_notif_dispatch(struct nc_session *session, nc_notif_dispatch_clb notif_clb)
2498{
2499 return nc_recv_notif_dispatch_data(session, notif_clb, NULL, NULL);
2500}
2501
2502API int
2503nc_recv_notif_dispatch_data(struct nc_session *session, nc_notif_dispatch_clb notif_clb, void *user_data,
2504 void (*free_data)(void *))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002505{
2506 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002507 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002508 int ret;
2509
Michal Vasko45e53ae2016-04-07 11:46:03 +02002510 if (!session) {
2511 ERRARG("session");
2512 return -1;
2513 } else if (!notif_clb) {
2514 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002515 return -1;
2516 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002517 ERR(session, "Invalid session to receive Notifications.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002518 return -1;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002519 }
2520
2521 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002522 if (!ntarg) {
2523 ERRMEM;
2524 return -1;
2525 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002526 ntarg->session = session;
2527 ntarg->notif_clb = notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002528 ntarg->user_data = user_data;
2529 ntarg->free_data = free_data;
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002530 ATOMIC_INC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002531
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002532 /* just so that nc_recv_notif_thread() does not immediately exit */
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002533 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002534
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002535 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002536 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002537 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002538 free(ntarg);
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002539 if (ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count) == 1) {
2540 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 0);
2541 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002542 return -1;
2543 }
2544
2545 return 0;
2546}
2547
Michal Vasko77367452021-02-16 16:32:18 +01002548static const char *
2549nc_wd2str(NC_WD_MODE wd)
2550{
2551 switch (wd) {
2552 case NC_WD_ALL:
2553 return "report-all";
2554 case NC_WD_ALL_TAG:
2555 return "report-all-tagged";
2556 case NC_WD_TRIM:
2557 return "trim";
2558 case NC_WD_EXPLICIT:
2559 return "explicit";
2560 default:
2561 break;
2562 }
2563
2564 return NULL;
2565}
2566
Michal Vasko086311b2016-01-08 09:53:11 +01002567API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002568nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002569{
2570 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002571 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002572 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002573 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002574 struct nc_rpc_getconfig *rpc_gc;
2575 struct nc_rpc_edit *rpc_e;
2576 struct nc_rpc_copy *rpc_cp;
2577 struct nc_rpc_delete *rpc_del;
2578 struct nc_rpc_lock *rpc_lock;
2579 struct nc_rpc_get *rpc_g;
2580 struct nc_rpc_kill *rpc_k;
2581 struct nc_rpc_commit *rpc_com;
2582 struct nc_rpc_cancel *rpc_can;
2583 struct nc_rpc_validate *rpc_val;
2584 struct nc_rpc_getschema *rpc_gs;
2585 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002586 struct nc_rpc_getdata *rpc_getd;
2587 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002588 struct nc_rpc_establishsub *rpc_estsub;
2589 struct nc_rpc_modifysub *rpc_modsub;
2590 struct nc_rpc_deletesub *rpc_delsub;
2591 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002592 struct nc_rpc_establishpush *rpc_estpush;
2593 struct nc_rpc_modifypush *rpc_modpush;
2594 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002595 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002596 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002597 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002598 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002599 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002600 uint64_t cur_msgid;
2601
Michal Vasko45e53ae2016-04-07 11:46:03 +02002602 if (!session) {
2603 ERRARG("session");
2604 return NC_MSG_ERROR;
2605 } else if (!rpc) {
2606 ERRARG("rpc");
2607 return NC_MSG_ERROR;
2608 } else if (!msgid) {
2609 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002610 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002611 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002612 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002613 return NC_MSG_ERROR;
2614 }
2615
Michal Vaskoc1171a42019-11-05 12:06:46 +01002616 switch (rpc->type) {
2617 case NC_RPC_ACT_GENERIC:
2618 /* checked when parsing */
2619 break;
2620 case NC_RPC_GETCONFIG:
2621 case NC_RPC_EDIT:
2622 case NC_RPC_COPY:
2623 case NC_RPC_DELETE:
2624 case NC_RPC_LOCK:
2625 case NC_RPC_UNLOCK:
2626 case NC_RPC_GET:
2627 case NC_RPC_KILL:
2628 case NC_RPC_COMMIT:
2629 case NC_RPC_DISCARD:
2630 case NC_RPC_CANCEL:
2631 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002632 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002633 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002634 ERR(session, "Missing \"ietf-netconf\" module in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002635 return NC_MSG_ERROR;
2636 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002637 break;
2638 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002639 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002640 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002641 ERR(session, "Missing \"ietf-netconf-monitoring\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002642 return NC_MSG_ERROR;
2643 }
2644 break;
2645 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002646 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002647 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002648 ERR(session, "Missing \"notifications\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002649 return NC_MSG_ERROR;
2650 }
2651 break;
2652 case NC_RPC_GETDATA:
2653 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002654 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002655 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002656 ERR(session, "Missing \"ietf-netconf-nmda\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002657 return NC_MSG_ERROR;
2658 }
2659 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002660 case NC_RPC_ESTABLISHSUB:
2661 case NC_RPC_MODIFYSUB:
2662 case NC_RPC_DELETESUB:
2663 case NC_RPC_KILLSUB:
2664 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2665 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002666 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002667 return NC_MSG_ERROR;
2668 }
2669 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002670 case NC_RPC_ESTABLISHPUSH:
2671 case NC_RPC_MODIFYPUSH:
2672 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2673 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002674 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002675 return NC_MSG_ERROR;
2676 }
2677 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2678 if (!mod2) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002679 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002680 return NC_MSG_ERROR;
2681 }
2682 break;
2683 case NC_RPC_RESYNCSUB:
2684 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2685 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002686 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002687 return NC_MSG_ERROR;
2688 }
2689 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002690 case NC_RPC_UNKNOWN:
2691 ERRINT;
2692 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002693 }
2694
Michal Vaskoab9deb62021-05-27 11:37:00 +02002695#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2696
Michal Vasko086311b2016-01-08 09:53:11 +01002697 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002698 case NC_RPC_ACT_GENERIC:
2699 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002700
2701 if (rpc_gen->has_data) {
2702 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002703 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002704 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002705 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2706 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2707 ly_in_free(in, 0);
2708 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002709 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002710 }
Michal Vasko086311b2016-01-08 09:53:11 +01002711 }
2712 break;
2713
2714 case NC_RPC_GETCONFIG:
2715 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2716
Michal Vaskoab9deb62021-05-27 11:37:00 +02002717 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2718 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2719 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002720 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002721 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002722 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node));
2723 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002724 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002725 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2726 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2727 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002728 }
2729 }
2730
2731 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002732 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002733 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002734 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002735 lyrc = LY_ENOTFOUND;
2736 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002737 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002738 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 +01002739 }
2740 break;
2741
2742 case NC_RPC_EDIT:
2743 rpc_e = (struct nc_rpc_edit *)rpc;
2744
Michal Vaskoab9deb62021-05-27 11:37:00 +02002745 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2746 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2747 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002748
2749 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002750 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 +01002751 }
Michal Vasko086311b2016-01-08 09:53:11 +01002752 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002753 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 +01002754 }
Michal Vasko086311b2016-01-08 09:53:11 +01002755 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002756 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 +01002757 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002758 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002759 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 +01002760 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002761 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002762 }
2763 break;
2764
2765 case NC_RPC_COPY:
2766 rpc_cp = (struct nc_rpc_copy *)rpc;
2767
Michal Vaskoab9deb62021-05-27 11:37:00 +02002768 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2769 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002770 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002771 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002772 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002773 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002774 }
2775
Michal Vaskoab9deb62021-05-27 11:37:00 +02002776 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002777 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002778 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002779 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 +01002780 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002781 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002782 }
2783 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002784 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002785 }
2786
2787 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002788 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002789 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002790 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002791 lyrc = LY_ENOTFOUND;
2792 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002793 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002794 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 +01002795 }
2796 break;
2797
2798 case NC_RPC_DELETE:
2799 rpc_del = (struct nc_rpc_delete *)rpc;
2800
Michal Vaskoab9deb62021-05-27 11:37:00 +02002801 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2802 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002803 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002804 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002805 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002806 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002807 }
2808 break;
2809
2810 case NC_RPC_LOCK:
2811 rpc_lock = (struct nc_rpc_lock *)rpc;
2812
Michal Vaskoab9deb62021-05-27 11:37:00 +02002813 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2814 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2815 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002816 break;
2817
2818 case NC_RPC_UNLOCK:
2819 rpc_lock = (struct nc_rpc_lock *)rpc;
2820
Michal Vaskoab9deb62021-05-27 11:37:00 +02002821 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2822 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2823 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002824 break;
2825
2826 case NC_RPC_GET:
2827 rpc_g = (struct nc_rpc_get *)rpc;
2828
Michal Vaskoab9deb62021-05-27 11:37:00 +02002829 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002830 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002831 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002832 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node));
2833 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002834 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002835 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2836 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2837 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002838 }
2839 }
2840
2841 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002842 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002843 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002844 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002845 lyrc = LY_ENOTFOUND;
2846 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002847 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002848 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 +01002849 }
2850 break;
2851
2852 case NC_RPC_KILL:
2853 rpc_k = (struct nc_rpc_kill *)rpc;
2854
Michal Vaskoab9deb62021-05-27 11:37:00 +02002855 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002856 sprintf(str, "%u", rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002857 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002858 break;
2859
2860 case NC_RPC_COMMIT:
2861 rpc_com = (struct nc_rpc_commit *)rpc;
2862
Michal Vaskoab9deb62021-05-27 11:37:00 +02002863 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002864 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002865 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002866 }
2867
2868 if (rpc_com->confirm_timeout) {
2869 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002870 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002871 }
Michal Vasko086311b2016-01-08 09:53:11 +01002872 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002873 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002874 }
Michal Vasko086311b2016-01-08 09:53:11 +01002875 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002876 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002877 }
2878 break;
2879
2880 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002881 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002882 break;
2883
2884 case NC_RPC_CANCEL:
2885 rpc_can = (struct nc_rpc_cancel *)rpc;
2886
Michal Vaskoab9deb62021-05-27 11:37:00 +02002887 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002888 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002889 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002890 }
2891 break;
2892
2893 case NC_RPC_VALIDATE:
2894 rpc_val = (struct nc_rpc_validate *)rpc;
2895
Michal Vaskoab9deb62021-05-27 11:37:00 +02002896 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2897 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002898 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002899 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002900 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 +01002901 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002902 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002903 }
2904 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002905 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002906 }
2907 break;
2908
2909 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002910 rpc_gs = (struct nc_rpc_getschema *)rpc;
2911
Michal Vaskoab9deb62021-05-27 11:37:00 +02002912 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2913 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002914 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002915 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002916 }
2917 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002918 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002919 }
2920 break;
2921
2922 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002923 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2924
Michal Vaskoab9deb62021-05-27 11:37:00 +02002925 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002926 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002927 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002928 }
2929
2930 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002931 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002932 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node));
2933 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002934 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002935 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2936 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2937 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002938 }
2939 }
Michal Vasko086311b2016-01-08 09:53:11 +01002940 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002941 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002942 }
Michal Vasko086311b2016-01-08 09:53:11 +01002943 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002944 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002945 }
2946 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002947
2948 case NC_RPC_GETDATA:
2949 rpc_getd = (struct nc_rpc_getdata *)rpc;
2950
Michal Vaskoab9deb62021-05-27 11:37:00 +02002951 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2952 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2953
Michal Vaskoc1171a42019-11-05 12:06:46 +01002954 if (rpc_getd->filter) {
2955 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002956 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 +01002957 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002958 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002959 }
2960 }
2961 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002962 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002963 }
2964 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002965 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2966 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002967 }
2968 if (rpc_getd->max_depth) {
2969 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002970 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002971 }
2972 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002973 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002974 }
2975
2976 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002977 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002978 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 +01002979 }
2980 break;
2981
2982 case NC_RPC_EDITDATA:
2983 rpc_editd = (struct nc_rpc_editdata *)rpc;
2984
Michal Vaskoab9deb62021-05-27 11:37:00 +02002985 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2986 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002987
2988 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002989 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2990 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002991 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002992 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002993 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 +01002994 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002995 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002996 }
2997 break;
2998
Michal Vasko96f247a2021-03-15 13:32:10 +01002999 case NC_RPC_ESTABLISHSUB:
3000 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
3001
Michal Vaskoab9deb62021-05-27 11:37:00 +02003002 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003003
3004 if (rpc_estsub->filter) {
3005 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003006 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML,
3007 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003008 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003009 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003010 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003011 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003012 }
3013 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02003014 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003015
3016 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003017 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003018 }
Michal Vasko96f247a2021-03-15 13:32:10 +01003019 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003020 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003021 }
Michal Vasko96f247a2021-03-15 13:32:10 +01003022 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003023 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003024 }
3025 break;
3026
3027 case NC_RPC_MODIFYSUB:
3028 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
3029
Michal Vaskoab9deb62021-05-27 11:37:00 +02003030 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003031
3032 sprintf(str, "%u", rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003033 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003034
3035 if (rpc_modsub->filter) {
3036 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003037 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML,
3038 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003039 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003040 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003041 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003042 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003043 }
3044 }
Michal Vasko96f247a2021-03-15 13:32:10 +01003045 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003046 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003047 }
3048 break;
3049
3050 case NC_RPC_DELETESUB:
3051 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
3052
Michal Vaskoab9deb62021-05-27 11:37:00 +02003053 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003054
3055 sprintf(str, "%u", rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003056 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003057 break;
3058
3059 case NC_RPC_KILLSUB:
3060 rpc_killsub = (struct nc_rpc_killsub *)rpc;
3061
Michal Vaskoab9deb62021-05-27 11:37:00 +02003062 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003063
3064 sprintf(str, "%u", rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003065 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003066 break;
3067
Michal Vasko305faca2021-03-25 09:16:02 +01003068 case NC_RPC_ESTABLISHPUSH:
3069 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
3070
Michal Vaskoab9deb62021-05-27 11:37:00 +02003071 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
3072 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003073
3074 if (rpc_estpush->filter) {
3075 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003076 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0,
3077 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003078 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003079 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003080 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003081 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003082 }
3083 }
3084
3085 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003086 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003087 }
Michal Vasko305faca2021-03-25 09:16:02 +01003088 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003089 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003090 }
3091
3092 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003093 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003094 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003095 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003096 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003097 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003098 }
3099 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003100 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003101 if (rpc_estpush->dampening_period) {
3102 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003103 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003104 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02003105 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
3106 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003107 if (rpc_estpush->excluded_change) {
3108 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003109 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
3110 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003111 }
3112 }
3113 }
3114 break;
3115
3116 case NC_RPC_MODIFYPUSH:
3117 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
3118
Michal Vaskoab9deb62021-05-27 11:37:00 +02003119 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01003120
3121 sprintf(str, "%u", rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003122 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
3123 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003124
3125 if (rpc_modpush->filter) {
3126 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003127 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0,
3128 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003129 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003130 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003131 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003132 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003133 }
3134 }
Michal Vasko305faca2021-03-25 09:16:02 +01003135 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003136 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003137 }
3138
3139 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003140 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003141 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003142 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003143 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003144 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003145 }
3146 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003147 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003148 if (rpc_modpush->dampening_period) {
3149 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003150 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003151 }
3152 }
3153 break;
3154
3155 case NC_RPC_RESYNCSUB:
3156 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
3157
Michal Vaskoab9deb62021-05-27 11:37:00 +02003158 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01003159 sprintf(str, "%u", rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003160 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003161 break;
3162
Michal Vasko96f247a2021-03-15 13:32:10 +01003163 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01003164 ERRINT;
3165 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01003166 }
3167
Michal Vaskoab9deb62021-05-27 11:37:00 +02003168#undef CHECK_LYRC_BREAK
3169
3170 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02003171 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02003172 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02003173 return NC_MSG_ERROR;
3174 }
3175
Michal Vasko131120a2018-05-29 15:44:02 +02003176 /* send RPC, store its message ID */
3177 r = nc_send_msg_io(session, timeout, data);
3178 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003179
Radek Krejcib4b19062018-02-07 16:33:06 +01003180 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01003181 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01003182 }
Michal Vasko086311b2016-01-08 09:53:11 +01003183
Michal Vasko131120a2018-05-29 15:44:02 +02003184 if (r == NC_MSG_RPC) {
3185 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003186 }
Michal Vasko131120a2018-05-29 15:44:02 +02003187 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01003188}
Michal Vaskode2946c2017-01-12 12:19:26 +01003189
3190API void
3191nc_client_session_set_not_strict(struct nc_session *session)
3192{
3193 if (session->side != NC_CLIENT) {
3194 ERRARG("session");
3195 return;
3196 }
3197
3198 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
3199}