blob: ead3f633bb62394d973f768b8431a06b491bb9a9 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_client.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 session client functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
14
Radek Krejcifd5b6682017-06-13 15:52:53 +020015#define _GNU_SOURCE
Michal Vaskobbf52c82020-04-07 13:08:50 +020016
17#ifdef __linux__
18# include <sys/syscall.h>
19#endif
20
Michal Vaskob83a3fa2021-05-26 09:53:42 +020021#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010022#include <assert.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <netdb.h>
Radek Krejci4cf58ec2016-02-26 15:04:52 +010026#include <netinet/in.h>
Michal Vasko83ad17e2019-01-30 10:11:37 +010027#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020028#include <poll.h>
Michal Vasko086311b2016-01-08 09:53:11 +010029#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020030#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010031#include <stdlib.h>
32#include <string.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020033#include <sys/select.h>
Michal Vasko086311b2016-01-08 09:53:11 +010034#include <sys/socket.h>
35#include <sys/stat.h>
36#include <sys/types.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020037#include <sys/un.h>
Michal Vasko086311b2016-01-08 09:53:11 +010038#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010039
40#include <libyang/libyang.h>
41
Michal Vasko9e8ac262020-04-07 13:06:45 +020042#include "compat.h"
Michal Vasko086311b2016-01-08 09:53:11 +010043#include "libnetconf.h"
Michal Vaskoa8ad4482016-01-28 14:25:54 +010044#include "messages_client.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020045#include "session_client.h"
Michal Vasko086311b2016-01-08 09:53:11 +010046
Michal Vasko8a4e1462020-05-07 11:32:31 +020047#include "../modules/ietf_netconf@2013-09-29_yang.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020048#include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h"
Michal Vasko8a4e1462020-05-07 11:32:31 +020049
Michal Vasko80ef5d22016-01-18 09:21:02 +010050static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
51
Radek Krejci62aa0642017-05-25 16:33:49 +020052#ifdef NC_ENABLED_SSH
53int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
54char *sshauth_password(const char *username, const char *hostname, void *priv);
55char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020056char *sshauth_privkey_passphrase(const char *privkey_path, void *priv);
Radek Krejci62aa0642017-05-25 16:33:49 +020057#endif /* NC_ENABLED_SSH */
58
59static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
60static pthread_key_t nc_client_context_key;
61#ifdef __linux__
62static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020063 .opts.ka = {
64 .enabled = 1,
65 .idle_time = 1,
66 .max_probes = 10,
67 .probe_interval = 5
68 },
Radek Krejci62aa0642017-05-25 16:33:49 +020069#ifdef NC_ENABLED_SSH
70 .ssh_opts = {
71 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
72 .auth_hostkey_check = sshauth_hostkey_check,
73 .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}},
79 .auth_hostkey_check = sshauth_hostkey_check,
80 .auth_password = sshauth_password,
81 .auth_interactive = sshauth_interactive,
82 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020083 },
Radek Krejci62aa0642017-05-25 16:33:49 +020084#endif /* NC_ENABLED_SSH */
85 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020086 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020087};
88#endif
89
90static void
91nc_client_context_free(void *ptr)
92{
93 struct nc_client_context *c = (struct nc_client_context *)ptr;
94
Radek Krejci5cebc6b2017-05-26 13:24:38 +020095 if (--(c->refcount)) {
96 /* still used */
97 return;
98 }
99
Radek Krejci62aa0642017-05-25 16:33:49 +0200100#ifdef __linux__
101 /* in __linux__ we use static memory in the main thread,
102 * so this check is for programs terminating the main()
103 * function by pthread_exit() :)
104 */
105 if (c != &context_main)
106#endif
107 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200108 /* for the main thread the same is done in nc_client_destroy() */
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400109 free(c->opts.schema_searchpath);
110
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200111#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400112 int i;
113 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;
164 e->ssh_opts.auth_pref[0].value = 3;
165 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;
168 e->ssh_opts.auth_pref[2].value = 1;
169 e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check;
170 e->ssh_opts.auth_password = sshauth_password;
171 e->ssh_opts.auth_interactive = sshauth_interactive;
172 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
173
174 /* callhome settings are the same except the inverted auth methods preferences */
175 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
176 e->ssh_ch_opts.auth_pref[0].value = 1;
177 e->ssh_ch_opts.auth_pref[1].value = 2;
178 e->ssh_ch_opts.auth_pref[2].value = 3;
179#endif /* NC_ENABLED_SSH */
180 }
181 pthread_setspecific(nc_client_context_key, e);
182 }
183
184 return e;
185}
186
187#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100188
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200189API void *
190nc_client_get_thread_context(void)
191{
192 return nc_client_context_location();
193}
194
195API void
196nc_client_set_thread_context(void *context)
197{
198 struct nc_client_context *old, *new;
199
200 if (!context) {
201 ERRARG(context);
202 return;
203 }
204
205 new = (struct nc_client_context *)context;
206 old = nc_client_context_location();
207 if (old == new) {
208 /* nothing to change */
209 return;
210 }
211
212 /* replace old by new, increase reference counter in the newly set context */
213 nc_client_context_free(old);
214 new->refcount++;
215 pthread_setspecific(nc_client_context_key, new);
216}
217
Radek Krejcifd5b6682017-06-13 15:52:53 +0200218int
219nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
220{
221 /* assign context (dicionary needed for handshake) */
222 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100223 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200224 return EXIT_FAILURE;
225 }
226
227 /* user path must be first, the first path is used to store schemas retreived via get-schema */
228 if (client_opts.schema_searchpath) {
229 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200230 } else if (!access(NC_YANG_DIR, F_OK)) {
231 ly_ctx_set_searchdir(ctx, NC_YANG_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200232 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200233
234 /* set callback for getting schemas, if provided */
235 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
236 } else {
237 session->flags |= NC_SESSION_SHAREDCTX;
238 }
239
240 session->ctx = ctx;
241
242 return EXIT_SUCCESS;
243}
244
Michal Vasko086311b2016-01-08 09:53:11 +0100245API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100246nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100247{
Michal Vasko3031aae2016-01-27 16:07:18 +0100248 if (client_opts.schema_searchpath) {
249 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100250 }
Michal Vasko086311b2016-01-08 09:53:11 +0100251
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100252 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100253 client_opts.schema_searchpath = strdup(path);
254 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100255 ERRMEM;
256 return 1;
257 }
258 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100259 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100260 }
261
262 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100263}
264
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100265API const char *
266nc_client_get_schema_searchpath(void)
267{
268 return client_opts.schema_searchpath;
269}
270
Radek Krejcifd5b6682017-06-13 15:52:53 +0200271API int
272nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100273{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200274 client_opts.schema_clb = clb;
275 if (clb) {
276 client_opts.schema_clb_data = user_data;
277 } else {
278 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100279 }
280
281 return 0;
282}
283
Radek Krejcifd5b6682017-06-13 15:52:53 +0200284API ly_module_imp_clb
285nc_client_get_schema_callback(void **user_data)
286{
287 if (user_data) {
288 (*user_data) = client_opts.schema_clb_data;
289 }
290 return client_opts.schema_clb;
291}
292
Radek Krejci65ef6d52018-08-16 16:35:02 +0200293struct schema_info {
294 char *name;
295 char *revision;
296 struct {
297 char *name;
298 char *revision;
299 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100300 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200301 int implemented;
302};
303
304struct clb_data_s {
305 void *user_data;
306 ly_module_imp_clb user_clb;
307 struct schema_info *schemas;
308 struct nc_session *session;
309 int has_get_schema;
310};
311
312static char *
313retrieve_schema_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200314 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100315{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200316 char *localfile = NULL;
317 FILE *f;
318 long length, l;
319 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100320
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200321 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200322 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
323 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200324 return NULL;
325 }
326 if (localfile) {
327 VRB("Session %u: reading schema from localfile \"%s\".", clb_data->session->id, localfile);
328 f = fopen(localfile, "r");
329 if (!f) {
330 ERR("Session %u: unable to open \"%s\" file to get schema (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200331 clb_data->session->id, localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200332 free(localfile);
333 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100334 }
Michal Vasko086311b2016-01-08 09:53:11 +0100335
Radek Krejci65ef6d52018-08-16 16:35:02 +0200336 fseek(f, 0, SEEK_END);
337 length = ftell(f);
Radek Krejci3f499a62018-08-17 10:16:57 +0200338 if (length < 0) {
339 ERR("Session %u: unable to get size of schema file \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200340 clb_data->session->id, localfile);
Radek Krejci3f499a62018-08-17 10:16:57 +0200341 free(localfile);
342 fclose(f);
343 return NULL;
344 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200345 fseek(f, 0, SEEK_SET);
346
347 model_data = malloc(length + 1);
348 if (!model_data) {
349 ERRMEM;
350 } else if ((l = fread(model_data, 1, length, f)) != length) {
351 ERR("Session %u: reading schema from \"%s\" failed (%d bytes read, but %d expected).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200352 clb_data->session->id, localfile, l, length);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200353 free(model_data);
354 model_data = NULL;
355 } else {
356 /* terminating NULL byte */
357 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100358 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200359 fclose(f);
360 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100361 }
362
Radek Krejci65ef6d52018-08-16 16:35:02 +0200363 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100364}
365
366static char *
Radek Krejci65ef6d52018-08-16 16:35:02 +0200367retrieve_schema_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200368 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100369{
Michal Vasko086311b2016-01-08 09:53:11 +0100370 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100371 struct lyd_node *envp = NULL, *op = NULL;
372 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100373 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100374 uint64_t msgid;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200375 char *localfile = NULL;
376 FILE *f;
377 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100378
Radek Krejci65ef6d52018-08-16 16:35:02 +0200379 VRB("Session %u: reading schema from server via get-schema.", clb_data->session->id);
380 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100381
Radek Krejci65ef6d52018-08-16 16:35:02 +0200382 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100383 usleep(1000);
384 }
385 if (msg == NC_MSG_ERROR) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200386 ERR("Session %u: failed to send the <get-schema> RPC.", clb_data->session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100387 nc_rpc_free(rpc);
388 return NULL;
389 }
390
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200391 do {
Michal Vasko77367452021-02-16 16:32:18 +0100392 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200393 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100394 nc_rpc_free(rpc);
395 if (msg == NC_MSG_WOULDBLOCK) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200396 ERR("Session %u: timeout for receiving reply to a <get-schema> expired.", clb_data->session->id);
Michal Vasko77367452021-02-16 16:32:18 +0100397 goto cleanup;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200398 } else if ((msg == NC_MSG_ERROR) || !op) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200399 ERR("Session %u: failed to receive a reply to <get-schema>.", clb_data->session->id);
Michal Vasko77367452021-02-16 16:32:18 +0100400 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100401 }
402
Michal Vasko77367452021-02-16 16:32:18 +0100403 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200404 ERR("Session %u: unexpected data in reply to a <get-schema> RPC.", clb_data->session->id);
Michal Vasko77367452021-02-16 16:32:18 +0100405 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200406 }
Michal Vasko77367452021-02-16 16:32:18 +0100407 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200408 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200409 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100410 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200411 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200412 break;
413 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100414 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200415 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200416 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200417 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200418 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100419 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200420 }
Michal Vasko086311b2016-01-08 09:53:11 +0100421
Radek Krejci488b0702018-08-29 14:47:15 +0200422 if (model_data && !model_data[0]) {
423 /* empty data */
424 free(model_data);
425 model_data = NULL;
426 }
427
Radek Krejcifd5b6682017-06-13 15:52:53 +0200428 /* try to store the model_data into local schema repository */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200429 if (model_data) {
430 *format = LYS_IN_YANG;
431 if (client_opts.schema_searchpath) {
Michal Vasko77367452021-02-16 16:32:18 +0100432 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
433 rev ? rev : "") == -1) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200434 ERRMEM;
Michal Vaskof945da52018-02-15 08:45:13 +0100435 } else {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200436 f = fopen(localfile, "w");
437 if (!f) {
438 WRN("Unable to store \"%s\" as a local copy of schema retrieved via <get-schema> (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200439 localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200440 } else {
441 fputs(model_data, f);
442 fclose(f);
443 }
444 free(localfile);
Michal Vaskof945da52018-02-15 08:45:13 +0100445 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200446 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200447 }
448
Michal Vasko77367452021-02-16 16:32:18 +0100449cleanup:
450 lyd_free_tree(envp);
451 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100452 return model_data;
453}
454
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200455static void
456free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200457{
458 free(data);
459 (void)user_data;
460}
461
Michal Vasko77367452021-02-16 16:32:18 +0100462static LY_ERR
Radek Krejci65ef6d52018-08-16 16:35:02 +0200463retrieve_schema_data(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev,
Michal Vasko77367452021-02-16 16:32:18 +0100464 void *user_data, LYS_INFORMAT *format, const char **module_data,
465 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200466{
467 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
468 unsigned int u, v, match = 1;
469 const char *name = NULL, *rev = NULL;
470 char *model_data = NULL;
471
472 /* get and check the final name and revision of the schema to be retrieved */
473 if (!mod_rev || !mod_rev[0]) {
474 /* newest revision requested - get the newest revision from the list of available modules on server */
475 match = 0;
476 for (u = 0; clb_data->schemas[u].name; ++u) {
477 if (strcmp(mod_name, clb_data->schemas[u].name)) {
478 continue;
479 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200480 if (!match || (strcmp(mod_rev, clb_data->schemas[u].revision) > 0)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200481 mod_rev = clb_data->schemas[u].revision;
482 }
483 match = u + 1;
484 }
485 if (!match) {
Michal Vasko1d2665c2021-02-12 09:28:44 +0100486 /* valid situation if we are retrieving YANG 1.1 schema and have only capabilities for now
487 * (when loading ietf-datastore for ietf-yang-library) */
488 VRB("Session %u: unable to identify revision of the schema \"%s\" from the available server side information.",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200489 clb_data->session->id, mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200490 }
491 }
492 if (submod_name) {
493 name = submod_name;
494 if (sub_rev) {
495 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200496 } else if (match) {
497 if (!clb_data->schemas[match - 1].submodules) {
Michal Vasko1d2665c2021-02-12 09:28:44 +0100498 VRB("Session %u: Unable to identify revision of the requested submodule \"%s\", in schema \"%s\", from the available server side information.",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200499 clb_data->session->id, submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200500 } else {
501 for (v = 0; clb_data->schemas[match - 1].submodules[v].name; ++v) {
502 if (!strcmp(submod_name, clb_data->schemas[match - 1].submodules[v].name)) {
503 rev = sub_rev = clb_data->schemas[match - 1].submodules[v].revision;
504 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200505 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200506 if (!rev) {
507 ERR("Session %u: requested submodule \"%s\" is not known for schema \"%s\" on server side.",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200508 clb_data->session->id, submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100509 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200510 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200511 }
512 }
513 } else {
514 name = mod_name;
515 rev = mod_rev;
516 }
517
Michal Vasko77367452021-02-16 16:32:18 +0100518 VRB("Session %u: retrieving data for schema \"%s\", revision \"%s\".", clb_data->session->id, name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200519
520 if (match) {
521 /* we have enough information to avoid communication with server and try to get
522 * the schema locally */
523
524 /* 1. try to get data locally */
525 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
526
527 /* 2. try to use <get-schema> */
528 if (!model_data && clb_data->has_get_schema) {
529 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
530 }
531 } else {
532 /* we are unsure which revision of the schema we should load, so first try to get
533 * the newest revision from the server via get-schema and only if the server does not
534 * implement get-schema, try to load the newest revision locally. This is imperfect
535 * solution, but there are situation when a client does not know what revision is
536 * actually implemented by the server. */
537
538 /* 1. try to use <get-schema> */
539 if (clb_data->has_get_schema) {
540 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
541 }
542
543 /* 2. try to get data locally */
544 if (!model_data) {
545 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
546 }
547 }
548
549 /* 3. try to use user callback */
550 if (!model_data && clb_data->user_clb) {
551 VRB("Session %u: reading schema via user callback.", clb_data->session->id);
Michal Vasko77367452021-02-16 16:32:18 +0100552 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
553 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200554 }
555
Jan Kundrát35972df2018-09-06 19:00:01 +0200556 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100557 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200558 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200559}
560
Michal Vaskoceae0152018-02-14 16:03:59 +0100561static int
Radek Krejci65ef6d52018-08-16 16:35:02 +0200562nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, struct schema_info *schemas,
Michal Vasko77367452021-02-16 16:32:18 +0100563 ly_module_imp_clb user_clb, void *user_data, int has_get_schema, struct lys_module **mod)
Michal Vaskoceae0152018-02-14 16:03:59 +0100564{
565 int ret = 0;
566 struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200567 const char *module_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200568 LYS_INFORMAT format;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200569
570 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200571 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100572
Radek Krejci65ef6d52018-08-16 16:35:02 +0200573 *mod = NULL;
574 if (revision) {
Michal Vasko77367452021-02-16 16:32:18 +0100575 *mod = ly_ctx_get_module(session->ctx, name, revision);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200576 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100577 if (*mod) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200578 if (!(*mod)->implemented) {
Michal Vaskoceae0152018-02-14 16:03:59 +0100579 /* make the present module implemented */
Michal Vasko77367452021-02-16 16:32:18 +0100580 if (lys_set_implemented(*mod, NULL)) {
Michal Vaskoceae0152018-02-14 16:03:59 +0100581 ERR("Failed to implement model \"%s\".", (*mod)->name);
582 ret = -1;
583 }
584 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200585 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100586 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200587 clb_data.has_get_schema = has_get_schema;
588 clb_data.schemas = schemas;
589 clb_data.session = session;
590 clb_data.user_clb = user_clb;
591 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100592
593 /* clear all the errors and just collect them for now */
594 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200595 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100596
Radek Krejci65ef6d52018-08-16 16:35:02 +0200597 /* get module data */
Michal Vasko77367452021-02-16 16:32:18 +0100598 retrieve_schema_data(name, revision, NULL, NULL, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100599
Radek Krejci65ef6d52018-08-16 16:35:02 +0200600 if (module_data) {
601 /* parse the schema */
602 ly_ctx_set_module_imp_clb(session->ctx, retrieve_schema_data, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100603
Michal Vasko77367452021-02-16 16:32:18 +0100604 lys_parse_mem(session->ctx, module_data, format, (const struct lys_module **)mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200605 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100606 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100607 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100608
Radek Krejci65ef6d52018-08-16 16:35:02 +0200609 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
610 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100611
Michal Vaskodbf7c272018-02-19 09:07:59 +0100612 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200613 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100614 if (!(*mod)) {
615 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100616 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100617 }
618 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200619 } else {
620 /* print only warnings */
621 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
622 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100623 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200624 }
625 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100626 }
627
Michal Vaskodbf7c272018-02-19 09:07:59 +0100628 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100629 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100630 }
631
632 return ret;
633}
634
Radek Krejci65ef6d52018-08-16 16:35:02 +0200635static void
636free_schema_info(struct schema_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200637{
Michal Vasko77367452021-02-16 16:32:18 +0100638 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200639
Radek Krejci65ef6d52018-08-16 16:35:02 +0200640 if (!list) {
641 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200642 }
643
Radek Krejci65ef6d52018-08-16 16:35:02 +0200644 for (u = 0; list[u].name; ++u) {
645 free(list[u].name);
646 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100647 if (list[u].features) {
648 for (v = 0; list[u].features[v]; ++v) {
649 free(list[u].features[v]);
650 }
651 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200652 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200653 if (list[u].submodules) {
654 for (v = 0; list[u].submodules[v].name; ++v) {
655 free(list[u].submodules[v].name);
656 free(list[u].submodules[v].revision);
657 }
658 free(list[u].submodules);
659 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200660 }
661 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200662}
663
Radek Krejci235d8cb2018-08-17 14:04:32 +0200664static int
665build_schema_info_yl(struct nc_session *session, struct schema_info **result)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200666{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200667 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100668 struct lyd_node *op = NULL, *envp = NULL;
669 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200670 NC_MSG_TYPE msg;
671 uint64_t msgid;
Radek Krejcia8dfaea2018-08-17 10:55:09 +0200672 struct ly_set *modules = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100673 uint32_t u, v, submodules_count, feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200674 struct lyd_node *iter, *child;
675 struct lys_module *mod;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200676 int ret = EXIT_SUCCESS;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200677
678 /* get yang-library data from the server */
Radek Krejci261737f2018-08-21 09:22:34 +0200679 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
Michal Vasko77367452021-02-16 16:32:18 +0100680 rpc = nc_rpc_getdata("ietf-datastores:operational", "/ietf-yang-library:*", "false", NULL, 0, 0, 0, 0, 0,
681 NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200682 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100683 rpc = nc_rpc_getdata("ietf-datastores:operational",
684 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", "false", NULL, 0, 0, 0, 0,
685 0, NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200686 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200687 if (!rpc) {
688 goto cleanup;
689 }
690
691 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
692 usleep(1000);
693 }
694 if (msg == NC_MSG_ERROR) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200695 WRN("Session %u: failed to send request for yang-library data.",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200696 session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200697 goto cleanup;
698 }
699
700 do {
Michal Vasko77367452021-02-16 16:32:18 +0100701 lyd_free_tree(envp);
702 lyd_free_tree(op);
703
704 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200705 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200706 if (msg == NC_MSG_WOULDBLOCK) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200707 WRN("Session %u: timeout for receiving reply to a <get> yang-library data expired.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200708 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100709 } else if (msg == NC_MSG_ERROR) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200710 WRN("Session %u: failed to receive a reply to <get> of yang-library data.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200711 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100712 } else if (!op || !lyd_child(op) || strcmp(lyd_child(op)->schema->name, "data")) {
713 WRN("Session %u: unexpected reply without data to a yang-library <get> RPC.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200714 goto cleanup;
715 }
716
Michal Vasko77367452021-02-16 16:32:18 +0100717 data = (struct lyd_node_any *)lyd_child(op);
718 if (data->value_type != LYD_ANYDATA_DATATREE) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200719 WRN("Session %u: unexpected data in reply to a yang-library <get> RPC.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200720 goto cleanup;
721 }
722
Michal Vasko77367452021-02-16 16:32:18 +0100723 if (lyd_find_xpath(data->value.tree, "/ietf-yang-library:modules-state/module", &modules)) {
Radek Krejci2d088832018-08-21 13:40:14 +0200724 WRN("Session %u: no module information in reply to a yang-library <get> RPC.", session->id);
725 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200726 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200727
Michal Vasko77367452021-02-16 16:32:18 +0100728 (*result) = calloc(modules->count + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200729 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200730 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200731 ret = EXIT_FAILURE;
Radek Krejcic9390502018-08-17 10:23:13 +0200732 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200733 }
734
Michal Vasko77367452021-02-16 16:32:18 +0100735 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200736 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100737 feature_count = 0;
738 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
739 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200740 if (iter->schema->module != mod) {
741 /* ignore node from other schemas (augments) */
742 continue;
743 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200744 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200745 /* ignore empty nodes */
746 continue;
747 }
748 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200749 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200750 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200751 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200752 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200753 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200754 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100755 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
756 if (!(*result)[u].features) {
757 ERRMEM;
758 free_schema_info(*result);
759 *result = NULL;
760 ret = EXIT_FAILURE;
761 goto cleanup;
762 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200763 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100764 (*result)[u].features[feature_count + 1] = NULL;
765 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200766 } else if (!strcmp(iter->schema->name, "submodule")) {
767 submodules_count++;
768 }
769 }
770
771 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200772 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
773 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200774 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200775 free_schema_info(*result);
776 *result = NULL;
777 ret = EXIT_FAILURE;
778 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200779 } else {
780 v = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100781 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
782 mod = modules->dnodes[u]->schema->module;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200783 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
Michal Vasko77367452021-02-16 16:32:18 +0100784 LY_LIST_FOR(lyd_child(iter), child) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200785 if (mod != child->schema->module) {
786 continue;
787 } else if (!strcmp(child->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200788 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200789 } else if (!strcmp(child->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200790 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200791 }
792 }
793 }
794 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200795 }
796 }
797 }
798
Radek Krejcifd5b6682017-06-13 15:52:53 +0200799cleanup:
800 nc_rpc_free(rpc);
Michal Vasko77367452021-02-16 16:32:18 +0100801 lyd_free_tree(envp);
802 lyd_free_tree(op);
803 ly_set_free(modules, NULL);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200804
Radek Krejci235d8cb2018-08-17 14:04:32 +0200805 if (session->status != NC_STATUS_RUNNING) {
806 /* something bad heppened, discard the session */
807 ERR("Session %d: invalid session, discarding.", nc_session_get_id(session));
808 ret = EXIT_FAILURE;
809 }
810
811 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200812}
813
Radek Krejci235d8cb2018-08-17 14:04:32 +0200814static int
815build_schema_info_cpblts(char **cpblts, struct schema_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200816{
Michal Vasko77367452021-02-16 16:32:18 +0100817 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200818 char *module_cpblt, *ptr, *ptr2;
819
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200820 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +0200821 (*result) = calloc(u + 1, sizeof **result);
822 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200823 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200824 return EXIT_FAILURE;
Radek Krejcic9390502018-08-17 10:23:13 +0200825 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200826
827 for (u = v = 0; cpblts[u]; ++u) {
828 module_cpblt = strstr(cpblts[u], "module=");
829 /* this capability requires a module */
830 if (!module_cpblt) {
831 continue;
832 }
833
834 /* get module's name */
835 ptr = (char *)module_cpblt + 7;
836 ptr2 = strchr(ptr, '&');
837 if (!ptr2) {
838 ptr2 = ptr + strlen(ptr);
839 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200840 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200841
842 /* get module's revision */
843 ptr = strstr(module_cpblt, "revision=");
844 if (ptr) {
845 ptr += 9;
846 ptr2 = strchr(ptr, '&');
847 if (!ptr2) {
848 ptr2 = ptr + strlen(ptr);
849 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200850 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200851 }
852
853 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200854 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200855
856 /* get module's features */
857 ptr = strstr(module_cpblt, "features=");
858 if (ptr) {
859 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +0100860 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200861 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
862 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +0100863 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
864 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
865 (*result)[v].features[feature_count + 1] = NULL;
866 ++feature_count;
867
Radek Krejci65ef6d52018-08-16 16:35:02 +0200868 ptr2 = ptr + 1;
869 }
870 }
871 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +0100872 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
873 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
874 (*result)[v].features[feature_count + 1] = NULL;
875 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200876 }
877 ++v;
878 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200879
880 return EXIT_SUCCESS;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200881}
882
883static int
Michal Vasko77367452021-02-16 16:32:18 +0100884nc_ctx_fill(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb, void *user_data,
885 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200886{
887 int ret = EXIT_FAILURE;
Michal Vasko77367452021-02-16 16:32:18 +0100888 struct lys_module *mod;
889 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200890
891 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +0200892 /* skip import-only modules */
893 if (!modules[u].implemented) {
894 continue;
895 }
896
Radek Krejci65ef6d52018-08-16 16:35:02 +0200897 /* we can continue even if it fails */
898 nc_ctx_load_module(session, modules[u].name, modules[u].revision, modules, user_clb, user_data, has_get_schema, &mod);
899
900 if (!mod) {
901 if (session->status != NC_STATUS_RUNNING) {
902 /* something bad heppened, discard the session */
903 ERR("Session %d: invalid session, discarding.", nc_session_get_id(session));
904 goto cleanup;
905 }
906
907 /* all loading ways failed, the schema will be ignored in the received data */
908 WRN("Failed to load schema \"%s@%s\".", modules[u].name, modules[u].revision ? modules[u].revision : "<latest>");
909 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
910 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100911 /* set the features */
912 lys_set_implemented(mod, (const char **)modules[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200913 }
Michal Vasko60f66602017-10-17 13:52:18 +0200914 }
915
Michal Vasko77367452021-02-16 16:32:18 +0100916 /* success */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200917 ret = EXIT_SUCCESS;
918
919cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +0200920 return ret;
921}
922
Radek Krejci65ef6d52018-08-16 16:35:02 +0200923static int
Michal Vasko77367452021-02-16 16:32:18 +0100924nc_ctx_fill_ietf_netconf(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb,
925 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200926{
Michal Vasko77367452021-02-16 16:32:18 +0100927 uint32_t u;
928 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200929
Michal Vasko77367452021-02-16 16:32:18 +0100930 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200931 if (!ietfnc) {
932 nc_ctx_load_module(session, "ietf-netconf", NULL, modules, user_clb, user_data, has_get_schema, &ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200933 if (!ietfnc) {
Michal Vasko77367452021-02-16 16:32:18 +0100934 lys_parse_mem(session->ctx, ietf_netconf_2013_09_29_yang, LYS_IN_YANG, (const struct lys_module **)&ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200935 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200936 }
937 if (!ietfnc) {
938 ERR("Loading base NETCONF schema failed.");
939 return 1;
940 }
941
942 /* set supported capabilities from ietf-netconf */
943 for (u = 0; modules[u].name; ++u) {
944 if (strcmp(modules[u].name, "ietf-netconf") || !modules[u].implemented) {
945 continue;
946 }
947
Michal Vasko77367452021-02-16 16:32:18 +0100948 lys_set_implemented(ietfnc, (const char **)modules[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200949 }
950
951 return 0;
952}
953
Michal Vasko086311b2016-01-08 09:53:11 +0100954int
955nc_ctx_check_and_fill(struct nc_session *session)
956{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200957 int i, get_schema_support = 0, yanglib_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +0100958 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100959 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100960 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +0200961 char *revision;
Radek Krejcib056ff82018-08-20 15:58:39 +0200962 struct schema_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100963
Michal Vasko2e6defd2016-10-07 15:48:15 +0200964 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +0100965
Radek Krejci65ef6d52018-08-16 16:35:02 +0200966 /* 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 +0200967 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200968
Radek Krejci65ef6d52018-08-16 16:35:02 +0200969 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
970 * schemas only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +0100971 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200972
973 /* our callback is set later with appropriate data */
974 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
975
976 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200977 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200978 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +0200979 get_schema_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200980 if (yanglib_support) {
981 break;
982 }
Michal Vaskof0fba4e2020-02-14 17:15:31 +0100983 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +0200984 yanglib_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200985 if (get_schema_support) {
986 break;
987 }
Michal Vasko086311b2016-01-08 09:53:11 +0100988 }
989 }
Michal Vasko31dd4c52020-04-17 10:29:44 +0200990 if (get_schema_support) {
991 VRB("Session %u: capability for <get-schema> support found.", session->id);
992 } else {
993 VRB("Session %u: capability for <get-schema> support not found.", session->id);
994 }
995 if (yanglib_support) {
996 VRB("Session %u: capability for yang-library support found.", session->id);
997 } else {
998 VRB("Session %u: capability for yang-library support not found.", session->id);
999 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001000
1001 /* get information about server's schemas from capabilities list until we will have yang-library */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001002 if (build_schema_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
1003 ERR("Session %u: unable to get server's schema information from the <hello>'s capabilities.", session->id);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001004 goto cleanup;
1005 }
1006
Michal Vasko086311b2016-01-08 09:53:11 +01001007 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001008 if (get_schema_support && lys_parse_mem(session->ctx, ietf_netconf_monitoring_2010_10_04_yang, LYS_IN_YANG, NULL)) {
Michal Vasko8a4e1462020-05-07 11:32:31 +02001009 WRN("Session %u: loading NETCONF monitoring schema failed, cannot use <get-schema>.", session->id);
1010 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001011 }
1012
1013 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001014 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001015 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001016 }
1017
Radek Krejci65ef6d52018-08-16 16:35:02 +02001018 /* get correct version of ietf-yang-library into context */
1019 if (yanglib_support) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001020 /* use get schema to get server's ietf-yang-library */
1021 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1022 if (!revision) {
Michal Vasko77367452021-02-16 16:32:18 +01001023 WRN("Session %u: loading NETCONF ietf-yang-library schema failed, missing revision in NETCONF <hello> message.",
1024 session->id);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001025 WRN("Session %u: unable to automatically use <get-schema>.", session->id);
Radek Krejcib1d250e2018-04-12 13:54:18 +02001026 yanglib_support = 0;
1027 } else {
1028 revision = strndup(&revision[9], 10);
Michal Vasko0d877f92020-04-02 13:52:43 +02001029 if (nc_ctx_load_module(session, "ietf-yang-library", revision, server_modules, old_clb, old_data,
1030 get_schema_support, &mod)) {
Michal Vasko77367452021-02-16 16:32:18 +01001031 WRN("Session %u: loading NETCONF ietf-yang-library schema failed, unable to use it to learn all "
1032 "the supported modules.", session->id);
Radek Krejcib1d250e2018-04-12 13:54:18 +02001033 yanglib_support = 0;
1034 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001035 if (strcmp(revision, "2019-01-04") >= 0) {
1036 /* we also need ietf-datastores to be implemented */
1037 if (nc_ctx_load_module(session, "ietf-datastores", NULL, server_modules, old_clb, old_data,
1038 get_schema_support, &mod)) {
Michal Vasko77367452021-02-16 16:32:18 +01001039 WRN("Session %u: loading NETCONF ietf-datastores schema failed, unable to use yang-library "
1040 "to learn all the supported modules.", session->id);
Michal Vasko0d877f92020-04-02 13:52:43 +02001041 yanglib_support = 0;
1042 }
1043 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001044 free(revision);
Michal Vasko77367452021-02-16 16:32:18 +01001045
1046 /* ietf-netconf-nmda is needed to issue get-data */
1047 if (nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, server_modules, old_clb, old_data,
1048 get_schema_support, &mod)) {
1049 WRN("Session %u: loading NETCONF ietf-netconf-nmda schema failed, unable to use get-data to retrieve "
1050 "yang-library data.", session->id);
1051 yanglib_support = 0;
1052 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001053 }
1054 }
1055
Radek Krejci65ef6d52018-08-16 16:35:02 +02001056 /* prepare structured information about server's schemas */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001057 if (yanglib_support) {
Radek Krejcif0633792018-08-20 15:12:09 +02001058 if (build_schema_info_yl(session, &sm)) {
1059 goto cleanup;
1060 } else if (!sm) {
1061 VRB("Session %u: trying to use capabilities instead of ietf-yang-library data.", session->id);
1062 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001063 /* prefer yang-library information, currently we have it from capabilities used for getting correct
1064 * yang-library schema */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001065 free_schema_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001066 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001067 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001068 }
Michal Vaskoef578332016-01-25 13:20:09 +01001069
Radek Krejci65ef6d52018-08-16 16:35:02 +02001070 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1071 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001072 }
1073
Radek Krejcifd5b6682017-06-13 15:52:53 +02001074 /* succsess */
1075 ret = 0;
1076
Michal Vaskoeee99412016-11-21 10:19:43 +01001077 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko77367452021-02-16 16:32:18 +01001078 WRN("Session %u: some models failed to be loaded, any data from these models (and any other unknown) will "
1079 "be ignored.", session->id);
Michal Vaskoef578332016-01-25 13:20:09 +01001080 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001081
1082cleanup:
Radek Krejci65ef6d52018-08-16 16:35:02 +02001083 free_schema_info(server_modules);
1084
Radek Krejcifd5b6682017-06-13 15:52:53 +02001085 /* set user callback back */
1086 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001087 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001088
Michal Vaskoef578332016-01-25 13:20:09 +01001089 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001090}
1091
1092API struct nc_session *
1093nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1094{
Michal Vaskod083db62016-01-19 10:31:29 +01001095 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001096
Michal Vasko45e53ae2016-04-07 11:46:03 +02001097 if (fdin < 0) {
1098 ERRARG("fdin");
1099 return NULL;
1100 } else if (fdout < 0) {
1101 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001102 return NULL;
1103 }
1104
1105 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001106 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001107 if (!session) {
1108 ERRMEM;
1109 return NULL;
1110 }
1111 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001112
1113 /* transport specific data */
1114 session->ti_type = NC_TI_FD;
1115 session->ti.fd.in = fdin;
1116 session->ti.fd.out = fdout;
1117
Robin Jarry4e38e292019-10-15 17:14:14 +02001118 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1119 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001120 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001121 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001122
1123 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001124 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001125 goto fail;
1126 }
1127 session->status = NC_STATUS_RUNNING;
1128
Michal Vaskoef578332016-01-25 13:20:09 +01001129 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001130 goto fail;
1131 }
1132
1133 return session;
1134
1135fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001136 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001137 return NULL;
1138}
1139
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001140API struct nc_session *
1141nc_connect_unix(const char *address, struct ly_ctx *ctx)
1142{
1143 struct nc_session *session = NULL;
1144 struct sockaddr_un sun;
1145 const struct passwd *pw;
1146 char *username;
1147 int sock = -1;
1148
1149 if (address == NULL) {
1150 ERRARG("address");
1151 return NULL;
1152 }
1153
1154 pw = getpwuid(geteuid());
1155 if (pw == NULL) {
1156 ERR("Failed to find username for euid=%u.\n", geteuid());
1157 goto fail;
1158 }
1159
1160 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1161 if (sock < 0) {
1162 ERR("Failed to create socket (%s).", strerror(errno));
1163 goto fail;
1164 }
1165
1166 memset(&sun, 0, sizeof(sun));
1167 sun.sun_family = AF_UNIX;
1168 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1169
1170 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
1171 ERR("cannot connect to sock server %s (%s)",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001172 address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001173 goto fail;
1174 }
1175
1176 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
1177 ERR("Fcntl failed (%s).", strerror(errno));
1178 goto fail;
1179 }
1180
1181 /* prepare session structure */
1182 session = nc_new_session(NC_CLIENT, 0);
1183 if (!session) {
1184 ERRMEM;
1185 goto fail;
1186 }
1187 session->status = NC_STATUS_STARTING;
1188
1189 /* transport specific data */
1190 session->ti_type = NC_TI_UNIX;
1191 session->ti.unixsock.sock = sock;
1192 sock = -1; /* do not close sock in fail label anymore */
1193
Robin Jarry4e38e292019-10-15 17:14:14 +02001194 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1195 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001196 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001197 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001198
Michal Vasko77367452021-02-16 16:32:18 +01001199 lydict_insert(ctx, address, 0, &session->path);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001200
1201 username = strdup(pw->pw_name);
1202 if (username == NULL) {
1203 ERRMEM;
1204 goto fail;
1205 }
Michal Vasko77367452021-02-16 16:32:18 +01001206 lydict_insert_zc(ctx, username, &session->username);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001207
1208 /* NETCONF handshake */
1209 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1210 goto fail;
1211 }
1212 session->status = NC_STATUS_RUNNING;
1213
1214 if (nc_ctx_check_and_fill(session) == -1) {
1215 goto fail;
1216 }
1217
1218 return session;
1219
1220fail:
1221 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001222 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001223 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001224 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001225 return NULL;
1226}
1227
Frank Rimpler9f838b02018-07-25 06:44:03 +00001228/*
1229 Helper for a non-blocking connect (which is required because of the locking
1230 concept for e.g. call home settings). For more details see nc_sock_connect().
1231 */
1232static int
Michal Vasko5e8d0192019-06-24 19:19:49 +02001233_non_blocking_connect(int timeout, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001234{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001235 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001236 int sock = -1;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001237 fd_set wset;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001238 struct timeval ts;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001239 socklen_t len = sizeof(int);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001240 struct in_addr *addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001241 uint16_t port;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001242 char str[INET6_ADDRSTRLEN];
Michal Vasko086311b2016-01-08 09:53:11 +01001243
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001244 if (sock_pending && (*sock_pending != -1)) {
1245 VRB("Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001246 sock = *sock_pending;
1247 } else {
1248 assert(res);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001249 if (res->ai_family == AF_INET6) {
1250 addr = (struct in_addr *) &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001251 port = ntohs(((struct sockaddr_in6 *)res->ai_addr)->sin6_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001252 } else {
1253 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001254 port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001255 }
1256 if (!inet_ntop(res->ai_family, addr, str, res->ai_addrlen)) {
1257 WRN("inet_ntop() failed (%s).", strerror(errno));
1258 } else {
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001259 VRB("Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001260 }
1261
1262 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001263 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1264 if (sock == -1) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001265 ERR("Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001266 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001267 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001268 /* make the socket non-blocking */
1269 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001270 ERR("fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001271 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001272 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001273 /* non-blocking connect! */
1274 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1275 if (errno != EINPROGRESS) {
1276 /* network connection failed, try another resource */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001277 ERR("connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001278 goto cleanup;
1279 }
1280 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001281 }
1282 ts.tv_sec = timeout;
1283 ts.tv_usec = 0;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001284
Frank Rimpler9f838b02018-07-25 06:44:03 +00001285 FD_ZERO(&wset);
1286 FD_SET(sock, &wset);
1287
1288 if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout != -1) ? &ts : NULL)) < 0) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001289 ERR("select() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001290 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001291 }
1292
Michal Vasko5e8d0192019-06-24 19:19:49 +02001293 if (ret == 0) {
1294 /* there was a timeout */
1295 VRB("Timed out after %ds (%s).", timeout, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001296 if (sock_pending) {
1297 /* no sock-close, we'll try it again */
1298 *sock_pending = sock;
1299 } else {
1300 close(sock);
1301 }
1302 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001303 }
Radek Krejci782041a2018-08-20 10:09:45 +02001304
1305 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001306 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001307 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001308 ERR("getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001309 goto cleanup;
1310 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001311 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001312 /* network connection failed, try another resource */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001313 VRB("getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001314 errno = error;
1315 goto cleanup;
1316 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001317
1318 /* enable keep-alive */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001319 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001320 goto cleanup;
1321 }
1322
Michal Vasko086311b2016-01-08 09:53:11 +01001323 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001324
Frank Rimpler9f838b02018-07-25 06:44:03 +00001325cleanup:
1326 if (sock_pending) {
1327 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001328 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001329 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001330 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001331}
1332
Frank Rimpler9f838b02018-07-25 06:44:03 +00001333/* A given timeout value limits the time how long the function blocks. If it has to block
1334 only for some seconds, a socket connection might not yet have been fully established.
1335 Therefore the active (pending) socket will be stored in *sock_pending, but the return
1336 value will be -1. In such a case a subsequent invokation is required, by providing the
1337 stored sock_pending, again.
1338 In general, if this function returns -1, when a timeout has been given, this function
1339 has to be invoked, until it returns a valid socket.
1340 */
1341int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001342nc_sock_connect(const char *host, uint16_t port, int timeout, struct nc_keepalives *ka, int *sock_pending, char **ip_host)
Frank Rimpler9f838b02018-07-25 06:44:03 +00001343{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001344 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001345 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001346 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001347 char *buf, port_s[6]; /* length of string representation of short int */
Michal Vasko66032bc2019-01-22 15:03:12 +01001348 void *addr;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001349
Michal Vasko66032bc2019-01-22 15:03:12 +01001350 DBG("nc_sock_connect(%s, %u, %d, %d)", host, port, timeout, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001351
1352 /* no pending socket */
1353 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001354 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001355 snprintf(port_s, 6, "%u", port);
1356 memset(&hints, 0, sizeof hints);
1357 hints.ai_family = AF_UNSPEC;
1358 hints.ai_socktype = SOCK_STREAM;
1359 hints.ai_protocol = IPPROTO_TCP;
1360 i = getaddrinfo(host, port_s, &hints, &res_list);
1361 if (i != 0) {
1362 ERR("Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001363 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001364 }
1365
1366 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001367 sock = _non_blocking_connect(timeout, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001368 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001369 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001370 /* try the next resource */
1371 continue;
1372 } else {
1373 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001374 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001375 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001376 }
1377 VRB("Successfully connected to %s:%s over %s.", host, port_s, (res->ai_family == AF_INET6) ? "IPv6" : "IPv4");
Michal Vasko83ad17e2019-01-30 10:11:37 +01001378
1379 opt = 1;
1380 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
1381 ERR("Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001382 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001383 }
1384
Michal Vasko66032bc2019-01-22 15:03:12 +01001385 if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) {
1386 buf = malloc(INET6_ADDRSTRLEN);
1387 if (!buf) {
1388 ERRMEM;
Michal Vaskocb846632019-12-13 15:12:45 +01001389 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001390 }
1391 if (res->ai_family == AF_INET) {
1392 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1393 } else {
1394 addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1395 }
1396 if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) {
1397 ERR("Converting host to IP address failed (%s).", strerror(errno));
1398 free(buf);
Michal Vaskocb846632019-12-13 15:12:45 +01001399 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001400 }
1401
1402 *ip_host = buf;
1403 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001404 break;
1405 }
1406 freeaddrinfo(res_list);
1407
1408 } else {
1409 /* try to get a connection with the pending socket */
1410 assert(sock_pending);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001411 sock = _non_blocking_connect(timeout, sock_pending, NULL, ka);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001412 }
1413
1414 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001415
1416error:
Michal Vasko0be85692021-03-02 08:04:57 +01001417 if (res_list) {
1418 freeaddrinfo(res_list);
1419 }
Michal Vaskocb846632019-12-13 15:12:45 +01001420 if (sock != -1) {
1421 close(sock);
1422 }
1423 if (sock_pending) {
1424 *sock_pending = -1;
1425 }
1426 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001427}
1428
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001429#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001430
Michal Vasko3031aae2016-01-27 16:07:18 +01001431int
1432nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1433{
1434 int sock;
1435
Michal Vasko45e53ae2016-04-07 11:46:03 +02001436 if (!address) {
1437 ERRARG("address");
1438 return -1;
1439 } else if (!port) {
1440 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001441 return -1;
1442 }
1443
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001444 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001445 if (sock == -1) {
1446 return -1;
1447 }
1448
1449 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001450 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1451 if (!client_opts.ch_binds) {
1452 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001453 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001454 return -1;
1455 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001456
Michal Vasko2e6defd2016-10-07 15:48:15 +02001457 client_opts.ch_bind_ti = nc_realloc(client_opts.ch_bind_ti, client_opts.ch_bind_count * sizeof *client_opts.ch_bind_ti);
1458 if (!client_opts.ch_bind_ti) {
1459 ERRMEM;
1460 close(sock);
1461 return -1;
1462 }
1463 client_opts.ch_bind_ti[client_opts.ch_bind_count - 1] = ti;
1464
Michal Vasko3031aae2016-01-27 16:07:18 +01001465 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001466 if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) {
1467 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001468 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001469 return -1;
1470 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001471 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1472 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001473 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001474
1475 return 0;
1476}
1477
1478int
1479nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1480{
1481 uint32_t i;
1482 int ret = -1;
1483
1484 if (!address && !port && !ti) {
1485 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1486 close(client_opts.ch_binds[i].sock);
1487 free((char *)client_opts.ch_binds[i].address);
1488
1489 ret = 0;
1490 }
1491 free(client_opts.ch_binds);
1492 client_opts.ch_binds = NULL;
1493 client_opts.ch_bind_count = 0;
1494 } else {
1495 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001496 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1497 (!port || (client_opts.ch_binds[i].port == port)) &&
1498 (!ti || (client_opts.ch_bind_ti[i] == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001499 close(client_opts.ch_binds[i].sock);
1500 free((char *)client_opts.ch_binds[i].address);
1501
1502 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001503 if (!client_opts.ch_bind_count) {
1504 free(client_opts.ch_binds);
1505 client_opts.ch_binds = NULL;
1506 } else if (i < client_opts.ch_bind_count) {
1507 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds);
1508 client_opts.ch_bind_ti[i] = client_opts.ch_bind_ti[client_opts.ch_bind_count];
1509 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001510
1511 ret = 0;
1512 }
1513 }
1514 }
1515
1516 return ret;
1517}
1518
1519API int
1520nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1521{
1522 int sock;
1523 char *host = NULL;
1524 uint16_t port, idx;
1525
Michal Vasko45e53ae2016-04-07 11:46:03 +02001526 if (!client_opts.ch_binds) {
1527 ERRINIT;
1528 return -1;
1529 } else if (!session) {
1530 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001531 return -1;
1532 }
1533
1534 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
1535
Michal Vasko50456e82016-02-02 12:16:08 +01001536 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001537 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001538 return sock;
1539 }
1540
Radek Krejci53691be2016-02-22 13:58:37 +01001541#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001542 if (client_opts.ch_bind_ti[idx] == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001543 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001544 } else
1545#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001546#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001547 if (client_opts.ch_bind_ti[idx] == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001548 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001549 } else
1550#endif
1551 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001552 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001553 *session = NULL;
1554 }
1555
1556 free(host);
1557
1558 if (!(*session)) {
1559 return -1;
1560 }
1561
1562 return 1;
1563}
1564
Radek Krejci53691be2016-02-22 13:58:37 +01001565#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001566
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001567API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001568nc_session_get_cpblts(const struct nc_session *session)
1569{
1570 if (!session) {
1571 ERRARG("session");
1572 return NULL;
1573 }
1574
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001575 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001576}
1577
1578API const char *
1579nc_session_cpblt(const struct nc_session *session, const char *capab)
1580{
1581 int i, len;
1582
1583 if (!session) {
1584 ERRARG("session");
1585 return NULL;
1586 } else if (!capab) {
1587 ERRARG("capab");
1588 return NULL;
1589 }
1590
1591 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001592 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1593 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1594 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001595 }
1596 }
1597
1598 return NULL;
1599}
1600
Michal Vasko9cd26a82016-05-31 08:58:48 +02001601API int
1602nc_session_ntf_thread_running(const struct nc_session *session)
1603{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001604 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001605 ERRARG("session");
1606 return 0;
1607 }
1608
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02001609 return ATOMIC_LOAD(session->opts.client.ntf_tid) ? 1 : 0;
Michal Vasko9cd26a82016-05-31 08:58:48 +02001610}
1611
Michal Vaskob7558c52016-02-26 15:04:19 +01001612API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001613nc_client_init(void)
1614{
1615 nc_init();
1616}
1617
1618API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001619nc_client_destroy(void)
1620{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001621 nc_client_set_schema_searchpath(NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001622#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001623 nc_client_ch_del_bind(NULL, 0, 0);
1624#endif
1625#ifdef NC_ENABLED_SSH
1626 nc_client_ssh_destroy_opts();
1627#endif
1628#ifdef NC_ENABLED_TLS
1629 nc_client_tls_destroy_opts();
1630#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001631 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001632}
1633
Michal Vasko77367452021-02-16 16:32:18 +01001634static NC_MSG_TYPE
1635recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001636{
Michal Vasko77367452021-02-16 16:32:18 +01001637 char *ptr;
1638 struct lyd_attr *attr;
1639 uint64_t cur_msgid;
1640
1641 assert(envp && !envp->schema);
1642
1643 /* find the message-id attribute */
1644 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1645 if (!strcmp(attr->name.name, "message-id")) {
1646 break;
1647 }
1648 }
1649
1650 if (!attr) {
1651 ERR("Session %u: received a <rpc-reply> without a message-id.", session->id);
1652 return NC_MSG_REPLY_ERR_MSGID;
1653 }
1654
1655 cur_msgid = strtoul(attr->value, &ptr, 10);
1656 if (cur_msgid != msgid) {
1657 ERR("Session %u: received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1658 session->id, cur_msgid, msgid);
1659 return NC_MSG_REPLY_ERR_MSGID;
1660 }
1661
1662 return NC_MSG_REPLY;
1663}
1664
1665static NC_MSG_TYPE
1666recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
1667{
1668 int r;
1669 LY_ERR lyrc;
1670 struct ly_in *msg = NULL;
1671 struct nc_msg_cont *cont, **cont_ptr;
1672 NC_MSG_TYPE ret = NC_MSG_ERROR;
1673
1674 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
1675
1676 *envp = NULL;
1677
1678 /* try to get rpc-reply from the session's queue */
1679 while (session->opts.client.replies) {
1680 cont = session->opts.client.replies;
1681 session->opts.client.replies = cont->next;
1682
1683 msg = cont->msg;
1684 free(cont);
1685
1686 /* parse */
1687 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
1688 if (!lyrc) {
1689 ret = recv_reply_check_msgid(session, *envp, msgid);
1690 goto cleanup;
1691 } else if (lyrc != LY_ENOT) {
1692 lyd_free_tree(*envp);
1693 *envp = NULL;
1694 ERR("Session %u: received an invalid message (%s).", session->id, ly_errmsg(LYD_CTX(op)));
1695 goto cleanup;
1696 } else {
1697 /* it was not a notification so it is nothing known */
1698 ERR("Session %u: received an unexpected message.", session->id);
1699 }
1700
1701 /* try the next message */
1702 ly_in_free(msg, 1);
1703 msg = NULL;
1704 }
1705
1706 /* read message from wire */
1707 r = nc_read_msg_poll_io(session, timeout, &msg);
1708 if (!r) {
1709 ret = NC_MSG_WOULDBLOCK;
1710 goto cleanup;
1711 } else if (r == -1) {
1712 goto cleanup;
1713 }
1714
1715 /* parse */
1716 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
1717 if (!lyrc) {
1718 ret = recv_reply_check_msgid(session, *envp, msgid);
1719 goto cleanup;
1720 } else if (lyrc != LY_ENOT) {
1721 lyd_free_tree(*envp);
1722 *envp = NULL;
1723 ERR("Session %u: received an invalid message (%s).", session->id, ly_errmsg(LYD_CTX(op)));
1724 goto cleanup;
1725 }
1726
Michal Vaskoc5ea6382021-03-15 16:24:08 +01001727 /* assume a notification, reset and store it */
1728 ly_in_reset(msg);
Michal Vasko77367452021-02-16 16:32:18 +01001729 cont_ptr = &session->opts.client.notifs;
1730 while (*cont_ptr) {
1731 cont_ptr = &((*cont_ptr)->next);
1732 }
1733 *cont_ptr = malloc(sizeof **cont_ptr);
1734 if (!*cont_ptr) {
1735 ERRMEM;
1736 goto cleanup;
1737 }
1738 (*cont_ptr)->msg = msg;
Michal Vaskoc5ea6382021-03-15 16:24:08 +01001739 msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001740 (*cont_ptr)->next = NULL;
1741
1742 ret = NC_MSG_NOTIF;
1743
1744cleanup:
1745 ly_in_free(msg, 1);
1746 return ret;
1747}
1748
1749static int
1750recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
1751{
1752 LY_ERR lyrc;
1753 struct nc_rpc_act_generic *rpc_gen;
1754 struct ly_in *in;
1755 struct lyd_node *tree, *op2;
1756 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01001757 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001758
1759 switch (rpc->type) {
1760 case NC_RPC_ACT_GENERIC:
1761 rpc_gen = (struct nc_rpc_act_generic *)rpc;
1762 if (rpc_gen->has_data) {
1763 tree = rpc_gen->content.data;
1764
1765 /* find the operation node */
1766 lyrc = LY_EINVAL;
1767 LYD_TREE_DFS_BEGIN(tree, op2) {
1768 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
1769 lyrc = lyd_dup_single(op2, NULL, 0, op);
1770 break;
1771 }
1772 LYD_TREE_DFS_END(tree, op2);
1773 }
1774 } else {
1775 ly_in_new_memory(rpc_gen->content.xml_str, &in);
1776 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
1777 ly_in_free(in, 0);
1778 if (lyrc) {
1779 return -1;
1780 }
1781
1782 /* we want just the operation node */
1783 lyrc = lyd_dup_single(op2, NULL, 0, op);
1784
1785 lyd_free_tree(tree);
1786 }
1787 break;
1788 case NC_RPC_GETCONFIG:
1789 module_name = "ietf-netconf";
1790 rpc_name = "get-config";
1791 break;
1792 case NC_RPC_EDIT:
1793 module_name = "ietf-netconf";
1794 rpc_name = "edit-config";
1795 break;
1796 case NC_RPC_COPY:
1797 module_name = "ietf-netconf";
1798 rpc_name = "copy-config";
1799 break;
1800 case NC_RPC_DELETE:
1801 module_name = "ietf-netconf";
1802 rpc_name = "delete-config";
1803 break;
1804 case NC_RPC_LOCK:
1805 module_name = "ietf-netconf";
1806 rpc_name = "lock";
1807 break;
1808 case NC_RPC_UNLOCK:
1809 module_name = "ietf-netconf";
1810 rpc_name = "unlock";
1811 break;
1812 case NC_RPC_GET:
1813 module_name = "ietf-netconf";
1814 rpc_name = "get";
1815 break;
1816 case NC_RPC_KILL:
1817 module_name = "ietf-netconf";
1818 rpc_name = "kill-session";
1819 break;
1820 case NC_RPC_COMMIT:
1821 module_name = "ietf-netconf";
1822 rpc_name = "commit";
1823 break;
1824 case NC_RPC_DISCARD:
1825 module_name = "ietf-netconf";
1826 rpc_name = "discard-changes";
1827 break;
1828 case NC_RPC_CANCEL:
1829 module_name = "ietf-netconf";
1830 rpc_name = "cancel-commit";
1831 break;
1832 case NC_RPC_VALIDATE:
1833 module_name = "ietf-netconf";
1834 rpc_name = "validate";
1835 break;
1836 case NC_RPC_GETSCHEMA:
1837 module_name = "ietf-netconf-monitoring";
1838 rpc_name = "get-schema";
1839 break;
1840 case NC_RPC_SUBSCRIBE:
1841 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02001842 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01001843 break;
1844 case NC_RPC_GETDATA:
1845 module_name = "ietf-netconf-nmda";
1846 rpc_name = "get-data";
1847 break;
1848 case NC_RPC_EDITDATA:
1849 module_name = "ietf-netconf-nmda";
1850 rpc_name = "edit-data";
1851 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01001852 case NC_RPC_ESTABLISHSUB:
1853 module_name = "ietf-subscribed-notifications";
1854 rpc_name = "establish-subscription";
1855 break;
1856 case NC_RPC_MODIFYSUB:
1857 module_name = "ietf-subscribed-notifications";
1858 rpc_name = "modify-subscription";
1859 break;
1860 case NC_RPC_DELETESUB:
1861 module_name = "ietf-subscribed-notifications";
1862 rpc_name = "delete-subscription";
1863 break;
1864 case NC_RPC_KILLSUB:
1865 module_name = "ietf-subscribed-notifications";
1866 rpc_name = "kill-subscription";
1867 break;
Michal Vasko305faca2021-03-25 09:16:02 +01001868 case NC_RPC_ESTABLISHPUSH:
1869 module_name = "ietf-subscribed-notifications";
1870 rpc_name = "establish-subscription";
1871 module_check = "ietf-yang-push";
1872 break;
1873 case NC_RPC_MODIFYPUSH:
1874 module_name = "ietf-subscribed-notifications";
1875 rpc_name = "modify-subscription";
1876 module_check = "ietf-yang-push";
1877 break;
1878 case NC_RPC_RESYNCSUB:
1879 module_name = "ietf-yang-push";
1880 rpc_name = "resync-subscription";
1881 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01001882 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01001883 lyrc = LY_EINT;
1884 break;
1885 }
1886
1887 if (module_name && rpc_name) {
1888 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
1889 if (!mod) {
1890 ERR("Session %u: missing \"%s\" schema in the context.", session->id, module_name);
1891 return -1;
1892 }
1893
1894 /* create the operation node */
1895 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
1896 }
Michal Vasko305faca2021-03-25 09:16:02 +01001897 if (module_check) {
1898 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
1899 ERR("Session %u: missing \"%s\" schema in the context.", session->id, module_check);
1900 return -1;
1901 }
1902 }
Michal Vasko77367452021-02-16 16:32:18 +01001903
1904 if (lyrc) {
1905 return -1;
1906 }
1907 return 0;
1908}
1909
1910API NC_MSG_TYPE
1911nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
1912 struct lyd_node **op)
1913{
1914 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001915
Michal Vasko45e53ae2016-04-07 11:46:03 +02001916 if (!session) {
1917 ERRARG("session");
1918 return NC_MSG_ERROR;
1919 } else if (!rpc) {
1920 ERRARG("rpc");
1921 return NC_MSG_ERROR;
Michal Vasko5a2c7162020-01-30 11:24:17 +01001922 } else if (!msgid) {
1923 ERRARG("msgid");
1924 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001925 } else if (!envp) {
1926 ERRARG("envp");
Michal Vasko45e53ae2016-04-07 11:46:03 +02001927 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001928 } else if (!op) {
1929 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01001930 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001931 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001932 ERR("Session %u: invalid session to receive RPC replies.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001933 return NC_MSG_ERROR;
1934 }
Michal Vasko77367452021-02-16 16:32:18 +01001935
1936 /* get a duplicate of the RPC node to append reply to */
1937 if (recv_reply_dup_rpc(session, rpc, op)) {
1938 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01001939 }
Michal Vasko086311b2016-01-08 09:53:11 +01001940
Michal Vasko77367452021-02-16 16:32:18 +01001941 /* receive a reply */
1942 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01001943
Michal Vasko77367452021-02-16 16:32:18 +01001944 /* do not return the RPC copy on error or if the reply includes no data */
1945 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
1946 lyd_free_tree(*op);
1947 *op = NULL;
1948 }
1949 return ret;
1950}
1951
1952static NC_MSG_TYPE
1953recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
1954{
1955 int r;
1956 LY_ERR lyrc;
1957 struct ly_in *msg = NULL;
1958 struct nc_msg_cont *cont, **cont_ptr;
1959 NC_MSG_TYPE ret = NC_MSG_ERROR;
1960
1961 *op = NULL;
1962 *envp = NULL;
1963
1964 /* try to get notification from the session's queue */
1965 while (session->opts.client.notifs) {
1966 cont = session->opts.client.notifs;
1967 session->opts.client.notifs = cont->next;
1968
1969 msg = cont->msg;
1970 free(cont);
1971
1972 /* parse */
1973 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
1974 if (!lyrc) {
1975 ret = NC_MSG_NOTIF;
1976 goto cleanup;
1977 } else if (lyrc != LY_ENOT) {
1978 lyd_free_tree(*envp);
1979 *envp = NULL;
1980 ERR("Session %u: received an invalid message (%s).", session->id, ly_errmsg(session->ctx));
1981 goto cleanup;
1982 } else {
1983 /* it was not a rpc-reply so it is nothing known */
1984 ERR("Session %u: received an unexpected message.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001985 }
Michal Vasko086311b2016-01-08 09:53:11 +01001986
Michal Vasko77367452021-02-16 16:32:18 +01001987 /* try the next message */
1988 ly_in_free(msg, 1);
1989 msg = NULL;
1990 }
1991
1992 /* read message from wire */
1993 r = nc_read_msg_poll_io(session, timeout, &msg);
1994 if (!r) {
1995 ret = NC_MSG_WOULDBLOCK;
1996 goto cleanup;
1997 } else if (r == -1) {
1998 goto cleanup;
1999 }
2000
2001 /* parse */
2002 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2003 if (!lyrc) {
2004 ret = NC_MSG_NOTIF;
2005 goto cleanup;
2006 } else if (lyrc != LY_ENOT) {
2007 lyd_free_tree(*envp);
2008 *envp = NULL;
2009 ERR("Session %u: received an invalid message (%s).", session->id, ly_errmsg(session->ctx));
2010 goto cleanup;
2011 }
2012
Michal Vaskoc5ea6382021-03-15 16:24:08 +01002013 /* assume a rpc-reply, reset and store it */
2014 ly_in_reset(msg);
Michal Vasko77367452021-02-16 16:32:18 +01002015 cont_ptr = &session->opts.client.replies;
2016 while (*cont_ptr) {
2017 cont_ptr = &((*cont_ptr)->next);
2018 }
2019 *cont_ptr = malloc(sizeof **cont_ptr);
2020 if (!*cont_ptr) {
2021 ERRMEM;
2022 goto cleanup;
2023 }
2024 (*cont_ptr)->msg = msg;
Michal Vaskoc5ea6382021-03-15 16:24:08 +01002025 msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002026 (*cont_ptr)->next = NULL;
2027
2028 ret = NC_MSG_REPLY;
2029
2030cleanup:
2031 ly_in_free(msg, 1);
2032 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002033}
2034
2035API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002036nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002037{
Michal Vasko45e53ae2016-04-07 11:46:03 +02002038 if (!session) {
2039 ERRARG("session");
2040 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002041 } else if (!envp) {
2042 ERRARG("envp");
2043 return NC_MSG_ERROR;
2044 } else if (!op) {
2045 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002046 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002047 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vaskod083db62016-01-19 10:31:29 +01002048 ERR("Session %u: invalid session to receive Notifications.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002049 return NC_MSG_ERROR;
2050 }
2051
Michal Vasko77367452021-02-16 16:32:18 +01002052 /* receive a notification */
2053 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002054}
2055
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002056static void *
2057nc_recv_notif_thread(void *arg)
2058{
2059 struct nc_ntf_thread_arg *ntarg;
2060 struct nc_session *session;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002061
Michal Vasko77367452021-02-16 16:32:18 +01002062 void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op);
2063 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002064 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002065 pthread_t *ntf_tid;
2066
2067 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002068
2069 ntarg = (struct nc_ntf_thread_arg *)arg;
2070 session = ntarg->session;
2071 notif_clb = ntarg->notif_clb;
2072 free(ntarg);
2073
Michal Vasko131120a2018-05-29 15:44:02 +02002074 /* remember our allocated tid, we will be freeing it */
Michal Vaskob639e9c2020-09-09 09:51:48 +02002075 ntf_tid = (pthread_t *)ATOMIC_LOAD(session->opts.client.ntf_tid);
Michal Vasko131120a2018-05-29 15:44:02 +02002076
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002077 while (ATOMIC_LOAD(session->opts.client.ntf_tid)) {
Michal Vasko77367452021-02-16 16:32:18 +01002078 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002079 if (msgtype == NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002080 notif_clb(session, envp, op);
2081 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2082 lyd_free_tree(envp);
2083 lyd_free_tree(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002084 break;
2085 }
Michal Vasko77367452021-02-16 16:32:18 +01002086 lyd_free_tree(envp);
2087 lyd_free_tree(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002088 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002089 /* quit this thread once the session is broken */
2090 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002091 }
2092
2093 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2094 }
2095
Michal Vasko0651c902016-05-19 15:55:42 +02002096 VRB("Session %u: notification thread exit.", session->id);
Michal Vaskob639e9c2020-09-09 09:51:48 +02002097 ATOMIC_STORE(session->opts.client.ntf_tid, (uintptr_t)NULL);
Michal Vasko131120a2018-05-29 15:44:02 +02002098 free(ntf_tid);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002099 return NULL;
2100}
2101
2102API int
Michal Vasko77367452021-02-16 16:32:18 +01002103nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session,
2104 const struct lyd_node *envp, const struct lyd_node *op))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002105{
2106 struct nc_ntf_thread_arg *ntarg;
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002107 pthread_t *tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002108 int ret;
2109
Michal Vasko45e53ae2016-04-07 11:46:03 +02002110 if (!session) {
2111 ERRARG("session");
2112 return -1;
2113 } else if (!notif_clb) {
2114 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002115 return -1;
2116 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
2117 ERR("Session %u: invalid session to receive Notifications.", session->id);
2118 return -1;
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002119 } else if (ATOMIC_LOAD(session->opts.client.ntf_tid)) {
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002120 ERR("Session %u: separate notification thread is already running.", session->id);
2121 return -1;
2122 }
2123
2124 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002125 if (!ntarg) {
2126 ERRMEM;
2127 return -1;
2128 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002129 ntarg->session = session;
2130 ntarg->notif_clb = notif_clb;
2131
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002132 tid = malloc(sizeof *tid);
2133 if (!tid) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01002134 ERRMEM;
2135 free(ntarg);
2136 return -1;
2137 }
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002138 /* just so that nc_recv_notif_thread() does not immediately exit, the value does not matter */
Michal Vaskob639e9c2020-09-09 09:51:48 +02002139 ATOMIC_STORE(session->opts.client.ntf_tid, (uintptr_t)tid);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002140
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002141 ret = pthread_create(tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002142 if (ret) {
2143 ERR("Session %u: failed to create a new thread (%s).", strerror(errno));
2144 free(ntarg);
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002145 free(tid);
Michal Vaskob639e9c2020-09-09 09:51:48 +02002146 ATOMIC_STORE(session->opts.client.ntf_tid, (uintptr_t)NULL);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002147 return -1;
2148 }
2149
2150 return 0;
2151}
2152
Michal Vasko77367452021-02-16 16:32:18 +01002153static const char *
2154nc_wd2str(NC_WD_MODE wd)
2155{
2156 switch (wd) {
2157 case NC_WD_ALL:
2158 return "report-all";
2159 case NC_WD_ALL_TAG:
2160 return "report-all-tagged";
2161 case NC_WD_TRIM:
2162 return "trim";
2163 case NC_WD_EXPLICIT:
2164 return "explicit";
2165 default:
2166 break;
2167 }
2168
2169 return NULL;
2170}
2171
Michal Vasko086311b2016-01-08 09:53:11 +01002172API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002173nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002174{
2175 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002176 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002177 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002178 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002179 struct nc_rpc_getconfig *rpc_gc;
2180 struct nc_rpc_edit *rpc_e;
2181 struct nc_rpc_copy *rpc_cp;
2182 struct nc_rpc_delete *rpc_del;
2183 struct nc_rpc_lock *rpc_lock;
2184 struct nc_rpc_get *rpc_g;
2185 struct nc_rpc_kill *rpc_k;
2186 struct nc_rpc_commit *rpc_com;
2187 struct nc_rpc_cancel *rpc_can;
2188 struct nc_rpc_validate *rpc_val;
2189 struct nc_rpc_getschema *rpc_gs;
2190 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002191 struct nc_rpc_getdata *rpc_getd;
2192 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002193 struct nc_rpc_establishsub *rpc_estsub;
2194 struct nc_rpc_modifysub *rpc_modsub;
2195 struct nc_rpc_deletesub *rpc_delsub;
2196 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002197 struct nc_rpc_establishpush *rpc_estpush;
2198 struct nc_rpc_modifypush *rpc_modpush;
2199 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002200 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002201 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002202 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002203 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002204 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002205 uint64_t cur_msgid;
2206
Michal Vasko45e53ae2016-04-07 11:46:03 +02002207 if (!session) {
2208 ERRARG("session");
2209 return NC_MSG_ERROR;
2210 } else if (!rpc) {
2211 ERRARG("rpc");
2212 return NC_MSG_ERROR;
2213 } else if (!msgid) {
2214 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002215 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002216 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vaskod083db62016-01-19 10:31:29 +01002217 ERR("Session %u: invalid session to send RPCs.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002218 return NC_MSG_ERROR;
2219 }
2220
Michal Vaskoc1171a42019-11-05 12:06:46 +01002221 switch (rpc->type) {
2222 case NC_RPC_ACT_GENERIC:
2223 /* checked when parsing */
2224 break;
2225 case NC_RPC_GETCONFIG:
2226 case NC_RPC_EDIT:
2227 case NC_RPC_COPY:
2228 case NC_RPC_DELETE:
2229 case NC_RPC_LOCK:
2230 case NC_RPC_UNLOCK:
2231 case NC_RPC_GET:
2232 case NC_RPC_KILL:
2233 case NC_RPC_COMMIT:
2234 case NC_RPC_DISCARD:
2235 case NC_RPC_CANCEL:
2236 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002237 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002238 if (!mod) {
Michal Vasko086cd572017-01-12 12:19:05 +01002239 ERR("Session %u: missing \"ietf-netconf\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002240 return NC_MSG_ERROR;
2241 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002242 break;
2243 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002244 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002245 if (!mod) {
2246 ERR("Session %u: missing \"ietf-netconf-monitoring\" schema in the context.", session->id);
2247 return NC_MSG_ERROR;
2248 }
2249 break;
2250 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002251 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002252 if (!mod) {
2253 ERR("Session %u: missing \"notifications\" schema in the context.", session->id);
2254 return NC_MSG_ERROR;
2255 }
2256 break;
2257 case NC_RPC_GETDATA:
2258 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002259 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002260 if (!mod) {
2261 ERR("Session %u: missing \"ietf-netconf-nmda\" schema in the context.", session->id);
2262 return NC_MSG_ERROR;
2263 }
2264 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002265 case NC_RPC_ESTABLISHSUB:
2266 case NC_RPC_MODIFYSUB:
2267 case NC_RPC_DELETESUB:
2268 case NC_RPC_KILLSUB:
2269 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2270 if (!mod) {
2271 ERR("Session %u: missing \"ietf-subscribed-notifications\" schema in the context.", session->id);
2272 return NC_MSG_ERROR;
2273 }
2274 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002275 case NC_RPC_ESTABLISHPUSH:
2276 case NC_RPC_MODIFYPUSH:
2277 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2278 if (!mod) {
2279 ERR("Session %u: missing \"ietf-subscribed-notifications\" schema in the context.", session->id);
2280 return NC_MSG_ERROR;
2281 }
2282 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2283 if (!mod2) {
2284 ERR("Session %u: missing \"ietf-yang-push\" schema in the context.", session->id);
2285 return NC_MSG_ERROR;
2286 }
2287 break;
2288 case NC_RPC_RESYNCSUB:
2289 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2290 if (!mod) {
2291 ERR("Session %u: missing \"ietf-yang-push\" schema in the context.", session->id);
2292 return NC_MSG_ERROR;
2293 }
2294 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002295 case NC_RPC_UNKNOWN:
2296 ERRINT;
2297 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002298 }
2299
Michal Vaskoab9deb62021-05-27 11:37:00 +02002300#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2301
Michal Vasko086311b2016-01-08 09:53:11 +01002302 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002303 case NC_RPC_ACT_GENERIC:
2304 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002305
2306 if (rpc_gen->has_data) {
2307 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002308 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002309 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002310 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2311 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2312 ly_in_free(in, 0);
2313 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002314 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002315 }
Michal Vasko086311b2016-01-08 09:53:11 +01002316 }
2317 break;
2318
2319 case NC_RPC_GETCONFIG:
2320 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2321
Michal Vaskoab9deb62021-05-27 11:37:00 +02002322 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2323 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2324 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002325 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002326 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002327 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node));
2328 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002329 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002330 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2331 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2332 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002333 }
2334 }
2335
2336 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002337 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002338 if (!ietfncwd) {
Michal Vaskoc1171a42019-11-05 12:06:46 +01002339 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002340 lyrc = LY_ENOTFOUND;
2341 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002342 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002343 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 +01002344 }
2345 break;
2346
2347 case NC_RPC_EDIT:
2348 rpc_e = (struct nc_rpc_edit *)rpc;
2349
Michal Vaskoab9deb62021-05-27 11:37:00 +02002350 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2351 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2352 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002353
2354 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002355 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 +01002356 }
Michal Vasko086311b2016-01-08 09:53:11 +01002357 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002358 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 +01002359 }
Michal Vasko086311b2016-01-08 09:53:11 +01002360 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002361 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 +01002362 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002363 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002364 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 +01002365 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002366 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002367 }
2368 break;
2369
2370 case NC_RPC_COPY:
2371 rpc_cp = (struct nc_rpc_copy *)rpc;
2372
Michal Vaskoab9deb62021-05-27 11:37:00 +02002373 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2374 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002375 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002376 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002377 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002378 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002379 }
2380
Michal Vaskoab9deb62021-05-27 11:37:00 +02002381 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002382 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002383 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002384 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 +01002385 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002386 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002387 }
2388 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002389 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002390 }
2391
2392 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002393 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002394 if (!ietfncwd) {
Michal Vaskoc1171a42019-11-05 12:06:46 +01002395 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002396 lyrc = LY_ENOTFOUND;
2397 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002398 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002399 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 +01002400 }
2401 break;
2402
2403 case NC_RPC_DELETE:
2404 rpc_del = (struct nc_rpc_delete *)rpc;
2405
Michal Vaskoab9deb62021-05-27 11:37:00 +02002406 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2407 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002408 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002409 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002410 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002411 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002412 }
2413 break;
2414
2415 case NC_RPC_LOCK:
2416 rpc_lock = (struct nc_rpc_lock *)rpc;
2417
Michal Vaskoab9deb62021-05-27 11:37:00 +02002418 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2419 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2420 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002421 break;
2422
2423 case NC_RPC_UNLOCK:
2424 rpc_lock = (struct nc_rpc_lock *)rpc;
2425
Michal Vaskoab9deb62021-05-27 11:37:00 +02002426 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2427 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2428 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002429 break;
2430
2431 case NC_RPC_GET:
2432 rpc_g = (struct nc_rpc_get *)rpc;
2433
Michal Vaskoab9deb62021-05-27 11:37:00 +02002434 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002435 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002436 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002437 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node));
2438 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002439 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002440 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2441 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2442 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002443 }
2444 }
2445
2446 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002447 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002448 if (!ietfncwd) {
Michal Vaskoc1171a42019-11-05 12:06:46 +01002449 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002450 lyrc = LY_ENOTFOUND;
2451 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002452 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002453 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 +01002454 }
2455 break;
2456
2457 case NC_RPC_KILL:
2458 rpc_k = (struct nc_rpc_kill *)rpc;
2459
Michal Vaskoab9deb62021-05-27 11:37:00 +02002460 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002461 sprintf(str, "%u", rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002462 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002463 break;
2464
2465 case NC_RPC_COMMIT:
2466 rpc_com = (struct nc_rpc_commit *)rpc;
2467
Michal Vaskoab9deb62021-05-27 11:37:00 +02002468 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002469 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002470 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002471 }
2472
2473 if (rpc_com->confirm_timeout) {
2474 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002475 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002476 }
Michal Vasko086311b2016-01-08 09:53:11 +01002477 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002478 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002479 }
Michal Vasko086311b2016-01-08 09:53:11 +01002480 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002481 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002482 }
2483 break;
2484
2485 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002486 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002487 break;
2488
2489 case NC_RPC_CANCEL:
2490 rpc_can = (struct nc_rpc_cancel *)rpc;
2491
Michal Vaskoab9deb62021-05-27 11:37:00 +02002492 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002493 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002494 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002495 }
2496 break;
2497
2498 case NC_RPC_VALIDATE:
2499 rpc_val = (struct nc_rpc_validate *)rpc;
2500
Michal Vaskoab9deb62021-05-27 11:37:00 +02002501 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2502 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002503 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002504 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002505 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 +01002506 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002507 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002508 }
2509 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002510 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002511 }
2512 break;
2513
2514 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002515 rpc_gs = (struct nc_rpc_getschema *)rpc;
2516
Michal Vaskoab9deb62021-05-27 11:37:00 +02002517 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2518 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002519 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002520 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002521 }
2522 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002523 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002524 }
2525 break;
2526
2527 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002528 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2529
Michal Vaskoab9deb62021-05-27 11:37:00 +02002530 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002531 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002532 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002533 }
2534
2535 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002536 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002537 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node));
2538 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002539 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002540 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2541 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2542 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002543 }
2544 }
Michal Vasko086311b2016-01-08 09:53:11 +01002545 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002546 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002547 }
Michal Vasko086311b2016-01-08 09:53:11 +01002548 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002549 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002550 }
2551 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002552
2553 case NC_RPC_GETDATA:
2554 rpc_getd = (struct nc_rpc_getdata *)rpc;
2555
Michal Vaskoab9deb62021-05-27 11:37:00 +02002556 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2557 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2558
Michal Vaskoc1171a42019-11-05 12:06:46 +01002559 if (rpc_getd->filter) {
2560 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002561 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 +01002562 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002563 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002564 }
2565 }
2566 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002567 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002568 }
2569 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002570 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2571 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002572 }
2573 if (rpc_getd->max_depth) {
2574 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002575 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002576 }
2577 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002578 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002579 }
2580
2581 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002582 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002583 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 +01002584 }
2585 break;
2586
2587 case NC_RPC_EDITDATA:
2588 rpc_editd = (struct nc_rpc_editdata *)rpc;
2589
Michal Vaskoab9deb62021-05-27 11:37:00 +02002590 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2591 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002592
2593 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002594 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2595 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002596 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002597 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002598 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 +01002599 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002600 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002601 }
2602 break;
2603
Michal Vasko96f247a2021-03-15 13:32:10 +01002604 case NC_RPC_ESTABLISHSUB:
2605 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2606
Michal Vaskoab9deb62021-05-27 11:37:00 +02002607 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002608
2609 if (rpc_estsub->filter) {
2610 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002611 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML,
2612 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002613 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002614 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002615 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002616 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002617 }
2618 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002619 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002620
2621 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002622 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002623 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002624 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002625 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002626 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002627 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002628 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002629 }
2630 break;
2631
2632 case NC_RPC_MODIFYSUB:
2633 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2634
Michal Vaskoab9deb62021-05-27 11:37:00 +02002635 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002636
2637 sprintf(str, "%u", rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002638 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002639
2640 if (rpc_modsub->filter) {
2641 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002642 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML,
2643 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002644 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002645 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002646 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002647 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002648 }
2649 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002650 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002651 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002652 }
2653 break;
2654
2655 case NC_RPC_DELETESUB:
2656 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
2657
Michal Vaskoab9deb62021-05-27 11:37:00 +02002658 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002659
2660 sprintf(str, "%u", rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002661 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002662 break;
2663
2664 case NC_RPC_KILLSUB:
2665 rpc_killsub = (struct nc_rpc_killsub *)rpc;
2666
Michal Vaskoab9deb62021-05-27 11:37:00 +02002667 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002668
2669 sprintf(str, "%u", rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002670 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002671 break;
2672
Michal Vasko305faca2021-03-25 09:16:02 +01002673 case NC_RPC_ESTABLISHPUSH:
2674 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
2675
Michal Vaskoab9deb62021-05-27 11:37:00 +02002676 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
2677 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002678
2679 if (rpc_estpush->filter) {
2680 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002681 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0,
2682 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002683 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002684 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002685 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002686 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002687 }
2688 }
2689
2690 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002691 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002692 }
Michal Vasko305faca2021-03-25 09:16:02 +01002693 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002694 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002695 }
2696
2697 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002698 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002699 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002700 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002701 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002702 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002703 }
2704 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002705 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002706 if (rpc_estpush->dampening_period) {
2707 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002708 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002709 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002710 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
2711 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002712 if (rpc_estpush->excluded_change) {
2713 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002714 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
2715 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002716 }
2717 }
2718 }
2719 break;
2720
2721 case NC_RPC_MODIFYPUSH:
2722 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
2723
Michal Vaskoab9deb62021-05-27 11:37:00 +02002724 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002725
2726 sprintf(str, "%u", rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002727 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
2728 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002729
2730 if (rpc_modpush->filter) {
2731 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002732 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0,
2733 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002734 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002735 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002736 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002737 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002738 }
2739 }
Michal Vasko305faca2021-03-25 09:16:02 +01002740 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002741 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002742 }
2743
2744 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002745 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002746 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002747 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002748 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002749 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002750 }
2751 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002752 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002753 if (rpc_modpush->dampening_period) {
2754 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002755 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002756 }
2757 }
2758 break;
2759
2760 case NC_RPC_RESYNCSUB:
2761 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
2762
Michal Vaskoab9deb62021-05-27 11:37:00 +02002763 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002764 sprintf(str, "%u", rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002765 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002766 break;
2767
Michal Vasko96f247a2021-03-15 13:32:10 +01002768 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002769 ERRINT;
2770 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002771 }
2772
Michal Vaskoab9deb62021-05-27 11:37:00 +02002773#undef CHECK_LYRC_BREAK
2774
2775 if (lyrc) {
2776 ERR("Session %u: failed to create RPC, perhaps a required feature is disabled.", session->id);
2777 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02002778 return NC_MSG_ERROR;
2779 }
2780
Michal Vasko77367452021-02-16 16:32:18 +01002781 if (lyd_validate_op(data, NULL, LYD_TYPE_RPC_YANG, NULL)) {
Radek Krejcib4b19062018-02-07 16:33:06 +01002782 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01002783 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01002784 }
Michal Vasko086311b2016-01-08 09:53:11 +01002785 return NC_MSG_ERROR;
2786 }
2787
Michal Vasko131120a2018-05-29 15:44:02 +02002788 /* send RPC, store its message ID */
2789 r = nc_send_msg_io(session, timeout, data);
2790 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002791
Radek Krejcib4b19062018-02-07 16:33:06 +01002792 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01002793 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01002794 }
Michal Vasko086311b2016-01-08 09:53:11 +01002795
Michal Vasko131120a2018-05-29 15:44:02 +02002796 if (r == NC_MSG_RPC) {
2797 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002798 }
Michal Vasko131120a2018-05-29 15:44:02 +02002799 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01002800}
Michal Vaskode2946c2017-01-12 12:19:26 +01002801
2802API void
2803nc_client_session_set_not_strict(struct nc_session *session)
2804{
2805 if (session->side != NC_CLIENT) {
2806 ERRARG("session");
2807 return;
2808 }
2809
2810 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
2811}