blob: 0948d9a52f4c232bc00c9d98262d13a6e72fa799 [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) {
Michal Vasko05532772021-06-03 12:12:38 +0200327 VRB(clb_data->session, "Reading schema from localfile \"%s\".", localfile);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200328 f = fopen(localfile, "r");
329 if (!f) {
Michal Vasko05532772021-06-03 12:12:38 +0200330 ERR(clb_data->session, "Unable to open \"%s\" file to get schema (%s).", localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200331 free(localfile);
332 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100333 }
Michal Vasko086311b2016-01-08 09:53:11 +0100334
Radek Krejci65ef6d52018-08-16 16:35:02 +0200335 fseek(f, 0, SEEK_END);
336 length = ftell(f);
Radek Krejci3f499a62018-08-17 10:16:57 +0200337 if (length < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200338 ERR(clb_data->session, "Unable to get size of schema file \"%s\".", localfile);
Radek Krejci3f499a62018-08-17 10:16:57 +0200339 free(localfile);
340 fclose(f);
341 return NULL;
342 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200343 fseek(f, 0, SEEK_SET);
344
345 model_data = malloc(length + 1);
346 if (!model_data) {
347 ERRMEM;
348 } else if ((l = fread(model_data, 1, length, f)) != length) {
Michal Vasko05532772021-06-03 12:12:38 +0200349 ERR(clb_data->session, "Reading schema from \"%s\" failed (%d bytes read, but %d expected).", localfile, l,
350 length);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200351 free(model_data);
352 model_data = NULL;
353 } else {
354 /* terminating NULL byte */
355 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100356 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200357 fclose(f);
358 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100359 }
360
Radek Krejci65ef6d52018-08-16 16:35:02 +0200361 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100362}
363
364static char *
Radek Krejci65ef6d52018-08-16 16:35:02 +0200365retrieve_schema_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200366 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100367{
Michal Vasko086311b2016-01-08 09:53:11 +0100368 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100369 struct lyd_node *envp = NULL, *op = NULL;
370 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100371 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100372 uint64_t msgid;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200373 char *localfile = NULL;
374 FILE *f;
375 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100376
Michal Vasko05532772021-06-03 12:12:38 +0200377 VRB(clb_data->session, "Reading schema from server via get-schema.");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200378 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100379
Radek Krejci65ef6d52018-08-16 16:35:02 +0200380 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100381 usleep(1000);
382 }
383 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200384 ERR(clb_data->session, "Failed to send the <get-schema> RPC.");
Michal Vasko086311b2016-01-08 09:53:11 +0100385 nc_rpc_free(rpc);
386 return NULL;
387 }
388
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200389 do {
Michal Vasko77367452021-02-16 16:32:18 +0100390 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200391 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100392 nc_rpc_free(rpc);
393 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200394 ERR(clb_data->session, "Timeout for receiving reply to a <get-schema> expired.");
Michal Vasko77367452021-02-16 16:32:18 +0100395 goto cleanup;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200396 } else if ((msg == NC_MSG_ERROR) || !op) {
Michal Vasko05532772021-06-03 12:12:38 +0200397 ERR(clb_data->session, "Failed to receive a reply to <get-schema>.");
Michal Vasko77367452021-02-16 16:32:18 +0100398 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100399 }
400
Michal Vasko77367452021-02-16 16:32:18 +0100401 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Michal Vasko05532772021-06-03 12:12:38 +0200402 ERR(clb_data->session, "Unexpected data in reply to a <get-schema> RPC.");
Michal Vasko77367452021-02-16 16:32:18 +0100403 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200404 }
Michal Vasko77367452021-02-16 16:32:18 +0100405 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200406 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200407 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100408 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200409 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200410 break;
411 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100412 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200413 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200414 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200415 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200416 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100417 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200418 }
Michal Vasko086311b2016-01-08 09:53:11 +0100419
Radek Krejci488b0702018-08-29 14:47:15 +0200420 if (model_data && !model_data[0]) {
421 /* empty data */
422 free(model_data);
423 model_data = NULL;
424 }
425
Radek Krejcifd5b6682017-06-13 15:52:53 +0200426 /* try to store the model_data into local schema repository */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200427 if (model_data) {
428 *format = LYS_IN_YANG;
429 if (client_opts.schema_searchpath) {
Michal Vasko77367452021-02-16 16:32:18 +0100430 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
431 rev ? rev : "") == -1) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200432 ERRMEM;
Michal Vaskof945da52018-02-15 08:45:13 +0100433 } else {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200434 f = fopen(localfile, "w");
435 if (!f) {
Michal Vasko05532772021-06-03 12:12:38 +0200436 WRN(clb_data->session, "Unable to store \"%s\" as a local copy of schema retrieved via <get-schema> (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200437 localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200438 } else {
439 fputs(model_data, f);
440 fclose(f);
441 }
442 free(localfile);
Michal Vaskof945da52018-02-15 08:45:13 +0100443 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200444 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200445 }
446
Michal Vasko77367452021-02-16 16:32:18 +0100447cleanup:
448 lyd_free_tree(envp);
449 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100450 return model_data;
451}
452
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200453static void
454free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200455{
456 free(data);
457 (void)user_data;
458}
459
Michal Vasko77367452021-02-16 16:32:18 +0100460static LY_ERR
Radek Krejci65ef6d52018-08-16 16:35:02 +0200461retrieve_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 +0100462 void *user_data, LYS_INFORMAT *format, const char **module_data,
463 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200464{
465 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
466 unsigned int u, v, match = 1;
467 const char *name = NULL, *rev = NULL;
468 char *model_data = NULL;
469
470 /* get and check the final name and revision of the schema to be retrieved */
471 if (!mod_rev || !mod_rev[0]) {
472 /* newest revision requested - get the newest revision from the list of available modules on server */
473 match = 0;
474 for (u = 0; clb_data->schemas[u].name; ++u) {
475 if (strcmp(mod_name, clb_data->schemas[u].name)) {
476 continue;
477 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200478 if (!match || (strcmp(mod_rev, clb_data->schemas[u].revision) > 0)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200479 mod_rev = clb_data->schemas[u].revision;
480 }
481 match = u + 1;
482 }
483 if (!match) {
Michal Vasko1d2665c2021-02-12 09:28:44 +0100484 /* valid situation if we are retrieving YANG 1.1 schema and have only capabilities for now
485 * (when loading ietf-datastore for ietf-yang-library) */
Michal Vasko05532772021-06-03 12:12:38 +0200486 VRB(clb_data->session, "Unable to identify revision of the schema \"%s\" from "
487 "the available server side information.", mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200488 }
489 }
490 if (submod_name) {
491 name = submod_name;
492 if (sub_rev) {
493 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200494 } else if (match) {
495 if (!clb_data->schemas[match - 1].submodules) {
Michal Vasko05532772021-06-03 12:12:38 +0200496 VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", "
497 "in schema \"%s\", from the available server side information.", submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200498 } else {
499 for (v = 0; clb_data->schemas[match - 1].submodules[v].name; ++v) {
500 if (!strcmp(submod_name, clb_data->schemas[match - 1].submodules[v].name)) {
501 rev = sub_rev = clb_data->schemas[match - 1].submodules[v].revision;
502 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200503 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200504 if (!rev) {
Michal Vasko05532772021-06-03 12:12:38 +0200505 ERR(clb_data->session, "Requested submodule \"%s\" is not known for schema \"%s\" on server side.",
506 submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100507 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200508 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200509 }
510 }
511 } else {
512 name = mod_name;
513 rev = mod_rev;
514 }
515
Michal Vasko05532772021-06-03 12:12:38 +0200516 VRB(clb_data->session, "Retrieving data for schema \"%s\", revision \"%s\".", name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200517
518 if (match) {
519 /* we have enough information to avoid communication with server and try to get
520 * the schema locally */
521
522 /* 1. try to get data locally */
523 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
524
525 /* 2. try to use <get-schema> */
526 if (!model_data && clb_data->has_get_schema) {
527 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
528 }
529 } else {
530 /* we are unsure which revision of the schema we should load, so first try to get
531 * the newest revision from the server via get-schema and only if the server does not
532 * implement get-schema, try to load the newest revision locally. This is imperfect
533 * solution, but there are situation when a client does not know what revision is
534 * actually implemented by the server. */
535
536 /* 1. try to use <get-schema> */
537 if (clb_data->has_get_schema) {
538 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
539 }
540
541 /* 2. try to get data locally */
542 if (!model_data) {
543 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
544 }
545 }
546
547 /* 3. try to use user callback */
548 if (!model_data && clb_data->user_clb) {
Michal Vasko05532772021-06-03 12:12:38 +0200549 VRB(clb_data->session, "Reading schema via user callback.");
Michal Vasko77367452021-02-16 16:32:18 +0100550 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
551 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200552 }
553
Jan Kundrát35972df2018-09-06 19:00:01 +0200554 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100555 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200556 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200557}
558
Michal Vaskoceae0152018-02-14 16:03:59 +0100559static int
Radek Krejci65ef6d52018-08-16 16:35:02 +0200560nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, struct schema_info *schemas,
Michal Vasko77367452021-02-16 16:32:18 +0100561 ly_module_imp_clb user_clb, void *user_data, int has_get_schema, struct lys_module **mod)
Michal Vaskoceae0152018-02-14 16:03:59 +0100562{
563 int ret = 0;
564 struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200565 const char *module_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200566 LYS_INFORMAT format;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200567
568 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200569 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100570
Radek Krejci65ef6d52018-08-16 16:35:02 +0200571 *mod = NULL;
572 if (revision) {
Michal Vasko77367452021-02-16 16:32:18 +0100573 *mod = ly_ctx_get_module(session->ctx, name, revision);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200574 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100575 if (*mod) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200576 if (!(*mod)->implemented) {
Michal Vaskoceae0152018-02-14 16:03:59 +0100577 /* make the present module implemented */
Michal Vasko77367452021-02-16 16:32:18 +0100578 if (lys_set_implemented(*mod, NULL)) {
Michal Vasko05532772021-06-03 12:12:38 +0200579 ERR(session, "Failed to implement model \"%s\".", (*mod)->name);
Michal Vaskoceae0152018-02-14 16:03:59 +0100580 ret = -1;
581 }
582 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200583 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100584 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200585 clb_data.has_get_schema = has_get_schema;
586 clb_data.schemas = schemas;
587 clb_data.session = session;
588 clb_data.user_clb = user_clb;
589 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100590
591 /* clear all the errors and just collect them for now */
592 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200593 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100594
Radek Krejci65ef6d52018-08-16 16:35:02 +0200595 /* get module data */
Michal Vasko77367452021-02-16 16:32:18 +0100596 retrieve_schema_data(name, revision, NULL, NULL, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100597
Radek Krejci65ef6d52018-08-16 16:35:02 +0200598 if (module_data) {
599 /* parse the schema */
600 ly_ctx_set_module_imp_clb(session->ctx, retrieve_schema_data, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100601
Michal Vasko77367452021-02-16 16:32:18 +0100602 lys_parse_mem(session->ctx, module_data, format, (const struct lys_module **)mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200603 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100604 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100605 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100606
Radek Krejci65ef6d52018-08-16 16:35:02 +0200607 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
608 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100609
Michal Vaskodbf7c272018-02-19 09:07:59 +0100610 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200611 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100612 if (!(*mod)) {
613 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100614 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100615 }
616 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200617 } else {
618 /* print only warnings */
619 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
620 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100621 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200622 }
623 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100624 }
625
Michal Vaskodbf7c272018-02-19 09:07:59 +0100626 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100627 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100628 }
629
630 return ret;
631}
632
Radek Krejci65ef6d52018-08-16 16:35:02 +0200633static void
634free_schema_info(struct schema_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200635{
Michal Vasko77367452021-02-16 16:32:18 +0100636 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200637
Radek Krejci65ef6d52018-08-16 16:35:02 +0200638 if (!list) {
639 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200640 }
641
Radek Krejci65ef6d52018-08-16 16:35:02 +0200642 for (u = 0; list[u].name; ++u) {
643 free(list[u].name);
644 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100645 if (list[u].features) {
646 for (v = 0; list[u].features[v]; ++v) {
647 free(list[u].features[v]);
648 }
649 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200650 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200651 if (list[u].submodules) {
652 for (v = 0; list[u].submodules[v].name; ++v) {
653 free(list[u].submodules[v].name);
654 free(list[u].submodules[v].revision);
655 }
656 free(list[u].submodules);
657 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200658 }
659 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200660}
661
Radek Krejci235d8cb2018-08-17 14:04:32 +0200662static int
663build_schema_info_yl(struct nc_session *session, struct schema_info **result)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200664{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200665 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100666 struct lyd_node *op = NULL, *envp = NULL;
667 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200668 NC_MSG_TYPE msg;
669 uint64_t msgid;
Radek Krejcia8dfaea2018-08-17 10:55:09 +0200670 struct ly_set *modules = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100671 uint32_t u, v, submodules_count, feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200672 struct lyd_node *iter, *child;
673 struct lys_module *mod;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200674 int ret = EXIT_SUCCESS;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200675
676 /* get yang-library data from the server */
Radek Krejci261737f2018-08-21 09:22:34 +0200677 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
Michal Vasko77367452021-02-16 16:32:18 +0100678 rpc = nc_rpc_getdata("ietf-datastores:operational", "/ietf-yang-library:*", "false", NULL, 0, 0, 0, 0, 0,
679 NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200680 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100681 rpc = nc_rpc_getdata("ietf-datastores:operational",
682 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", "false", NULL, 0, 0, 0, 0,
683 0, NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200684 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200685 if (!rpc) {
686 goto cleanup;
687 }
688
689 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
690 usleep(1000);
691 }
692 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200693 WRN(session, "Failed to send request for yang-library data.");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200694 goto cleanup;
695 }
696
697 do {
Michal Vasko77367452021-02-16 16:32:18 +0100698 lyd_free_tree(envp);
699 lyd_free_tree(op);
700
701 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200702 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200703 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200704 WRN(session, "Timeout for receiving reply to a <get> yang-library data expired.");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200705 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100706 } else if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200707 WRN(session, "Failed to receive a reply to <get> of yang-library data.");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200708 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100709 } else if (!op || !lyd_child(op) || strcmp(lyd_child(op)->schema->name, "data")) {
Michal Vasko05532772021-06-03 12:12:38 +0200710 WRN(session, "Unexpected reply without data to a yang-library <get> RPC.");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200711 goto cleanup;
712 }
713
Michal Vasko77367452021-02-16 16:32:18 +0100714 data = (struct lyd_node_any *)lyd_child(op);
715 if (data->value_type != LYD_ANYDATA_DATATREE) {
Michal Vasko05532772021-06-03 12:12:38 +0200716 WRN(session, "Unexpected data in reply to a yang-library <get> RPC.");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200717 goto cleanup;
718 }
719
Michal Vasko77367452021-02-16 16:32:18 +0100720 if (lyd_find_xpath(data->value.tree, "/ietf-yang-library:modules-state/module", &modules)) {
Michal Vasko05532772021-06-03 12:12:38 +0200721 WRN(session, "No module information in reply to a yang-library <get> RPC.");
Radek Krejci2d088832018-08-21 13:40:14 +0200722 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200723 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200724
Michal Vasko77367452021-02-16 16:32:18 +0100725 (*result) = calloc(modules->count + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200726 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200727 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200728 ret = EXIT_FAILURE;
Radek Krejcic9390502018-08-17 10:23:13 +0200729 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200730 }
731
Michal Vasko77367452021-02-16 16:32:18 +0100732 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200733 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100734 feature_count = 0;
735 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
736 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Michal Vasko5d9d5972021-06-09 14:17:01 +0200737 if (!iter->schema || (iter->schema->module != mod)) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200738 /* ignore node from other schemas (augments) */
739 continue;
740 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200741 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200742 /* ignore empty nodes */
743 continue;
744 }
745 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200746 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200747 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200748 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200749 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200750 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200751 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100752 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
753 if (!(*result)[u].features) {
754 ERRMEM;
755 free_schema_info(*result);
756 *result = NULL;
757 ret = EXIT_FAILURE;
758 goto cleanup;
759 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200760 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100761 (*result)[u].features[feature_count + 1] = NULL;
762 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200763 } else if (!strcmp(iter->schema->name, "submodule")) {
764 submodules_count++;
765 }
766 }
767
768 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200769 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
770 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200771 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200772 free_schema_info(*result);
773 *result = NULL;
774 ret = EXIT_FAILURE;
775 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200776 } else {
777 v = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100778 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
779 mod = modules->dnodes[u]->schema->module;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200780 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
Michal Vasko77367452021-02-16 16:32:18 +0100781 LY_LIST_FOR(lyd_child(iter), child) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200782 if (mod != child->schema->module) {
783 continue;
784 } else if (!strcmp(child->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200785 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200786 } else if (!strcmp(child->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200787 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200788 }
789 }
790 }
791 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200792 }
793 }
794 }
795
Radek Krejcifd5b6682017-06-13 15:52:53 +0200796cleanup:
797 nc_rpc_free(rpc);
Michal Vasko77367452021-02-16 16:32:18 +0100798 lyd_free_tree(envp);
799 lyd_free_tree(op);
800 ly_set_free(modules, NULL);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200801
Radek Krejci235d8cb2018-08-17 14:04:32 +0200802 if (session->status != NC_STATUS_RUNNING) {
Michal Vasko05532772021-06-03 12:12:38 +0200803 /* something bad happened, discard the session */
804 ERR(session, "Invalid session, discarding.");
Radek Krejci235d8cb2018-08-17 14:04:32 +0200805 ret = EXIT_FAILURE;
806 }
807
808 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200809}
810
Radek Krejci235d8cb2018-08-17 14:04:32 +0200811static int
812build_schema_info_cpblts(char **cpblts, struct schema_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200813{
Michal Vasko77367452021-02-16 16:32:18 +0100814 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200815 char *module_cpblt, *ptr, *ptr2;
816
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200817 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +0200818 (*result) = calloc(u + 1, sizeof **result);
819 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200820 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200821 return EXIT_FAILURE;
Radek Krejcic9390502018-08-17 10:23:13 +0200822 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200823
824 for (u = v = 0; cpblts[u]; ++u) {
825 module_cpblt = strstr(cpblts[u], "module=");
826 /* this capability requires a module */
827 if (!module_cpblt) {
828 continue;
829 }
830
831 /* get module's name */
832 ptr = (char *)module_cpblt + 7;
833 ptr2 = strchr(ptr, '&');
834 if (!ptr2) {
835 ptr2 = ptr + strlen(ptr);
836 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200837 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200838
839 /* get module's revision */
840 ptr = strstr(module_cpblt, "revision=");
841 if (ptr) {
842 ptr += 9;
843 ptr2 = strchr(ptr, '&');
844 if (!ptr2) {
845 ptr2 = ptr + strlen(ptr);
846 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200847 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200848 }
849
850 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200851 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200852
853 /* get module's features */
854 ptr = strstr(module_cpblt, "features=");
855 if (ptr) {
856 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +0100857 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200858 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
859 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +0100860 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
861 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
862 (*result)[v].features[feature_count + 1] = NULL;
863 ++feature_count;
864
Radek Krejci65ef6d52018-08-16 16:35:02 +0200865 ptr2 = ptr + 1;
866 }
867 }
868 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +0100869 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
870 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
871 (*result)[v].features[feature_count + 1] = NULL;
872 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200873 }
874 ++v;
875 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200876
877 return EXIT_SUCCESS;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200878}
879
880static int
Michal Vasko77367452021-02-16 16:32:18 +0100881nc_ctx_fill(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb, void *user_data,
882 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200883{
884 int ret = EXIT_FAILURE;
Michal Vasko77367452021-02-16 16:32:18 +0100885 struct lys_module *mod;
886 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200887
888 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +0200889 /* skip import-only modules */
890 if (!modules[u].implemented) {
891 continue;
892 }
893
Radek Krejci65ef6d52018-08-16 16:35:02 +0200894 /* we can continue even if it fails */
895 nc_ctx_load_module(session, modules[u].name, modules[u].revision, modules, user_clb, user_data, has_get_schema, &mod);
896
897 if (!mod) {
898 if (session->status != NC_STATUS_RUNNING) {
899 /* something bad heppened, discard the session */
Michal Vasko05532772021-06-03 12:12:38 +0200900 ERR(session, "Invalid session, discarding.");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200901 goto cleanup;
902 }
903
904 /* all loading ways failed, the schema will be ignored in the received data */
Michal Vasko05532772021-06-03 12:12:38 +0200905 WRN(session, "Failed to load schema \"%s@%s\".", modules[u].name, modules[u].revision ?
906 modules[u].revision : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200907 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
908 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100909 /* set the features */
910 lys_set_implemented(mod, (const char **)modules[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200911 }
Michal Vasko60f66602017-10-17 13:52:18 +0200912 }
913
Michal Vasko77367452021-02-16 16:32:18 +0100914 /* success */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200915 ret = EXIT_SUCCESS;
916
917cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +0200918 return ret;
919}
920
Radek Krejci65ef6d52018-08-16 16:35:02 +0200921static int
Michal Vasko77367452021-02-16 16:32:18 +0100922nc_ctx_fill_ietf_netconf(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb,
923 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200924{
Michal Vasko77367452021-02-16 16:32:18 +0100925 uint32_t u;
926 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200927
Michal Vasko77367452021-02-16 16:32:18 +0100928 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200929 if (!ietfnc) {
930 nc_ctx_load_module(session, "ietf-netconf", NULL, modules, user_clb, user_data, has_get_schema, &ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200931 if (!ietfnc) {
Michal Vasko77367452021-02-16 16:32:18 +0100932 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 +0200933 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200934 }
935 if (!ietfnc) {
Michal Vasko05532772021-06-03 12:12:38 +0200936 ERR(session, "Loading base NETCONF schema failed.");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200937 return 1;
938 }
939
940 /* set supported capabilities from ietf-netconf */
941 for (u = 0; modules[u].name; ++u) {
942 if (strcmp(modules[u].name, "ietf-netconf") || !modules[u].implemented) {
943 continue;
944 }
945
Michal Vasko77367452021-02-16 16:32:18 +0100946 lys_set_implemented(ietfnc, (const char **)modules[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200947 }
948
949 return 0;
950}
951
Michal Vasko086311b2016-01-08 09:53:11 +0100952int
953nc_ctx_check_and_fill(struct nc_session *session)
954{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200955 int i, get_schema_support = 0, yanglib_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +0100956 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100957 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100958 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +0200959 char *revision;
Radek Krejcib056ff82018-08-20 15:58:39 +0200960 struct schema_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100961
Michal Vasko2e6defd2016-10-07 15:48:15 +0200962 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +0100963
Radek Krejci65ef6d52018-08-16 16:35:02 +0200964 /* 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 +0200965 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200966
Radek Krejci65ef6d52018-08-16 16:35:02 +0200967 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
968 * schemas only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +0100969 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200970
971 /* our callback is set later with appropriate data */
972 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
973
974 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200975 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200976 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +0200977 get_schema_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200978 if (yanglib_support) {
979 break;
980 }
Michal Vaskof0fba4e2020-02-14 17:15:31 +0100981 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +0200982 yanglib_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200983 if (get_schema_support) {
984 break;
985 }
Michal Vasko086311b2016-01-08 09:53:11 +0100986 }
987 }
Michal Vasko31dd4c52020-04-17 10:29:44 +0200988 if (get_schema_support) {
Michal Vasko05532772021-06-03 12:12:38 +0200989 VRB(session, "Capability for <get-schema> support found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +0200990 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200991 VRB(session, "Capability for <get-schema> support not found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +0200992 }
993 if (yanglib_support) {
Michal Vasko05532772021-06-03 12:12:38 +0200994 VRB(session, "Capability for yang-library support found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +0200995 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200996 VRB(session, "Capability for yang-library support not found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +0200997 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200998
999 /* get information about server's schemas from capabilities list until we will have yang-library */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001000 if (build_schema_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
Michal Vasko05532772021-06-03 12:12:38 +02001001 ERR(session, "Unable to get server's schema information from the <hello>'s capabilities.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001002 goto cleanup;
1003 }
1004
Michal Vasko086311b2016-01-08 09:53:11 +01001005 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001006 if (get_schema_support && lys_parse_mem(session->ctx, ietf_netconf_monitoring_2010_10_04_yang, LYS_IN_YANG, NULL)) {
Michal Vasko05532772021-06-03 12:12:38 +02001007 WRN(session, "Loading NETCONF monitoring schema failed, cannot use <get-schema>.");
Michal Vasko8a4e1462020-05-07 11:32:31 +02001008 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001009 }
1010
1011 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001012 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001013 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001014 }
1015
Radek Krejci65ef6d52018-08-16 16:35:02 +02001016 /* get correct version of ietf-yang-library into context */
1017 if (yanglib_support) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001018 /* use get schema to get server's ietf-yang-library */
1019 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1020 if (!revision) {
Michal Vasko05532772021-06-03 12:12:38 +02001021 WRN(session, "Loading NETCONF ietf-yang-library schema failed, missing revision in NETCONF <hello> message.");
1022 WRN(session, "Unable to automatically use <get-schema>.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001023 yanglib_support = 0;
1024 } else {
1025 revision = strndup(&revision[9], 10);
Michal Vasko0d877f92020-04-02 13:52:43 +02001026 if (nc_ctx_load_module(session, "ietf-yang-library", revision, server_modules, old_clb, old_data,
1027 get_schema_support, &mod)) {
Michal Vasko05532772021-06-03 12:12:38 +02001028 WRN(session, "Loading NETCONF ietf-yang-library schema failed, unable to use it to learn all "
1029 "the supported modules.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001030 yanglib_support = 0;
1031 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001032 if (strcmp(revision, "2019-01-04") >= 0) {
1033 /* we also need ietf-datastores to be implemented */
1034 if (nc_ctx_load_module(session, "ietf-datastores", NULL, server_modules, old_clb, old_data,
1035 get_schema_support, &mod)) {
Michal Vasko05532772021-06-03 12:12:38 +02001036 WRN(session, "Loading NETCONF ietf-datastores schema failed, unable to use yang-library "
1037 "to learn all the supported modules.");
Michal Vasko0d877f92020-04-02 13:52:43 +02001038 yanglib_support = 0;
1039 }
1040 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001041 free(revision);
Michal Vasko77367452021-02-16 16:32:18 +01001042
1043 /* ietf-netconf-nmda is needed to issue get-data */
1044 if (nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, server_modules, old_clb, old_data,
1045 get_schema_support, &mod)) {
Michal Vasko05532772021-06-03 12:12:38 +02001046 WRN(session, "Loading NETCONF ietf-netconf-nmda schema failed, unable to use get-data to retrieve "
1047 "yang-library data.");
Michal Vasko77367452021-02-16 16:32:18 +01001048 yanglib_support = 0;
1049 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001050 }
1051 }
1052
Radek Krejci65ef6d52018-08-16 16:35:02 +02001053 /* prepare structured information about server's schemas */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001054 if (yanglib_support) {
Radek Krejcif0633792018-08-20 15:12:09 +02001055 if (build_schema_info_yl(session, &sm)) {
1056 goto cleanup;
1057 } else if (!sm) {
Michal Vasko05532772021-06-03 12:12:38 +02001058 VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
Radek Krejcif0633792018-08-20 15:12:09 +02001059 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001060 /* prefer yang-library information, currently we have it from capabilities used for getting correct
1061 * yang-library schema */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001062 free_schema_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001063 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001064 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001065 }
Michal Vaskoef578332016-01-25 13:20:09 +01001066
Radek Krejci65ef6d52018-08-16 16:35:02 +02001067 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1068 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001069 }
1070
Radek Krejcifd5b6682017-06-13 15:52:53 +02001071 /* succsess */
1072 ret = 0;
1073
Michal Vaskoeee99412016-11-21 10:19:43 +01001074 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko05532772021-06-03 12:12:38 +02001075 WRN(session, "Some models failed to be loaded, any data from these models (and any other unknown) will "
1076 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001077 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001078
1079cleanup:
Radek Krejci65ef6d52018-08-16 16:35:02 +02001080 free_schema_info(server_modules);
1081
Radek Krejcifd5b6682017-06-13 15:52:53 +02001082 /* set user callback back */
1083 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001084 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001085
Michal Vaskoef578332016-01-25 13:20:09 +01001086 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001087}
1088
1089API struct nc_session *
1090nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1091{
Michal Vaskod083db62016-01-19 10:31:29 +01001092 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001093
Michal Vasko45e53ae2016-04-07 11:46:03 +02001094 if (fdin < 0) {
1095 ERRARG("fdin");
1096 return NULL;
1097 } else if (fdout < 0) {
1098 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001099 return NULL;
1100 }
1101
1102 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001103 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001104 if (!session) {
1105 ERRMEM;
1106 return NULL;
1107 }
1108 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001109
1110 /* transport specific data */
1111 session->ti_type = NC_TI_FD;
1112 session->ti.fd.in = fdin;
1113 session->ti.fd.out = fdout;
1114
Robin Jarry4e38e292019-10-15 17:14:14 +02001115 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1116 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001117 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001118 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001119
1120 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001121 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001122 goto fail;
1123 }
1124 session->status = NC_STATUS_RUNNING;
1125
Michal Vaskoef578332016-01-25 13:20:09 +01001126 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001127 goto fail;
1128 }
1129
1130 return session;
1131
1132fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001133 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001134 return NULL;
1135}
1136
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001137API struct nc_session *
1138nc_connect_unix(const char *address, struct ly_ctx *ctx)
1139{
1140 struct nc_session *session = NULL;
1141 struct sockaddr_un sun;
1142 const struct passwd *pw;
1143 char *username;
1144 int sock = -1;
1145
1146 if (address == NULL) {
1147 ERRARG("address");
1148 return NULL;
1149 }
1150
1151 pw = getpwuid(geteuid());
1152 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001153 ERR(NULL, "Failed to find username for euid=%u.\n", geteuid());
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001154 goto fail;
1155 }
1156
1157 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1158 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001159 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001160 goto fail;
1161 }
1162
1163 memset(&sun, 0, sizeof(sun));
1164 sun.sun_family = AF_UNIX;
1165 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1166
1167 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001168 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001169 goto fail;
1170 }
1171
1172 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001173 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001174 goto fail;
1175 }
1176
1177 /* prepare session structure */
1178 session = nc_new_session(NC_CLIENT, 0);
1179 if (!session) {
1180 ERRMEM;
1181 goto fail;
1182 }
1183 session->status = NC_STATUS_STARTING;
1184
1185 /* transport specific data */
1186 session->ti_type = NC_TI_UNIX;
1187 session->ti.unixsock.sock = sock;
1188 sock = -1; /* do not close sock in fail label anymore */
1189
Robin Jarry4e38e292019-10-15 17:14:14 +02001190 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1191 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001192 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001193 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001194
Michal Vasko77367452021-02-16 16:32:18 +01001195 lydict_insert(ctx, address, 0, &session->path);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001196
1197 username = strdup(pw->pw_name);
1198 if (username == NULL) {
1199 ERRMEM;
1200 goto fail;
1201 }
Michal Vasko77367452021-02-16 16:32:18 +01001202 lydict_insert_zc(ctx, username, &session->username);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001203
1204 /* NETCONF handshake */
1205 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1206 goto fail;
1207 }
1208 session->status = NC_STATUS_RUNNING;
1209
1210 if (nc_ctx_check_and_fill(session) == -1) {
1211 goto fail;
1212 }
1213
1214 return session;
1215
1216fail:
1217 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001218 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001219 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001220 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001221 return NULL;
1222}
1223
Frank Rimpler9f838b02018-07-25 06:44:03 +00001224/*
1225 Helper for a non-blocking connect (which is required because of the locking
1226 concept for e.g. call home settings). For more details see nc_sock_connect().
1227 */
1228static int
Michal Vasko5e8d0192019-06-24 19:19:49 +02001229_non_blocking_connect(int timeout, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001230{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001231 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001232 int sock = -1;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001233 fd_set wset;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001234 struct timeval ts;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001235 socklen_t len = sizeof(int);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001236 struct in_addr *addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001237 uint16_t port;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001238 char str[INET6_ADDRSTRLEN];
Michal Vasko086311b2016-01-08 09:53:11 +01001239
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001240 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001241 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001242 sock = *sock_pending;
1243 } else {
1244 assert(res);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001245 if (res->ai_family == AF_INET6) {
1246 addr = (struct in_addr *) &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001247 port = ntohs(((struct sockaddr_in6 *)res->ai_addr)->sin6_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001248 } else {
1249 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001250 port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001251 }
1252 if (!inet_ntop(res->ai_family, addr, str, res->ai_addrlen)) {
Michal Vasko05532772021-06-03 12:12:38 +02001253 WRN(NULL, "inet_ntop() failed (%s).", strerror(errno));
Michal Vasko5e8d0192019-06-24 19:19:49 +02001254 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001255 VRB(NULL, "Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001256 }
1257
1258 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001259 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1260 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001261 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001262 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001263 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001264 /* make the socket non-blocking */
1265 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001266 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001267 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001268 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001269 /* non-blocking connect! */
1270 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1271 if (errno != EINPROGRESS) {
1272 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001273 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001274 goto cleanup;
1275 }
1276 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001277 }
1278 ts.tv_sec = timeout;
1279 ts.tv_usec = 0;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001280
Frank Rimpler9f838b02018-07-25 06:44:03 +00001281 FD_ZERO(&wset);
1282 FD_SET(sock, &wset);
1283
1284 if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout != -1) ? &ts : NULL)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001285 ERR(NULL, "select() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001286 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001287 }
1288
Michal Vasko5e8d0192019-06-24 19:19:49 +02001289 if (ret == 0) {
1290 /* there was a timeout */
Michal Vasko05532772021-06-03 12:12:38 +02001291 VRB(NULL, "Timed out after %ds (%s).", timeout, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001292 if (sock_pending) {
1293 /* no sock-close, we'll try it again */
1294 *sock_pending = sock;
1295 } else {
1296 close(sock);
1297 }
1298 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001299 }
Radek Krejci782041a2018-08-20 10:09:45 +02001300
1301 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001302 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001303 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001304 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001305 goto cleanup;
1306 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001307 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001308 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001309 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001310 errno = error;
1311 goto cleanup;
1312 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001313
1314 /* enable keep-alive */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001315 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001316 goto cleanup;
1317 }
1318
Michal Vasko086311b2016-01-08 09:53:11 +01001319 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001320
Frank Rimpler9f838b02018-07-25 06:44:03 +00001321cleanup:
1322 if (sock_pending) {
1323 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001324 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001325 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001326 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001327}
1328
Frank Rimpler9f838b02018-07-25 06:44:03 +00001329/* A given timeout value limits the time how long the function blocks. If it has to block
1330 only for some seconds, a socket connection might not yet have been fully established.
1331 Therefore the active (pending) socket will be stored in *sock_pending, but the return
1332 value will be -1. In such a case a subsequent invokation is required, by providing the
1333 stored sock_pending, again.
1334 In general, if this function returns -1, when a timeout has been given, this function
1335 has to be invoked, until it returns a valid socket.
1336 */
1337int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001338nc_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 +00001339{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001340 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001341 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001342 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001343 char *buf, port_s[6]; /* length of string representation of short int */
Michal Vasko66032bc2019-01-22 15:03:12 +01001344 void *addr;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001345
Michal Vasko05532772021-06-03 12:12:38 +02001346 DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001347
1348 /* no pending socket */
1349 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001350 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001351 snprintf(port_s, 6, "%u", port);
1352 memset(&hints, 0, sizeof hints);
1353 hints.ai_family = AF_UNSPEC;
1354 hints.ai_socktype = SOCK_STREAM;
1355 hints.ai_protocol = IPPROTO_TCP;
1356 i = getaddrinfo(host, port_s, &hints, &res_list);
1357 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001358 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001359 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001360 }
1361
1362 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001363 sock = _non_blocking_connect(timeout, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001364 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001365 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001366 /* try the next resource */
1367 continue;
1368 } else {
1369 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001370 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001371 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001372 }
Michal Vasko05532772021-06-03 12:12:38 +02001373 VRB(NULL, "Successfully connected to %s:%s over %s.", host, port_s, (res->ai_family == AF_INET6) ? "IPv6" : "IPv4");
Michal Vasko83ad17e2019-01-30 10:11:37 +01001374
1375 opt = 1;
1376 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001377 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001378 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001379 }
1380
Michal Vasko66032bc2019-01-22 15:03:12 +01001381 if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) {
1382 buf = malloc(INET6_ADDRSTRLEN);
1383 if (!buf) {
1384 ERRMEM;
Michal Vaskocb846632019-12-13 15:12:45 +01001385 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001386 }
1387 if (res->ai_family == AF_INET) {
1388 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1389 } else {
1390 addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1391 }
1392 if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +02001393 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
Michal Vasko66032bc2019-01-22 15:03:12 +01001394 free(buf);
Michal Vaskocb846632019-12-13 15:12:45 +01001395 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001396 }
1397
1398 *ip_host = buf;
1399 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001400 break;
1401 }
1402 freeaddrinfo(res_list);
1403
1404 } else {
1405 /* try to get a connection with the pending socket */
1406 assert(sock_pending);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001407 sock = _non_blocking_connect(timeout, sock_pending, NULL, ka);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001408 }
1409
1410 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001411
1412error:
Michal Vasko0be85692021-03-02 08:04:57 +01001413 if (res_list) {
1414 freeaddrinfo(res_list);
1415 }
Michal Vaskocb846632019-12-13 15:12:45 +01001416 if (sock != -1) {
1417 close(sock);
1418 }
1419 if (sock_pending) {
1420 *sock_pending = -1;
1421 }
1422 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001423}
1424
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001425#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001426
Michal Vasko3031aae2016-01-27 16:07:18 +01001427int
1428nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1429{
1430 int sock;
1431
Michal Vasko45e53ae2016-04-07 11:46:03 +02001432 if (!address) {
1433 ERRARG("address");
1434 return -1;
1435 } else if (!port) {
1436 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001437 return -1;
1438 }
1439
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001440 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001441 if (sock == -1) {
1442 return -1;
1443 }
1444
1445 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001446 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1447 if (!client_opts.ch_binds) {
1448 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001449 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001450 return -1;
1451 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001452
Michal Vasko2e6defd2016-10-07 15:48:15 +02001453 client_opts.ch_bind_ti = nc_realloc(client_opts.ch_bind_ti, client_opts.ch_bind_count * sizeof *client_opts.ch_bind_ti);
1454 if (!client_opts.ch_bind_ti) {
1455 ERRMEM;
1456 close(sock);
1457 return -1;
1458 }
1459 client_opts.ch_bind_ti[client_opts.ch_bind_count - 1] = ti;
1460
Michal Vasko3031aae2016-01-27 16:07:18 +01001461 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001462 if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) {
1463 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001464 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001465 return -1;
1466 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001467 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1468 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001469 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001470
1471 return 0;
1472}
1473
1474int
1475nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1476{
1477 uint32_t i;
1478 int ret = -1;
1479
1480 if (!address && !port && !ti) {
1481 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1482 close(client_opts.ch_binds[i].sock);
1483 free((char *)client_opts.ch_binds[i].address);
1484
1485 ret = 0;
1486 }
1487 free(client_opts.ch_binds);
1488 client_opts.ch_binds = NULL;
1489 client_opts.ch_bind_count = 0;
1490 } else {
1491 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001492 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1493 (!port || (client_opts.ch_binds[i].port == port)) &&
1494 (!ti || (client_opts.ch_bind_ti[i] == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001495 close(client_opts.ch_binds[i].sock);
1496 free((char *)client_opts.ch_binds[i].address);
1497
1498 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001499 if (!client_opts.ch_bind_count) {
1500 free(client_opts.ch_binds);
1501 client_opts.ch_binds = NULL;
1502 } else if (i < client_opts.ch_bind_count) {
1503 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds);
1504 client_opts.ch_bind_ti[i] = client_opts.ch_bind_ti[client_opts.ch_bind_count];
1505 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001506
1507 ret = 0;
1508 }
1509 }
1510 }
1511
1512 return ret;
1513}
1514
1515API int
1516nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1517{
1518 int sock;
1519 char *host = NULL;
1520 uint16_t port, idx;
1521
Michal Vasko45e53ae2016-04-07 11:46:03 +02001522 if (!client_opts.ch_binds) {
1523 ERRINIT;
1524 return -1;
1525 } else if (!session) {
1526 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001527 return -1;
1528 }
1529
1530 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
1531
Michal Vasko50456e82016-02-02 12:16:08 +01001532 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001533 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001534 return sock;
1535 }
1536
Radek Krejci53691be2016-02-22 13:58:37 +01001537#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001538 if (client_opts.ch_bind_ti[idx] == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001539 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001540 } else
1541#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001542#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001543 if (client_opts.ch_bind_ti[idx] == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001544 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001545 } else
1546#endif
1547 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001548 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001549 *session = NULL;
1550 }
1551
1552 free(host);
1553
1554 if (!(*session)) {
1555 return -1;
1556 }
1557
1558 return 1;
1559}
1560
Radek Krejci53691be2016-02-22 13:58:37 +01001561#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001562
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001563API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001564nc_session_get_cpblts(const struct nc_session *session)
1565{
1566 if (!session) {
1567 ERRARG("session");
1568 return NULL;
1569 }
1570
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001571 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001572}
1573
1574API const char *
1575nc_session_cpblt(const struct nc_session *session, const char *capab)
1576{
1577 int i, len;
1578
1579 if (!session) {
1580 ERRARG("session");
1581 return NULL;
1582 } else if (!capab) {
1583 ERRARG("capab");
1584 return NULL;
1585 }
1586
1587 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001588 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1589 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1590 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001591 }
1592 }
1593
1594 return NULL;
1595}
1596
Michal Vasko9cd26a82016-05-31 08:58:48 +02001597API int
1598nc_session_ntf_thread_running(const struct nc_session *session)
1599{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001600 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001601 ERRARG("session");
1602 return 0;
1603 }
1604
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001605 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread) ? 1 : 0;
Michal Vasko9cd26a82016-05-31 08:58:48 +02001606}
1607
Michal Vaskob7558c52016-02-26 15:04:19 +01001608API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001609nc_client_init(void)
1610{
1611 nc_init();
1612}
1613
1614API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001615nc_client_destroy(void)
1616{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001617 nc_client_set_schema_searchpath(NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001618#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001619 nc_client_ch_del_bind(NULL, 0, 0);
1620#endif
1621#ifdef NC_ENABLED_SSH
1622 nc_client_ssh_destroy_opts();
1623#endif
1624#ifdef NC_ENABLED_TLS
1625 nc_client_tls_destroy_opts();
1626#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001627 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001628}
1629
Michal Vasko77367452021-02-16 16:32:18 +01001630static NC_MSG_TYPE
1631recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001632{
Michal Vasko77367452021-02-16 16:32:18 +01001633 char *ptr;
1634 struct lyd_attr *attr;
1635 uint64_t cur_msgid;
1636
1637 assert(envp && !envp->schema);
1638
1639 /* find the message-id attribute */
1640 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1641 if (!strcmp(attr->name.name, "message-id")) {
1642 break;
1643 }
1644 }
1645
1646 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02001647 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01001648 return NC_MSG_REPLY_ERR_MSGID;
1649 }
1650
1651 cur_msgid = strtoul(attr->value, &ptr, 10);
1652 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02001653 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1654 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01001655 return NC_MSG_REPLY_ERR_MSGID;
1656 }
1657
1658 return NC_MSG_REPLY;
1659}
1660
1661static NC_MSG_TYPE
1662recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
1663{
1664 int r;
1665 LY_ERR lyrc;
1666 struct ly_in *msg = NULL;
1667 struct nc_msg_cont *cont, **cont_ptr;
1668 NC_MSG_TYPE ret = NC_MSG_ERROR;
1669
1670 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
1671
1672 *envp = NULL;
1673
1674 /* try to get rpc-reply from the session's queue */
1675 while (session->opts.client.replies) {
1676 cont = session->opts.client.replies;
1677 session->opts.client.replies = cont->next;
1678
1679 msg = cont->msg;
1680 free(cont);
1681
1682 /* parse */
1683 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
1684 if (!lyrc) {
1685 ret = recv_reply_check_msgid(session, *envp, msgid);
1686 goto cleanup;
1687 } else if (lyrc != LY_ENOT) {
1688 lyd_free_tree(*envp);
1689 *envp = NULL;
Michal Vasko05532772021-06-03 12:12:38 +02001690 ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op)));
Michal Vasko77367452021-02-16 16:32:18 +01001691 goto cleanup;
1692 } else {
1693 /* it was not a notification so it is nothing known */
Michal Vasko05532772021-06-03 12:12:38 +02001694 ERR(session, "Received an unexpected message.");
Michal Vasko77367452021-02-16 16:32:18 +01001695 }
1696
1697 /* try the next message */
1698 ly_in_free(msg, 1);
1699 msg = NULL;
1700 }
1701
1702 /* read message from wire */
1703 r = nc_read_msg_poll_io(session, timeout, &msg);
1704 if (!r) {
1705 ret = NC_MSG_WOULDBLOCK;
1706 goto cleanup;
1707 } else if (r == -1) {
1708 goto cleanup;
1709 }
1710
1711 /* parse */
1712 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
1713 if (!lyrc) {
1714 ret = recv_reply_check_msgid(session, *envp, msgid);
1715 goto cleanup;
1716 } else if (lyrc != LY_ENOT) {
1717 lyd_free_tree(*envp);
1718 *envp = NULL;
Michal Vasko05532772021-06-03 12:12:38 +02001719 ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op)));
Michal Vasko77367452021-02-16 16:32:18 +01001720 goto cleanup;
1721 }
1722
Michal Vaskoc5ea6382021-03-15 16:24:08 +01001723 /* assume a notification, reset and store it */
1724 ly_in_reset(msg);
Michal Vasko77367452021-02-16 16:32:18 +01001725 cont_ptr = &session->opts.client.notifs;
1726 while (*cont_ptr) {
1727 cont_ptr = &((*cont_ptr)->next);
1728 }
1729 *cont_ptr = malloc(sizeof **cont_ptr);
1730 if (!*cont_ptr) {
1731 ERRMEM;
1732 goto cleanup;
1733 }
1734 (*cont_ptr)->msg = msg;
Michal Vaskoc5ea6382021-03-15 16:24:08 +01001735 msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001736 (*cont_ptr)->next = NULL;
1737
1738 ret = NC_MSG_NOTIF;
1739
1740cleanup:
1741 ly_in_free(msg, 1);
1742 return ret;
1743}
1744
1745static int
1746recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
1747{
1748 LY_ERR lyrc;
1749 struct nc_rpc_act_generic *rpc_gen;
1750 struct ly_in *in;
1751 struct lyd_node *tree, *op2;
1752 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01001753 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001754
1755 switch (rpc->type) {
1756 case NC_RPC_ACT_GENERIC:
1757 rpc_gen = (struct nc_rpc_act_generic *)rpc;
1758 if (rpc_gen->has_data) {
1759 tree = rpc_gen->content.data;
1760
1761 /* find the operation node */
1762 lyrc = LY_EINVAL;
1763 LYD_TREE_DFS_BEGIN(tree, op2) {
1764 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
1765 lyrc = lyd_dup_single(op2, NULL, 0, op);
1766 break;
1767 }
1768 LYD_TREE_DFS_END(tree, op2);
1769 }
1770 } else {
1771 ly_in_new_memory(rpc_gen->content.xml_str, &in);
1772 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
1773 ly_in_free(in, 0);
1774 if (lyrc) {
1775 return -1;
1776 }
1777
1778 /* we want just the operation node */
1779 lyrc = lyd_dup_single(op2, NULL, 0, op);
1780
1781 lyd_free_tree(tree);
1782 }
1783 break;
1784 case NC_RPC_GETCONFIG:
1785 module_name = "ietf-netconf";
1786 rpc_name = "get-config";
1787 break;
1788 case NC_RPC_EDIT:
1789 module_name = "ietf-netconf";
1790 rpc_name = "edit-config";
1791 break;
1792 case NC_RPC_COPY:
1793 module_name = "ietf-netconf";
1794 rpc_name = "copy-config";
1795 break;
1796 case NC_RPC_DELETE:
1797 module_name = "ietf-netconf";
1798 rpc_name = "delete-config";
1799 break;
1800 case NC_RPC_LOCK:
1801 module_name = "ietf-netconf";
1802 rpc_name = "lock";
1803 break;
1804 case NC_RPC_UNLOCK:
1805 module_name = "ietf-netconf";
1806 rpc_name = "unlock";
1807 break;
1808 case NC_RPC_GET:
1809 module_name = "ietf-netconf";
1810 rpc_name = "get";
1811 break;
1812 case NC_RPC_KILL:
1813 module_name = "ietf-netconf";
1814 rpc_name = "kill-session";
1815 break;
1816 case NC_RPC_COMMIT:
1817 module_name = "ietf-netconf";
1818 rpc_name = "commit";
1819 break;
1820 case NC_RPC_DISCARD:
1821 module_name = "ietf-netconf";
1822 rpc_name = "discard-changes";
1823 break;
1824 case NC_RPC_CANCEL:
1825 module_name = "ietf-netconf";
1826 rpc_name = "cancel-commit";
1827 break;
1828 case NC_RPC_VALIDATE:
1829 module_name = "ietf-netconf";
1830 rpc_name = "validate";
1831 break;
1832 case NC_RPC_GETSCHEMA:
1833 module_name = "ietf-netconf-monitoring";
1834 rpc_name = "get-schema";
1835 break;
1836 case NC_RPC_SUBSCRIBE:
1837 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02001838 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01001839 break;
1840 case NC_RPC_GETDATA:
1841 module_name = "ietf-netconf-nmda";
1842 rpc_name = "get-data";
1843 break;
1844 case NC_RPC_EDITDATA:
1845 module_name = "ietf-netconf-nmda";
1846 rpc_name = "edit-data";
1847 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01001848 case NC_RPC_ESTABLISHSUB:
1849 module_name = "ietf-subscribed-notifications";
1850 rpc_name = "establish-subscription";
1851 break;
1852 case NC_RPC_MODIFYSUB:
1853 module_name = "ietf-subscribed-notifications";
1854 rpc_name = "modify-subscription";
1855 break;
1856 case NC_RPC_DELETESUB:
1857 module_name = "ietf-subscribed-notifications";
1858 rpc_name = "delete-subscription";
1859 break;
1860 case NC_RPC_KILLSUB:
1861 module_name = "ietf-subscribed-notifications";
1862 rpc_name = "kill-subscription";
1863 break;
Michal Vasko305faca2021-03-25 09:16:02 +01001864 case NC_RPC_ESTABLISHPUSH:
1865 module_name = "ietf-subscribed-notifications";
1866 rpc_name = "establish-subscription";
1867 module_check = "ietf-yang-push";
1868 break;
1869 case NC_RPC_MODIFYPUSH:
1870 module_name = "ietf-subscribed-notifications";
1871 rpc_name = "modify-subscription";
1872 module_check = "ietf-yang-push";
1873 break;
1874 case NC_RPC_RESYNCSUB:
1875 module_name = "ietf-yang-push";
1876 rpc_name = "resync-subscription";
1877 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01001878 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01001879 lyrc = LY_EINT;
1880 break;
1881 }
1882
1883 if (module_name && rpc_name) {
1884 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
1885 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02001886 ERR(session, "Missing \"%s\" schema in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01001887 return -1;
1888 }
1889
1890 /* create the operation node */
1891 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
1892 }
Michal Vasko305faca2021-03-25 09:16:02 +01001893 if (module_check) {
1894 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko05532772021-06-03 12:12:38 +02001895 ERR(session, "Missing \"%s\" schema in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01001896 return -1;
1897 }
1898 }
Michal Vasko77367452021-02-16 16:32:18 +01001899
1900 if (lyrc) {
1901 return -1;
1902 }
1903 return 0;
1904}
1905
1906API NC_MSG_TYPE
1907nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
1908 struct lyd_node **op)
1909{
1910 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001911
Michal Vasko45e53ae2016-04-07 11:46:03 +02001912 if (!session) {
1913 ERRARG("session");
1914 return NC_MSG_ERROR;
1915 } else if (!rpc) {
1916 ERRARG("rpc");
1917 return NC_MSG_ERROR;
Michal Vasko5a2c7162020-01-30 11:24:17 +01001918 } else if (!msgid) {
1919 ERRARG("msgid");
1920 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001921 } else if (!envp) {
1922 ERRARG("envp");
Michal Vasko45e53ae2016-04-07 11:46:03 +02001923 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001924 } else if (!op) {
1925 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01001926 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001927 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02001928 ERR(session, "Invalid session to receive RPC replies.");
Michal Vasko086311b2016-01-08 09:53:11 +01001929 return NC_MSG_ERROR;
1930 }
Michal Vasko77367452021-02-16 16:32:18 +01001931
1932 /* get a duplicate of the RPC node to append reply to */
1933 if (recv_reply_dup_rpc(session, rpc, op)) {
1934 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01001935 }
Michal Vasko086311b2016-01-08 09:53:11 +01001936
Michal Vasko77367452021-02-16 16:32:18 +01001937 /* receive a reply */
1938 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01001939
Michal Vasko77367452021-02-16 16:32:18 +01001940 /* do not return the RPC copy on error or if the reply includes no data */
1941 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
1942 lyd_free_tree(*op);
1943 *op = NULL;
1944 }
1945 return ret;
1946}
1947
1948static NC_MSG_TYPE
1949recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
1950{
1951 int r;
1952 LY_ERR lyrc;
1953 struct ly_in *msg = NULL;
1954 struct nc_msg_cont *cont, **cont_ptr;
1955 NC_MSG_TYPE ret = NC_MSG_ERROR;
1956
1957 *op = NULL;
1958 *envp = NULL;
1959
1960 /* try to get notification from the session's queue */
1961 while (session->opts.client.notifs) {
1962 cont = session->opts.client.notifs;
1963 session->opts.client.notifs = cont->next;
1964
1965 msg = cont->msg;
1966 free(cont);
1967
1968 /* parse */
1969 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
1970 if (!lyrc) {
1971 ret = NC_MSG_NOTIF;
1972 goto cleanup;
1973 } else if (lyrc != LY_ENOT) {
1974 lyd_free_tree(*envp);
1975 *envp = NULL;
Michal Vasko05532772021-06-03 12:12:38 +02001976 ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx));
Michal Vasko77367452021-02-16 16:32:18 +01001977 goto cleanup;
1978 } else {
1979 /* it was not a rpc-reply so it is nothing known */
Michal Vasko05532772021-06-03 12:12:38 +02001980 ERR(session, "Received an unexpected message.");
Michal Vasko086311b2016-01-08 09:53:11 +01001981 }
Michal Vasko086311b2016-01-08 09:53:11 +01001982
Michal Vasko77367452021-02-16 16:32:18 +01001983 /* try the next message */
1984 ly_in_free(msg, 1);
1985 msg = NULL;
1986 }
1987
1988 /* read message from wire */
1989 r = nc_read_msg_poll_io(session, timeout, &msg);
1990 if (!r) {
1991 ret = NC_MSG_WOULDBLOCK;
1992 goto cleanup;
1993 } else if (r == -1) {
1994 goto cleanup;
1995 }
1996
1997 /* parse */
1998 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
1999 if (!lyrc) {
2000 ret = NC_MSG_NOTIF;
2001 goto cleanup;
2002 } else if (lyrc != LY_ENOT) {
2003 lyd_free_tree(*envp);
2004 *envp = NULL;
Michal Vasko05532772021-06-03 12:12:38 +02002005 ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx));
Michal Vasko77367452021-02-16 16:32:18 +01002006 goto cleanup;
2007 }
2008
Michal Vaskoc5ea6382021-03-15 16:24:08 +01002009 /* assume a rpc-reply, reset and store it */
2010 ly_in_reset(msg);
Michal Vasko77367452021-02-16 16:32:18 +01002011 cont_ptr = &session->opts.client.replies;
2012 while (*cont_ptr) {
2013 cont_ptr = &((*cont_ptr)->next);
2014 }
2015 *cont_ptr = malloc(sizeof **cont_ptr);
2016 if (!*cont_ptr) {
2017 ERRMEM;
2018 goto cleanup;
2019 }
2020 (*cont_ptr)->msg = msg;
Michal Vaskoc5ea6382021-03-15 16:24:08 +01002021 msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002022 (*cont_ptr)->next = NULL;
2023
2024 ret = NC_MSG_REPLY;
2025
2026cleanup:
2027 ly_in_free(msg, 1);
2028 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002029}
2030
2031API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002032nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002033{
Michal Vasko45e53ae2016-04-07 11:46:03 +02002034 if (!session) {
2035 ERRARG("session");
2036 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002037 } else if (!envp) {
2038 ERRARG("envp");
2039 return NC_MSG_ERROR;
2040 } else if (!op) {
2041 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002042 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002043 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002044 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002045 return NC_MSG_ERROR;
2046 }
2047
Michal Vasko77367452021-02-16 16:32:18 +01002048 /* receive a notification */
2049 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002050}
2051
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002052static void *
2053nc_recv_notif_thread(void *arg)
2054{
2055 struct nc_ntf_thread_arg *ntarg;
2056 struct nc_session *session;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002057
Michal Vasko77367452021-02-16 16:32:18 +01002058 void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op);
2059 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002060 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002061
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002062 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002063 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002064
2065 ntarg = (struct nc_ntf_thread_arg *)arg;
2066 session = ntarg->session;
2067 notif_clb = ntarg->notif_clb;
2068 free(ntarg);
2069
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002070 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread) == 1) {
Michal Vasko77367452021-02-16 16:32:18 +01002071 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002072 if (msgtype == NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002073 notif_clb(session, envp, op);
2074 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2075 lyd_free_tree(envp);
2076 lyd_free_tree(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002077 break;
2078 }
Michal Vasko77367452021-02-16 16:32:18 +01002079 lyd_free_tree(envp);
2080 lyd_free_tree(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002081 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002082 /* quit this thread once the session is broken */
2083 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002084 }
2085
2086 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2087 }
2088
Michal Vasko05532772021-06-03 12:12:38 +02002089 VRB(session, "Notification thread exit.");
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002090 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002091 return NULL;
2092}
2093
2094API int
Michal Vasko77367452021-02-16 16:32:18 +01002095nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session,
2096 const struct lyd_node *envp, const struct lyd_node *op))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002097{
2098 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002099 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002100 int ret;
2101
Michal Vasko45e53ae2016-04-07 11:46:03 +02002102 if (!session) {
2103 ERRARG("session");
2104 return -1;
2105 } else if (!notif_clb) {
2106 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002107 return -1;
2108 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002109 ERR(session, "Invalid session to receive Notifications.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002110 return -1;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002111 } else if (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread)) {
Michal Vasko05532772021-06-03 12:12:38 +02002112 ERR(session, "Separate notification thread is already running.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002113 return -1;
2114 }
2115
2116 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002117 if (!ntarg) {
2118 ERRMEM;
2119 return -1;
2120 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002121 ntarg->session = session;
2122 ntarg->notif_clb = notif_clb;
2123
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002124 /* just so that nc_recv_notif_thread() does not immediately exit */
2125 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002126
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002127 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002128 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002129 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002130 free(ntarg);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002131 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002132 return -1;
2133 }
2134
2135 return 0;
2136}
2137
Michal Vasko77367452021-02-16 16:32:18 +01002138static const char *
2139nc_wd2str(NC_WD_MODE wd)
2140{
2141 switch (wd) {
2142 case NC_WD_ALL:
2143 return "report-all";
2144 case NC_WD_ALL_TAG:
2145 return "report-all-tagged";
2146 case NC_WD_TRIM:
2147 return "trim";
2148 case NC_WD_EXPLICIT:
2149 return "explicit";
2150 default:
2151 break;
2152 }
2153
2154 return NULL;
2155}
2156
Michal Vasko086311b2016-01-08 09:53:11 +01002157API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002158nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002159{
2160 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002161 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002162 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002163 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002164 struct nc_rpc_getconfig *rpc_gc;
2165 struct nc_rpc_edit *rpc_e;
2166 struct nc_rpc_copy *rpc_cp;
2167 struct nc_rpc_delete *rpc_del;
2168 struct nc_rpc_lock *rpc_lock;
2169 struct nc_rpc_get *rpc_g;
2170 struct nc_rpc_kill *rpc_k;
2171 struct nc_rpc_commit *rpc_com;
2172 struct nc_rpc_cancel *rpc_can;
2173 struct nc_rpc_validate *rpc_val;
2174 struct nc_rpc_getschema *rpc_gs;
2175 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002176 struct nc_rpc_getdata *rpc_getd;
2177 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002178 struct nc_rpc_establishsub *rpc_estsub;
2179 struct nc_rpc_modifysub *rpc_modsub;
2180 struct nc_rpc_deletesub *rpc_delsub;
2181 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002182 struct nc_rpc_establishpush *rpc_estpush;
2183 struct nc_rpc_modifypush *rpc_modpush;
2184 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002185 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002186 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002187 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002188 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002189 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002190 uint64_t cur_msgid;
2191
Michal Vasko45e53ae2016-04-07 11:46:03 +02002192 if (!session) {
2193 ERRARG("session");
2194 return NC_MSG_ERROR;
2195 } else if (!rpc) {
2196 ERRARG("rpc");
2197 return NC_MSG_ERROR;
2198 } else if (!msgid) {
2199 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002200 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002201 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002202 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002203 return NC_MSG_ERROR;
2204 }
2205
Michal Vaskoc1171a42019-11-05 12:06:46 +01002206 switch (rpc->type) {
2207 case NC_RPC_ACT_GENERIC:
2208 /* checked when parsing */
2209 break;
2210 case NC_RPC_GETCONFIG:
2211 case NC_RPC_EDIT:
2212 case NC_RPC_COPY:
2213 case NC_RPC_DELETE:
2214 case NC_RPC_LOCK:
2215 case NC_RPC_UNLOCK:
2216 case NC_RPC_GET:
2217 case NC_RPC_KILL:
2218 case NC_RPC_COMMIT:
2219 case NC_RPC_DISCARD:
2220 case NC_RPC_CANCEL:
2221 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002222 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002223 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002224 ERR(session, "Missing \"ietf-netconf\" schema in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002225 return NC_MSG_ERROR;
2226 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002227 break;
2228 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002229 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002230 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002231 ERR(session, "Missing \"ietf-netconf-monitoring\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002232 return NC_MSG_ERROR;
2233 }
2234 break;
2235 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002236 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002237 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002238 ERR(session, "Missing \"notifications\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002239 return NC_MSG_ERROR;
2240 }
2241 break;
2242 case NC_RPC_GETDATA:
2243 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002244 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002245 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002246 ERR(session, "Missing \"ietf-netconf-nmda\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002247 return NC_MSG_ERROR;
2248 }
2249 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002250 case NC_RPC_ESTABLISHSUB:
2251 case NC_RPC_MODIFYSUB:
2252 case NC_RPC_DELETESUB:
2253 case NC_RPC_KILLSUB:
2254 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2255 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002256 ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002257 return NC_MSG_ERROR;
2258 }
2259 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002260 case NC_RPC_ESTABLISHPUSH:
2261 case NC_RPC_MODIFYPUSH:
2262 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2263 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002264 ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002265 return NC_MSG_ERROR;
2266 }
2267 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2268 if (!mod2) {
Michal Vasko05532772021-06-03 12:12:38 +02002269 ERR(session, "Missing \"ietf-yang-push\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002270 return NC_MSG_ERROR;
2271 }
2272 break;
2273 case NC_RPC_RESYNCSUB:
2274 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2275 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002276 ERR(session, "Missing \"ietf-yang-push\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002277 return NC_MSG_ERROR;
2278 }
2279 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002280 case NC_RPC_UNKNOWN:
2281 ERRINT;
2282 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002283 }
2284
Michal Vaskoab9deb62021-05-27 11:37:00 +02002285#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2286
Michal Vasko086311b2016-01-08 09:53:11 +01002287 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002288 case NC_RPC_ACT_GENERIC:
2289 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002290
2291 if (rpc_gen->has_data) {
2292 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002293 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002294 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002295 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2296 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2297 ly_in_free(in, 0);
2298 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002299 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002300 }
Michal Vasko086311b2016-01-08 09:53:11 +01002301 }
2302 break;
2303
2304 case NC_RPC_GETCONFIG:
2305 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2306
Michal Vaskoab9deb62021-05-27 11:37:00 +02002307 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2308 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2309 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002310 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002311 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002312 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node));
2313 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002314 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002315 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2316 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2317 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002318 }
2319 }
2320
2321 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002322 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002323 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002324 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002325 lyrc = LY_ENOTFOUND;
2326 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002327 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002328 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 +01002329 }
2330 break;
2331
2332 case NC_RPC_EDIT:
2333 rpc_e = (struct nc_rpc_edit *)rpc;
2334
Michal Vaskoab9deb62021-05-27 11:37:00 +02002335 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2336 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2337 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002338
2339 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002340 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 +01002341 }
Michal Vasko086311b2016-01-08 09:53:11 +01002342 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002343 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 +01002344 }
Michal Vasko086311b2016-01-08 09:53:11 +01002345 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002346 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 +01002347 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002348 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002349 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 +01002350 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002351 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002352 }
2353 break;
2354
2355 case NC_RPC_COPY:
2356 rpc_cp = (struct nc_rpc_copy *)rpc;
2357
Michal Vaskoab9deb62021-05-27 11:37:00 +02002358 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2359 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002360 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002361 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002362 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002363 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002364 }
2365
Michal Vaskoab9deb62021-05-27 11:37:00 +02002366 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002367 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002368 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002369 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 +01002370 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002371 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002372 }
2373 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002374 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002375 }
2376
2377 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002378 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002379 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002380 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002381 lyrc = LY_ENOTFOUND;
2382 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002383 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002384 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 +01002385 }
2386 break;
2387
2388 case NC_RPC_DELETE:
2389 rpc_del = (struct nc_rpc_delete *)rpc;
2390
Michal Vaskoab9deb62021-05-27 11:37:00 +02002391 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2392 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002393 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002394 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002395 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002396 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002397 }
2398 break;
2399
2400 case NC_RPC_LOCK:
2401 rpc_lock = (struct nc_rpc_lock *)rpc;
2402
Michal Vaskoab9deb62021-05-27 11:37:00 +02002403 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2404 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2405 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002406 break;
2407
2408 case NC_RPC_UNLOCK:
2409 rpc_lock = (struct nc_rpc_lock *)rpc;
2410
Michal Vaskoab9deb62021-05-27 11:37:00 +02002411 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2412 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2413 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002414 break;
2415
2416 case NC_RPC_GET:
2417 rpc_g = (struct nc_rpc_get *)rpc;
2418
Michal Vaskoab9deb62021-05-27 11:37:00 +02002419 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002420 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002421 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002422 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node));
2423 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002424 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002425 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2426 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2427 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002428 }
2429 }
2430
2431 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002432 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002433 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002434 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002435 lyrc = LY_ENOTFOUND;
2436 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002437 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002438 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 +01002439 }
2440 break;
2441
2442 case NC_RPC_KILL:
2443 rpc_k = (struct nc_rpc_kill *)rpc;
2444
Michal Vaskoab9deb62021-05-27 11:37:00 +02002445 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002446 sprintf(str, "%u", rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002447 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002448 break;
2449
2450 case NC_RPC_COMMIT:
2451 rpc_com = (struct nc_rpc_commit *)rpc;
2452
Michal Vaskoab9deb62021-05-27 11:37:00 +02002453 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002454 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002455 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002456 }
2457
2458 if (rpc_com->confirm_timeout) {
2459 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002460 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002461 }
Michal Vasko086311b2016-01-08 09:53:11 +01002462 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002463 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002464 }
Michal Vasko086311b2016-01-08 09:53:11 +01002465 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002466 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002467 }
2468 break;
2469
2470 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002471 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002472 break;
2473
2474 case NC_RPC_CANCEL:
2475 rpc_can = (struct nc_rpc_cancel *)rpc;
2476
Michal Vaskoab9deb62021-05-27 11:37:00 +02002477 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002478 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002479 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002480 }
2481 break;
2482
2483 case NC_RPC_VALIDATE:
2484 rpc_val = (struct nc_rpc_validate *)rpc;
2485
Michal Vaskoab9deb62021-05-27 11:37:00 +02002486 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2487 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002488 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002489 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002490 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 +01002491 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002492 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002493 }
2494 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002495 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002496 }
2497 break;
2498
2499 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002500 rpc_gs = (struct nc_rpc_getschema *)rpc;
2501
Michal Vaskoab9deb62021-05-27 11:37:00 +02002502 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2503 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002504 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002505 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002506 }
2507 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002508 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002509 }
2510 break;
2511
2512 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002513 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2514
Michal Vaskoab9deb62021-05-27 11:37:00 +02002515 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002516 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002517 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002518 }
2519
2520 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002521 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002522 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node));
2523 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002524 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002525 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2526 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2527 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002528 }
2529 }
Michal Vasko086311b2016-01-08 09:53:11 +01002530 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002531 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002532 }
Michal Vasko086311b2016-01-08 09:53:11 +01002533 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002534 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002535 }
2536 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002537
2538 case NC_RPC_GETDATA:
2539 rpc_getd = (struct nc_rpc_getdata *)rpc;
2540
Michal Vaskoab9deb62021-05-27 11:37:00 +02002541 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2542 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2543
Michal Vaskoc1171a42019-11-05 12:06:46 +01002544 if (rpc_getd->filter) {
2545 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002546 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 +01002547 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002548 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002549 }
2550 }
2551 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002552 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002553 }
2554 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002555 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2556 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002557 }
2558 if (rpc_getd->max_depth) {
2559 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002560 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002561 }
2562 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002563 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002564 }
2565
2566 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002567 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002568 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 +01002569 }
2570 break;
2571
2572 case NC_RPC_EDITDATA:
2573 rpc_editd = (struct nc_rpc_editdata *)rpc;
2574
Michal Vaskoab9deb62021-05-27 11:37:00 +02002575 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2576 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002577
2578 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002579 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2580 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002581 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002582 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002583 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 +01002584 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002585 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002586 }
2587 break;
2588
Michal Vasko96f247a2021-03-15 13:32:10 +01002589 case NC_RPC_ESTABLISHSUB:
2590 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2591
Michal Vaskoab9deb62021-05-27 11:37:00 +02002592 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002593
2594 if (rpc_estsub->filter) {
2595 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002596 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML,
2597 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002598 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002599 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002600 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002601 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002602 }
2603 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002604 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002605
2606 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002607 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002608 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002609 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002610 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002611 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002612 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002613 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002614 }
2615 break;
2616
2617 case NC_RPC_MODIFYSUB:
2618 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2619
Michal Vaskoab9deb62021-05-27 11:37:00 +02002620 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002621
2622 sprintf(str, "%u", rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002623 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002624
2625 if (rpc_modsub->filter) {
2626 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002627 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML,
2628 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002629 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002630 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002631 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002632 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002633 }
2634 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002635 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002636 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002637 }
2638 break;
2639
2640 case NC_RPC_DELETESUB:
2641 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
2642
Michal Vaskoab9deb62021-05-27 11:37:00 +02002643 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002644
2645 sprintf(str, "%u", rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002646 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002647 break;
2648
2649 case NC_RPC_KILLSUB:
2650 rpc_killsub = (struct nc_rpc_killsub *)rpc;
2651
Michal Vaskoab9deb62021-05-27 11:37:00 +02002652 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002653
2654 sprintf(str, "%u", rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002655 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002656 break;
2657
Michal Vasko305faca2021-03-25 09:16:02 +01002658 case NC_RPC_ESTABLISHPUSH:
2659 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
2660
Michal Vaskoab9deb62021-05-27 11:37:00 +02002661 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
2662 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002663
2664 if (rpc_estpush->filter) {
2665 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002666 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0,
2667 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002668 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002669 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002670 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002671 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002672 }
2673 }
2674
2675 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002676 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002677 }
Michal Vasko305faca2021-03-25 09:16:02 +01002678 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002679 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002680 }
2681
2682 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002683 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002684 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002685 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002686 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002687 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002688 }
2689 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002690 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002691 if (rpc_estpush->dampening_period) {
2692 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002693 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002694 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002695 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
2696 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002697 if (rpc_estpush->excluded_change) {
2698 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002699 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
2700 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002701 }
2702 }
2703 }
2704 break;
2705
2706 case NC_RPC_MODIFYPUSH:
2707 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
2708
Michal Vaskoab9deb62021-05-27 11:37:00 +02002709 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002710
2711 sprintf(str, "%u", rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002712 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
2713 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002714
2715 if (rpc_modpush->filter) {
2716 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002717 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0,
2718 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002719 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002720 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002721 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002722 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002723 }
2724 }
Michal Vasko305faca2021-03-25 09:16:02 +01002725 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002726 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002727 }
2728
2729 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002730 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002731 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002732 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002733 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002734 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002735 }
2736 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002737 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002738 if (rpc_modpush->dampening_period) {
2739 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002740 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002741 }
2742 }
2743 break;
2744
2745 case NC_RPC_RESYNCSUB:
2746 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
2747
Michal Vaskoab9deb62021-05-27 11:37:00 +02002748 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002749 sprintf(str, "%u", rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002750 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002751 break;
2752
Michal Vasko96f247a2021-03-15 13:32:10 +01002753 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002754 ERRINT;
2755 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002756 }
2757
Michal Vaskoab9deb62021-05-27 11:37:00 +02002758#undef CHECK_LYRC_BREAK
2759
2760 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02002761 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002762 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02002763 return NC_MSG_ERROR;
2764 }
2765
Michal Vasko131120a2018-05-29 15:44:02 +02002766 /* send RPC, store its message ID */
2767 r = nc_send_msg_io(session, timeout, data);
2768 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002769
Radek Krejcib4b19062018-02-07 16:33:06 +01002770 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01002771 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01002772 }
Michal Vasko086311b2016-01-08 09:53:11 +01002773
Michal Vasko131120a2018-05-29 15:44:02 +02002774 if (r == NC_MSG_RPC) {
2775 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002776 }
Michal Vasko131120a2018-05-29 15:44:02 +02002777 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01002778}
Michal Vaskode2946c2017-01-12 12:19:26 +01002779
2780API void
2781nc_client_session_set_not_strict(struct nc_session *session)
2782{
2783 if (session->side != NC_CLIENT) {
2784 ERRARG("session");
2785 return;
2786 }
2787
2788 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
2789}