blob: 250164e302241adc69ae57bd2fac94d977b0bedc [file] [log] [blame]
Radek Krejci469aab82012-07-22 18:42:20 +02001/*!
2 * \file mod_netconf.c
3 * \brief NETCONF Apache modul for Netopeer
4 * \author Tomas Cejka <cejkat@cesnet.cz>
5 * \author Radek Krejci <rkrejci@cesnet.cz>
6 * \date 2011
7 * \date 2012
Tomas Cejka94da2c52013-01-08 18:20:30 +01008 * \date 2013
Radek Krejci469aab82012-07-22 18:42:20 +02009 */
10/*
Tomas Cejkad340dbf2013-03-24 20:36:57 +010011 * Copyright (C) 2011-2013 CESNET
Radek Krejci469aab82012-07-22 18:42:20 +020012 *
13 * LICENSE TERMS
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in
22 * the documentation and/or other materials provided with the
23 * distribution.
24 * 3. Neither the name of the Company nor the names of its contributors
25 * may be used to endorse or promote products derived from this
26 * software without specific prior written permission.
27 *
28 * ALTERNATIVELY, provided that this notice is retained in full, this
29 * product may be distributed under the terms of the GNU General Public
30 * License (GPL) version 2 or later, in which case the provisions
31 * of the GPL apply INSTEAD OF those given above.
32 *
33 * This software is provided ``as is'', and any express or implied
34 * warranties, including, but not limited to, the implied warranties of
35 * merchantability and fitness for a particular purpose are disclaimed.
36 * In no event shall the company or contributors be liable for any
37 * direct, indirect, incidental, special, exemplary, or consequential
38 * damages (including, but not limited to, procurement of substitute
39 * goods or services; loss of use, data, or profits; or business
40 * interruption) however caused and on any theory of liability, whether
41 * in contract, strict liability, or tort (including negligence or
42 * otherwise) arising in any way out of the use of this software, even
43 * if advised of the possibility of such damage.
44 *
45 */
Michal Vaskoc3146782015-11-04 14:46:41 +010046#define _GNU_SOURCE
Radek Krejci469aab82012-07-22 18:42:20 +020047
Radek Krejci7b4ddd02012-07-30 08:09:58 +020048#include <unistd.h>
49#include <poll.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010050#include <time.h>
Radek Krejci469aab82012-07-22 18:42:20 +020051#include <sys/types.h>
52#include <sys/socket.h>
53#include <sys/un.h>
Tomas Cejkad340dbf2013-03-24 20:36:57 +010054#include <sys/fcntl.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010055#include <sys/stat.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010056#include <pwd.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010057#include <errno.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010058#include <limits.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010059#include <grp.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010060#include <signal.h>
David Kupka8e60a372012-09-04 09:15:20 +020061#include <pthread.h>
62#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020063
Radek Krejci469aab82012-07-22 18:42:20 +020064#include <libnetconf.h>
Tomas Cejkab3cc64f2013-05-03 19:44:54 +020065#include <libnetconf_ssh.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020066
Tomas Cejka04e08f42014-03-27 19:52:34 +010067#include "../config.h"
68
Tomas Cejkad340dbf2013-03-24 20:36:57 +010069#ifdef WITH_NOTIFICATIONS
70#include "notification_module.h"
71#endif
72
Tomas Cejka94da2c52013-01-08 18:20:30 +010073#include "message_type.h"
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020074#include "mod_netconf.h"
Radek Krejci469aab82012-07-22 18:42:20 +020075
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010076#define SCHEMA_DIR "/tmp/yang_models"
Radek Krejci469aab82012-07-22 18:42:20 +020077#define MAX_PROCS 5
Tomas Cejka86f0fc12014-09-17 15:09:38 +020078#define SOCKET_FILENAME "/var/run/mod_netconf.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020079#define MAX_SOCKET_CL 10
80#define BUFFER_SIZE 4096
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010081#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
82#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020083
Michal Vasko7732dee2015-11-05 10:22:15 +010084/* sleep in master process for non-blocking socket reading, in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020085#define SLEEP_TIME 200
86
87#ifndef offsetof
88#define offsetof(type, member) ((size_t) ((type *) 0)->member)
89#endif
90
Tomas Cejka027f3bc2012-11-10 20:28:36 +010091/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020092struct timeval timeout = { 1, 0 };
93
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010094#define NCWITHDEFAULTS NCWD_MODE_NOTSET
Tomas Cejka5064c232013-01-17 09:30:58 +010095
Radek Krejci469aab82012-07-22 18:42:20 +020096#define MSG_OK 0
97#define MSG_OPEN 1
98#define MSG_DATA 2
99#define MSG_CLOSE 3
100#define MSG_ERROR 4
101#define MSG_UNKNOWN 5
102
Tomas Cejka47387fd2013-06-10 20:37:46 +0200103pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200104pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100105pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100106pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
107
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100108unsigned int session_key_generator = 1;
Michal Vaskoc3146782015-11-04 14:46:41 +0100109struct session_with_mutex *netconf_sessions_list = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +0100110static const char *sockname;
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200111static pthread_key_t notif_history_key;
Tomas Cejka442258e2014-04-01 18:17:18 +0200112pthread_key_t err_reply_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200113volatile int isterminated = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200114static char* password;
115
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100116json_object *create_ok_reply(void);
117json_object *create_data_reply(const char *data);
118static char *netconf_getschema(unsigned int session_key, const char *identifier, const char *version,
119 const char *format, json_object **err);
120static void node_add_metadata_recursive(struct lyd_node *data_tree, struct lys_module *module,
121 json_object *data_json_parent);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100122static void node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100123
124static void
125signal_handler(int sign)
Radek Krejci469aab82012-07-22 18:42:20 +0200126{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100127 switch (sign) {
128 case SIGINT:
129 case SIGTERM:
130 isterminated = 1;
131 break;
132 }
Radek Krejci469aab82012-07-22 18:42:20 +0200133}
134
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100135int
136netconf_callback_ssh_hostkey_check(const char* UNUSED(hostname), ssh_session UNUSED(session))
Radek Krejci469aab82012-07-22 18:42:20 +0200137{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100138 /* always approve */
139 return (EXIT_SUCCESS);
Radek Krejci469aab82012-07-22 18:42:20 +0200140}
141
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100142char *
143netconf_callback_sshauth_passphrase(const char *UNUSED(username), const char *UNUSED(hostname), const char *UNUSED(priv_key_file))
Radek Krejci469aab82012-07-22 18:42:20 +0200144{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100145 char *buf;
146 buf = strdup(password);
147 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200148}
149
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100150char *
151netconf_callback_sshauth_password(const char *UNUSED(username), const char *UNUSED(hostname))
Radek Krejci469aab82012-07-22 18:42:20 +0200152{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100153 char *buf;
154 buf = strdup(password);
155 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200156}
157
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100158char *
159netconf_callback_sshauth_interactive(const char *UNUSED(name), const char *UNUSED(instruction),
160 const char *UNUSED(prompt), int UNUSED(echo))
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200161{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100162 char *buf;
163 buf = strdup(password);
164 return (buf);
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200165}
166
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100167void
168netconf_callback_error_process(const char *UNUSED(tag),
169 const char *UNUSED(type),
170 const char *UNUSED(severity),
171 const char *UNUSED(apptag),
172 const char *UNUSED(path),
173 const char *message,
174 const char *UNUSED(attribute),
175 const char *UNUSED(element),
176 const char *UNUSED(ns),
177 const char *UNUSED(sid))
Radek Krejcic11fd862012-07-26 12:41:21 +0200178{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100179 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
180 if (err_reply_p == NULL) {
181 ERROR("Error message was not allocated. %s", __func__);
182 return;
183 }
184 json_object *err_reply = *err_reply_p;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100185
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100186 json_object *array = NULL;
187 if (err_reply == NULL) {
188 ERROR("error calback: empty error list");
189 pthread_mutex_lock(&json_lock);
190 err_reply = json_object_new_object();
191 array = json_object_new_array();
192 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
193 json_object_object_add(err_reply, "errors", array);
194 if (message != NULL) {
195 json_object_array_add(array, json_object_new_string(message));
196 }
197 pthread_mutex_unlock(&json_lock);
198 (*err_reply_p) = err_reply;
199 } else {
200 ERROR("error calback: nonempty error list");
201 pthread_mutex_lock(&json_lock);
202 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
203 if (message != NULL) {
204 json_object_array_add(array, json_object_new_string(message));
205 }
206 }
207 pthread_mutex_unlock(&json_lock);
208 }
209 pthread_setspecific(err_reply_key, err_reply_p);
210 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200211}
212
Tomas Cejka47387fd2013-06-10 20:37:46 +0200213/**
214 * should be used in locked area
215 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100216void
217prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200218{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100219 json_object *json_obj = NULL;
220 json_object *js_tmp = NULL;
221 char *old_sid = NULL;
222 const char *j_old_sid = NULL;
223 const char *cpbltstr;
224 struct nc_cpblts* cpblts = NULL;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200225
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100226 if (s == NULL) {
227 ERROR("No session given.");
228 return;
229 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200230
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100231 pthread_mutex_lock(&json_lock);
232 if (s->hello_message != NULL) {
233 ERROR("clean previous hello message");
234 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
235 j_old_sid = json_object_get_string(js_tmp);
236 if (j_old_sid != NULL) {
237 old_sid = strdup(j_old_sid);
238 }
239 }
240 json_object_put(s->hello_message);
241 s->hello_message = NULL;
242 }
243 s->hello_message = json_object_new_object();
244 if (session != NULL) {
245 if (old_sid != NULL) {
246 /* use previous sid */
247 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
248 free(old_sid);
249 old_sid = NULL;
250 } else {
251 /* we don't have old sid */
252 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
253 }
254 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
255 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
256 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
257 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
258 cpblts = nc_session_get_cpblts (session);
259 if (cpblts != NULL) {
260 json_obj = json_object_new_array();
261 nc_cpblts_iter_start (cpblts);
262 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
263 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
264 }
265 json_object_object_add(s->hello_message, "capabilities", json_obj);
266 }
267 DEBUG("%s", json_object_to_json_string(s->hello_message));
268 } else {
269 ERROR("Session was not given.");
270 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
271 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
272 }
273 DEBUG("Status info from hello message prepared");
274 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200275
276}
277
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100278void
279create_err_reply_p()
Tomas Cejka442258e2014-04-01 18:17:18 +0200280{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100281 json_object **err_reply = calloc(1, sizeof(json_object **));
282 if (err_reply == NULL) {
283 ERROR("Allocation of err_reply storage failed!");
284 return;
285 }
286 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
287 ERROR("cannot set thread-specific value.");
288 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200289}
290
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100291void
292clean_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200293{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100294 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
295 if (err_reply != NULL) {
296 if (*err_reply != NULL) {
297 pthread_mutex_lock(&json_lock);
298 json_object_put(*err_reply);
299 pthread_mutex_unlock(&json_lock);
300 }
301 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
302 ERROR("Cannot set thread-specific hash value.");
303 }
304 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200305}
306
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100307void
308free_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200309{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100310 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
311 if (err_reply != NULL) {
312 if (*err_reply != NULL) {
313 pthread_mutex_lock(&json_lock);
314 json_object_put(*err_reply);
315 pthread_mutex_unlock(&json_lock);
316 }
317 free(err_reply);
318 err_reply = NULL;
319 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
320 ERROR("Cannot set thread-specific hash value.");
321 }
322 }
323}
324
325static struct session_with_mutex *
326session_get_locked(unsigned int session_key, json_object **err)
327{
328 struct session_with_mutex *locked_session;
329
330 /* get non-exclusive (read) access to sessions_list (conns) */
331 DEBUG("LOCK wrlock %s", __func__);
332 if (pthread_rwlock_rdlock(&session_lock) != 0) {
333 if (*err) {
334 *err = create_error_reply("Locking failed.");
335 }
336 return NULL;
337 }
338 /* get session where send the RPC */
339 for (locked_session = netconf_sessions_list;
340 locked_session && (locked_session->session_key != session_key);
341 locked_session = locked_session->next);
342 if (!locked_session) {
343 if (*err) {
344 *err = create_error_reply("Session not found.");
345 }
346 return NULL;
347 }
348
349 /* get exclusive access to session */
350 DEBUG("LOCK mutex %s", __func__);
351 if (pthread_mutex_lock(&locked_session->lock) != 0) {
352 if (*err) {
353 *err = create_error_reply("Locking failed.");
354 }
355 goto wrlock_fail;
356 }
357 return locked_session;
358
359wrlock_fail:
360 DEBUG("UNLOCK wrlock %s", __func__);
361 pthread_rwlock_unlock(&session_lock);
362 return NULL;
363}
364
365static void
366session_unlock(struct session_with_mutex *locked_session)
367{
368 DEBUG("UNLOCK mutex %s", __func__);
369 pthread_mutex_unlock(&locked_session->lock);
370 DEBUG("UNLOCK wrlock %s", __func__);
371 pthread_rwlock_unlock(&session_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +0200372}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200373
Michal Vasko3fda9a92015-11-23 10:10:57 +0100374static void
375node_metadata_text(const char *text, const char *name, json_object *parent)
376{
377 json_object *obj;
378
379 if (!text) {
380 return;
381 }
382
383 obj = json_object_new_string(text);
384 json_object_object_add(parent, name, obj);
385}
386
387static void
388node_metadata_restr(struct lys_restr *restr, const char *name, json_object *parent)
389{
390 json_object *obj;
391
392 if (!restr) {
393 return;
394 }
395
396 obj = json_object_new_string(restr->expr);
397 json_object_object_add(parent, name, obj);
398}
399
400static void
401node_metadata_must(uint8_t must_size, struct lys_restr *must, json_object *parent)
402{
403 uint8_t i;
404 json_object *array, *obj;
405
406 if (!must_size || !must) {
407 return;
408 }
409
410 array = json_object_new_array();
411
412 for (i = 0; i < must_size; ++i) {
413 obj = json_object_new_string(must[i].expr);
414 json_object_array_add(array, obj);
415 }
416
417 json_object_object_add(parent, "must", array);
418}
419
420static void
421node_metadata_basic(struct lys_node *node, json_object *parent)
422{
423 json_object *obj;
424
425 /* description */
426 node_metadata_text(node->dsc, "description", parent);
427
428 /* reference */
429 node_metadata_text(node->ref, "reference", parent);
430
431 /* config */
432 if (node->flags & LYS_CONFIG_R) {
433 obj = json_object_new_boolean(0);
434 } else {
435 obj = json_object_new_boolean(1);
436 }
437 json_object_object_add(parent, "config", obj);
438
439 /* status */
440 if (node->flags & LYS_STATUS_DEPRC) {
441 obj = json_object_new_string("deprecated");
442 } else if (node->flags & LYS_STATUS_OBSLT) {
443 obj = json_object_new_string("obsolete");
444 } else {
445 obj = json_object_new_string("current");
446 }
447 json_object_object_add(parent, "status", obj);
448
449 /* mandatory */
450 if (node->flags & LYS_MAND_TRUE) {
451 obj = json_object_new_boolean(1);
452 } else {
453 obj = json_object_new_boolean(0);
454 }
455 json_object_object_add(parent, "mandatory", obj);
456
457 /* NACM extensions */
458 if (node->nacm) {
459 if (node->nacm & LYS_NACM_DENYW) {
460 obj = json_object_new_string("default-deny-write");
461 } else {
462 obj = json_object_new_string("default-deny-all");
463 }
464 json_object_object_add(parent, "ext", obj);
465 }
466}
467
468static void
469node_metadata_when(struct lys_when *when, json_object *parent)
470{
471 json_object *obj;
472
473 if (!when) {
474 return;
475 }
476
477 obj = json_object_new_string(when->cond);
478 json_object_object_add(parent, "when", obj);
479}
480
481static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100482node_metadata_children_recursive(struct lys_node *node, json_object **child_array, json_object **choice_array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100483{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100484 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100485 struct lys_node *child;
486
487 if (!node->child) {
488 return;
489 }
490
491 LY_TREE_FOR(node->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100492 if (child->nodetype == LYS_USES) {
493 node_metadata_children_recursive(child, child_array, choice_array);
494 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100495 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100496 if (!*child_array) {
497 *child_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100498 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100499 json_object_array_add(*child_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100500 } else if (child->nodetype == LYS_CHOICE) {
501 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100502 if (!*choice_array) {
503 *choice_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100504 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100505 json_object_array_add(*choice_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100506 }
507 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100508}
509
510static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100511node_metadata_cases_recursive(struct lys_node_choice *choice, json_object *array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100512{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100513 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100514 struct lys_node *child;
515
516 if (!choice->child) {
517 return;
518 }
519
Michal Vasko3fda9a92015-11-23 10:10:57 +0100520 LY_TREE_FOR(choice->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100521 if (child->nodetype == LYS_USES) {
522 node_metadata_cases_recursive((struct lys_node_choice *)child, array);
523 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_CASE)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100524 obj = json_object_new_string(child->name);
525 json_object_array_add(array, obj);
526 }
527 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100528}
529
530static void
531node_metadata_min_max(uint32_t min, uint32_t max, json_object *parent)
532{
533 json_object *obj;
534
535 if (min) {
536 obj = json_object_new_int(min);
537 json_object_object_add(parent, "min-elements", obj);
538 }
539
540 if (max) {
541 obj = json_object_new_int(max);
542 json_object_object_add(parent, "max-elements", obj);
543 }
544}
545
546static void
547node_metadata_ident_recursive(struct lys_ident *ident, json_object *array)
548{
549 struct lys_ident_der *cur;
550 json_object *obj;
551
552 if (!ident) {
553 return;
554 }
555
556 obj = json_object_new_string(ident->name);
557 json_object_array_add(array, obj);
558
559 for (cur = ident->der; cur; cur = cur->next) {
560 node_metadata_ident_recursive(cur->ident, array);
561 }
562}
563
564static void
565node_metadata_type(struct lys_type *type, struct lys_module *module, json_object *parent)
566{
567 json_object *obj, *array, *item;
568 char *str;
569 int i;
570
571 /* built-in YANG type */
572 if (!type->der->module) {
573 switch (type->base) {
574 case LY_TYPE_BINARY:
575 node_metadata_text("binary", "type", parent);
576 node_metadata_restr(type->info.binary.length, "length", parent);
577 break;
578 case LY_TYPE_BITS:
579 node_metadata_text("bits", "type", parent);
580
581 array = json_object_new_array();
582 for (i = 0; i < type->info.bits.count; ++i) {
583 item = json_object_new_object();
584 obj = json_object_new_string(type->info.bits.bit[i].name);
585 json_object_object_add(item, "name", obj);
586 obj = json_object_new_int(type->info.bits.bit[i].pos);
587 json_object_object_add(item, "position", obj);
588 json_object_array_add(array, item);
589 }
590 json_object_object_add(parent, "bits", array);
591 break;
592 case LY_TYPE_BOOL:
593 node_metadata_text("bool", "type", parent);
594 break;
595 case LY_TYPE_DEC64:
596 node_metadata_text("decimal64", "type", parent);
597 node_metadata_restr(type->info.dec64.range, "range", parent);
598 obj = json_object_new_int(type->info.dec64.dig);
599 json_object_object_add(parent, "fraction-digits", obj);
600 break;
601 case LY_TYPE_EMPTY:
602 node_metadata_text("empty", "type", parent);
603 break;
604 case LY_TYPE_ENUM:
605 node_metadata_text("enumeration", "type", parent);
606
607 array = json_object_new_array();
608 for (i = 0; i < type->info.enums.count; ++i) {
609 obj = json_object_new_string(type->info.enums.enm[i].name);
610 json_object_array_add(array, obj);
611 }
612 json_object_object_add(parent, "enumval", array);
613 break;
614 case LY_TYPE_IDENT:
615 node_metadata_text("identityref", "type", parent);
616
617 array = json_object_new_array();
618 node_metadata_ident_recursive(type->info.ident.ref, array);
619 json_object_object_add(parent, "identityval", array);
620 break;
621 case LY_TYPE_INST:
622 node_metadata_text("instance-identifier", "type", parent);
623 if (type->info.inst.req == -1) {
624 obj = json_object_new_boolean(0);
625 } else {
626 obj = json_object_new_boolean(1);
627 }
628 json_object_object_add(parent, "require-instance", obj);
629 break;
630 case LY_TYPE_LEAFREF:
631 node_metadata_text("leafref", "type", parent);
632 node_metadata_text(type->info.lref.path, "path", parent);
633 break;
634 case LY_TYPE_STRING:
635 node_metadata_text("string", "type", parent);
636 node_metadata_restr(type->info.str.length, "length", parent);
637 if (type->info.str.pat_count) {
638 array = json_object_new_array();
639 for (i = 0; i < type->info.str.pat_count; ++i) {
640 obj = json_object_new_string(type->info.str.patterns[i].expr);
641 json_object_array_add(array, obj);
642 }
643 json_object_object_add(parent, "pattern", array);
644 }
645 break;
646 case LY_TYPE_UNION:
647 node_metadata_text("union", "type", parent);
648 array = json_object_new_array();
649 for (i = 0; i < type->info.uni.count; ++i) {
650 obj = json_object_new_object();
651 node_metadata_type(&type->info.uni.types[i], module, obj);
652 json_object_array_add(array, obj);
653 }
654 json_object_object_add(parent, "types", array);
655 break;
656 case LY_TYPE_INT8:
657 node_metadata_text("int8", "type", parent);
658 node_metadata_restr(type->info.num.range, "range", parent);
659 break;
660 case LY_TYPE_UINT8:
661 node_metadata_text("uint8", "type", parent);
662 node_metadata_restr(type->info.num.range, "range", parent);
663 break;
664 case LY_TYPE_INT16:
665 node_metadata_text("int16", "type", parent);
666 node_metadata_restr(type->info.num.range, "range", parent);
667 break;
668 case LY_TYPE_UINT16:
669 node_metadata_text("uint16", "type", parent);
670 node_metadata_restr(type->info.num.range, "range", parent);
671 break;
672 case LY_TYPE_INT32:
673 node_metadata_text("int32", "type", parent);
674 node_metadata_restr(type->info.num.range, "range", parent);
675 break;
676 case LY_TYPE_UINT32:
677 node_metadata_text("uint32", "type", parent);
678 node_metadata_restr(type->info.num.range, "range", parent);
679 break;
680 case LY_TYPE_INT64:
681 node_metadata_text("int64", "type", parent);
682 node_metadata_restr(type->info.num.range, "range", parent);
683 break;
684 case LY_TYPE_UINT64:
685 node_metadata_text("uint64", "type", parent);
686 node_metadata_restr(type->info.num.range, "range", parent);
687 break;
688 default:
689 ERROR("Internal: unknown type (%s:%d)", __FILE__, __LINE__);
690 break;
691 }
692
693 /* typedef */
694 } else {
695 if (!module || !type->module_name || !strcmp(type->module_name, module->name)) {
696 node_metadata_text(type->der->name, "type", parent);
697 } else {
698 asprintf(&str, "%s:%s", type->module_name, type->der->name);
699 node_metadata_text(str, "type", parent);
700 free(str);
701 }
702 obj = json_object_new_object();
703 node_metadata_typedef(type->der, obj);
704 json_object_object_add(parent, "typedef", obj);
705 }
706}
707
708static void
709node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent)
710{
711 json_object *obj;
712
713 /* description */
714 node_metadata_text(tpdf->dsc, "description", parent);
715
716 /* reference */
717 node_metadata_text(tpdf->ref, "reference", parent);
718
719 /* status */
720 if (tpdf->flags & LYS_STATUS_DEPRC) {
721 obj = json_object_new_string("deprecated");
722 } else if (tpdf->flags & LYS_STATUS_OBSLT) {
723 obj = json_object_new_string("obsolete");
724 } else {
725 obj = json_object_new_string("current");
726 }
727 json_object_object_add(parent, "status", obj);
728
729 /* type */
730 node_metadata_type(&tpdf->type, tpdf->module, parent);
731
732 /* units */
733 node_metadata_text(tpdf->units, "units", parent);
734
735 /* default */
736 node_metadata_text(tpdf->dflt, "default", parent);
737}
738
739static void
740node_metadata_container(struct lys_node_container *cont, json_object *parent)
741{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100742 json_object *obj, *child_array = NULL, *choice_array = NULL;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100743
744 /* element type */
745 obj = json_object_new_string("container");
746 json_object_object_add(parent, "eltype", obj);
747
748 /* shared info */
749 node_metadata_basic((struct lys_node *)cont, parent);
750
751 /* must */
752 node_metadata_must(cont->must_size, cont->must, parent);
753
754 /* presence */
755 node_metadata_text(cont->presence, "presence", parent);
756
757 /* when */
758 node_metadata_when(cont->when, parent);
759
760 /* children & choice */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100761 node_metadata_children_recursive((struct lys_node *)cont, &child_array, &choice_array);
762 if (child_array) {
763 json_object_object_add(parent, "children", child_array);
764 }
765 if (choice_array) {
766 json_object_object_add(parent, "choice", choice_array);
767 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100768}
769
770static void
771node_metadata_choice(struct lys_node_choice *choice, json_object *parent)
772{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100773 json_object *obj, *array;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100774
775 /* element type */
776 obj = json_object_new_string("choice");
777 json_object_object_add(parent, "eltype", obj);
778
779 /* shared info */
780 node_metadata_basic((struct lys_node *)choice, parent);
781
782 /* default */
783 node_metadata_text(choice->dflt->name, "default", parent);
784
785 /* when */
786 node_metadata_when(choice->when, parent);
787
788 /* cases */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100789 if (choice->child) {
790 array = json_object_new_array();
791 node_metadata_cases_recursive(choice, array);
792 json_object_object_add(parent, "cases", array);
793 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100794}
795
796static void
797node_metadata_leaf(struct lys_node_leaf *leaf, json_object *parent)
798{
799 json_object *obj;
800 struct lys_node_list *list;
801 int is_key, i;
802
803 /* element type */
804 obj = json_object_new_string("leaf");
805 json_object_object_add(parent, "eltype", obj);
806
807 /* shared info */
808 node_metadata_basic((struct lys_node *)leaf, parent);
809
810 /* type */
811 node_metadata_type(&leaf->type, leaf->module, parent);
812
813 /* units */
814 node_metadata_text(leaf->units, "units", parent);
815
816 /* default */
817 node_metadata_text(leaf->dflt, "default", parent);
818
819 /* must */
820 node_metadata_must(leaf->must_size, leaf->must, parent);
821
822 /* when */
823 node_metadata_when(leaf->when, parent);
824
825 /* iskey */
826 is_key = 0;
827 list = (struct lys_node_list *)lys_parent((struct lys_node *)leaf);
828 if (list && (list->nodetype == LYS_LIST)) {
829 for (i = 0; i < list->keys_size; ++i) {
830 if (list->keys[i] == leaf) {
831 is_key = 1;
832 break;
833 }
834 }
835 }
836 obj = json_object_new_boolean(is_key);
837 json_object_object_add(parent, "iskey", obj);
838}
839
840static void
841node_metadata_leaflist(struct lys_node_leaflist *llist, json_object *parent)
842{
843 json_object *obj;
844
845 /* element type */
846 obj = json_object_new_string("leaf-list");
847 json_object_object_add(parent, "eltype", obj);
848
849 /* shared info */
850 node_metadata_basic((struct lys_node *)llist, parent);
851
852 /* type */
853 node_metadata_type(&llist->type, llist->module, parent);
854
855 /* units */
856 node_metadata_text(llist->units, "units", parent);
857
858 /* must */
859 node_metadata_must(llist->must_size, llist->must, parent);
860
861 /* when */
862 node_metadata_when(llist->when, parent);
863
864 /* min/max-elements */
865 node_metadata_min_max(llist->min, llist->max, parent);
866}
867
868static void
869node_metadata_list(struct lys_node_list *list, json_object *parent)
870{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100871 json_object *obj, *array, *child_array = NULL, *choice_array = NULL;;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100872 int i;
873 unsigned int j;
874
875 /* element type */
876 obj = json_object_new_string("list");
877 json_object_object_add(parent, "eltype", obj);
878
879 /* shared info */
880 node_metadata_basic((struct lys_node *)list, parent);
881
882 /* must */
883 node_metadata_must(list->must_size, list->must, parent);
884
885 /* when */
886 node_metadata_when(list->when, parent);
887
888 /* min/max-elements */
889 node_metadata_min_max(list->min, list->max, parent);
890
891 /* keys */
892 if (list->keys_size) {
893 array = json_object_new_array();
894 for (i = 0; i < list->keys_size; ++i) {
895 obj = json_object_new_string(list->keys[i]->name);
896 json_object_array_add(array, obj);
897 }
898 json_object_object_add(parent, "keys", array);
899 }
900
901 /* unique */
902 if (list->unique_size) {
903 array = json_object_new_array();
904 for (i = 0; i < list->unique_size; ++i) {
905 for (j = 0; j < list->unique[i].expr_size; ++j) {
906 obj = json_object_new_string(list->unique[i].expr[j]);
907 json_object_array_add(array, obj);
908 }
909 }
910 json_object_object_add(parent, "unique", array);
911 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100912
913 /* children & choice */
914 node_metadata_children_recursive((struct lys_node *)list, &child_array, &choice_array);
915 if (child_array) {
916 json_object_object_add(parent, "children", child_array);
917 }
918 if (choice_array) {
919 json_object_object_add(parent, "choice", choice_array);
920 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100921}
922
923static void
924node_metadata_anyxml(struct lys_node_anyxml *anyxml, json_object *parent)
925{
926 json_object *obj;
927
928 /* element type */
929 obj = json_object_new_string("anyxml");
930 json_object_object_add(parent, "eltype", obj);
931
932 /* shared info */
933 node_metadata_basic((struct lys_node *)anyxml, parent);
934
935 /* must */
936 node_metadata_must(anyxml->must_size, anyxml->must, parent);
937
938 /* when */
939 node_metadata_when(anyxml->when, parent);
940
941}
942
943static void
944node_metadata_case(struct lys_node_case *cas, json_object *parent)
945{
946 json_object *obj;
947
948 /* element type */
949 obj = json_object_new_string("case");
950 json_object_object_add(parent, "eltype", obj);
951
952 /* shared info */
953 node_metadata_basic((struct lys_node *)cas, parent);
954
955 /* when */
956 node_metadata_when(cas->when, parent);
957}
958
Michal Vaskoa45770b2015-11-23 15:49:41 +0100959static void
960node_metadata_rpc(struct lys_node_rpc *rpc, json_object *parent)
961{
962 json_object *obj;
963
964 /* element type */
965 obj = json_object_new_string("rpc");
966 json_object_object_add(parent, "eltype", obj);
967
968 /* description */
969 node_metadata_text(rpc->dsc, "description", parent);
970
971 /* reference */
972 node_metadata_text(rpc->ref, "reference", parent);
973
974 /* status */
975 if (rpc->flags & LYS_STATUS_DEPRC) {
976 obj = json_object_new_string("deprecated");
977 } else if (rpc->flags & LYS_STATUS_OBSLT) {
978 obj = json_object_new_string("obsolete");
979 } else {
980 obj = json_object_new_string("current");
981 }
982 json_object_object_add(parent, "status", obj);
983}
984
985static void
986node_metadata_model(struct lys_module *module, json_object *parent)
987{
988 json_object *obj, *array, *item;
989 int i;
990
991 /* yang-version */
992 if (module->version == 2) {
993 obj = json_object_new_string("1.1");
994 } else {
995 obj = json_object_new_string("1.0");
996 }
997 json_object_object_add(parent, "yang-version", obj);
998
999 /* namespace */
1000 node_metadata_text(module->ns, "namespace", parent);
1001
1002 /* prefix */
1003 node_metadata_text(module->prefix, "prefix", parent);
1004
1005 /* contact */
1006 node_metadata_text(module->contact, "contact", parent);
1007
1008 /* organization */
1009 node_metadata_text(module->org, "organization", parent);
1010
1011 /* revision */
1012 if (module->rev_size) {
1013 node_metadata_text(module->rev[0].date, "revision", parent);
1014 }
1015
1016 /* description */
1017 node_metadata_text(module->dsc, "description", parent);
1018
1019 /* import */
1020 if (module->imp_size) {
1021 array = json_object_new_array();
1022 for (i = 0; i < module->imp_size; ++i) {
1023 item = json_object_new_object();
1024
1025 node_metadata_text(module->imp[i].module->name, "name", item);
1026 node_metadata_text(module->imp[i].prefix, "prefix", item);
1027 if (module->imp[i].rev && module->imp[i].rev[0]) {
1028 node_metadata_text(module->imp[i].rev, "revision", item);
1029 }
1030
1031 json_object_array_add(array, item);
1032 }
1033 json_object_object_add(parent, "imports", array);
1034 }
1035
1036 /* include */
1037 if (module->inc_size) {
1038 array = json_object_new_array();
1039 for (i = 0; i < module->inc_size; ++i) {
1040 item = json_object_new_object();
1041
1042 node_metadata_text(module->inc[i].submodule->name, "name", item);
1043 if (module->inc[i].rev && module->inc[i].rev[0]) {
1044 node_metadata_text(module->inc[i].rev, "revision", item);
1045 }
1046
1047 json_object_array_add(array, item);
1048 }
1049 json_object_object_add(parent, "includes", array);
1050 }
1051}
1052
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001053/**
1054 * \defgroup netconf_operations NETCONF operations
1055 * The list of NETCONF operations that mod_netconf supports.
1056 * @{
1057 */
1058
1059/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001060 * \brief Send RPC and wait for reply with timeout.
1061 *
1062 * \param[in] session libnetconf session
1063 * \param[in] rpc prepared RPC message
1064 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
1065 * \param[out] reply reply from the server
1066 * \return Value from nc_session_recv_reply() or NC_MSG_UNKNOWN when send_rpc() fails.
1067 * On success, it returns NC_MSG_REPLY.
1068 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001069NC_MSG_TYPE
1070netconf_send_recv_timed(struct nc_session *session, nc_rpc *rpc, int timeout, nc_reply **reply)
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001071{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001072 const nc_msgid msgid = NULL;
1073 NC_MSG_TYPE ret = NC_MSG_UNKNOWN;
1074 msgid = nc_session_send_rpc(session, rpc);
1075 if (msgid == NULL) {
1076 return ret;
1077 }
1078 do {
1079 ret = nc_session_recv_reply(session, timeout, reply);
1080 if (ret == NC_MSG_HELLO) {
1081 ERROR("<hello> received instead reply, it will be lost.");
1082 nc_reply_free(*reply);
1083 }
1084 if (ret == NC_MSG_WOULDBLOCK) {
1085 ERROR("Timeout for receiving RPC reply expired.");
1086 break;
1087 }
1088 } while (ret == NC_MSG_HELLO || ret == NC_MSG_NOTIFICATION);
1089 return ret;
1090}
1091
1092static int
1093ctx_download_module(struct session_with_mutex *session, const char *model_name, const char *revision, const char *schema_dir)
1094{
1095 json_object *err = NULL;
1096 char *model_data = NULL, *model_path;
1097 size_t length;
1098 FILE *file;
1099
1100 DEBUG("UNLOCK rwlock %s", __func__);
1101 if (pthread_rwlock_unlock(&session_lock) != 0) {
1102 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1103 return 1;
1104 }
1105
1106 model_data = netconf_getschema(session->session_key, model_name, revision, "yin", &err);
1107
1108 DEBUG("LOCK rwlock %s", __func__);
1109 if (pthread_rwlock_wrlock(&session_lock) != 0) {
1110 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1111 return 1;
1112 }
1113
1114 if (!model_data) {
1115 if (err) {
1116 json_object_put(err);
1117 }
1118 ERROR("Failed to get-schema of \"%s\".", model_name);
1119 return 1;
1120 }
1121
1122 if (revision) {
1123 asprintf(&model_path, "%s/%s@%s.yin", schema_dir, model_name, revision);
1124 } else {
1125 asprintf(&model_path, "%s/%s.yin", schema_dir, model_name);
1126 }
1127
1128 file = fopen(model_path, "w");
1129 if (!file) {
1130 ERROR("Failed to open \"%s\" for writing (%s).", model_path, strerror(errno));
1131 free(model_data);
1132 free(model_path);
1133 return 1;
1134 }
1135 free(model_path);
1136
1137 length = strlen(model_data);
1138 if (fwrite(model_data, 1, length, file) < length) {
1139 ERROR("Failed to store the model \"%s\".", model_name);
1140 free(model_data);
1141 fclose(file);
1142 return 1;
1143 }
1144
1145 free(model_data);
1146 fclose(file);
1147 return 0;
1148}
1149
1150static void
1151ctx_enable_features(struct lys_module *module, const char *cpblt)
1152{
1153 char *ptr, *ptr2, *features = NULL;
1154
1155 /* parse features */
1156 ptr = strstr(cpblt, "features=");
1157 if (ptr) {
1158 ptr += 9;
1159 ptr2 = strchr(ptr, '&');
1160 if (!ptr2) {
1161 ptr2 = ptr + strlen(ptr);
1162 }
1163 features = strndup(ptr, ptr2 - ptr);
1164 }
1165
1166 /* enable features */
1167 if (features) {
1168 /* basically manual strtok_r (to avoid macro) */
1169 ptr2 = features;
1170 for (ptr = features; *ptr; ++ptr) {
1171 if (*ptr == ',') {
1172 *ptr = '\0';
1173 /* remember last feature */
1174 ptr2 = ptr + 1;
1175 }
1176 }
1177
1178 ptr = features;
1179 lys_features_enable(module, ptr);
1180 while (ptr != ptr2) {
1181 ptr += strlen(ptr) + 1;
1182 lys_features_enable(module, ptr);
1183 }
1184
1185 free(features);
1186 }
1187}
1188
1189static void
1190ctx_enable_capabs(struct lys_module *ietfnc, json_object *cpb_array)
1191{
1192 json_object *item;
1193 int i;
1194 const char *capab;
1195
1196 /* set supported capabilities from ietf-netconf */
1197 for (i = 0; i < json_object_array_length(cpb_array); ++i) {
1198 item = json_object_array_get_idx(cpb_array, i);
1199 capab = json_object_get_string(item);
1200
1201 if (!strncmp(capab, "urn:ietf:params:netconf:capability:", 35)) {
1202 if (!strncmp(capab, "writable-running", 16)) {
1203 lys_features_enable(ietfnc, "writable-running");
1204 } else if (!strncmp(capab, "candidate", 9)) {
1205 lys_features_enable(ietfnc, "candidate");
1206 } else if (!strcmp(capab, "confirmed-commit:1.1")) {
1207 lys_features_enable(ietfnc, "confirmed-commit");
1208 } else if (!strncmp(capab, "rollback-on-error", 17)) {
1209 lys_features_enable(ietfnc, "rollback-on-error");
1210 } else if (!strcmp(capab, "validate:1.1")) {
1211 lys_features_enable(ietfnc, "validate");
1212 } else if (!strncmp(capab, "startup", 7)) {
1213 lys_features_enable(ietfnc, "startup");
1214 } else if (!strncmp(capab, "url", 3)) {
1215 lys_features_enable(ietfnc, "url");
1216 } else if (!strncmp(capab, "xpath", 5)) {
1217 lys_features_enable(ietfnc, "xpath");
1218 }
1219 }
1220 }
1221}
1222
1223static int
1224prepare_context(struct session_with_mutex *session)
1225{
1226 struct lys_module *module;
1227 json_object *array, *item;
1228 char *ptr, *ptr2;
1229 char *model_name = NULL, *revision = NULL;
1230 const char *capab;
1231 int i, get_schema_support;
1232
1233 if (json_object_object_get_ex(session->hello_message, "capabilities", &array) == FALSE) {
1234 return 1;
1235 }
1236
1237 get_schema_support = 0;
1238 for (i = 0; i < json_object_array_length(array); ++i) {
1239 item = json_object_array_get_idx(array, i);
1240 capab = json_object_get_string(item);
1241
1242 if (!strncmp(capab, "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", 51)) {
1243 get_schema_support = 1;
1244 break;
1245 }
1246 }
1247
1248 if (get_schema_support) {
1249 errno = 0;
1250 if (eaccess(SCHEMA_DIR, W_OK)) {
1251 if (errno == ENOENT) {
1252 if (mkdir(SCHEMA_DIR, 00755)) {
1253 ERROR("Failed to create temp model dir \"%s\" (%s).", SCHEMA_DIR, strerror(errno));
1254 return 1;
1255 }
1256 } else {
1257 ERROR("Unable to write to temp model dir \"%s\" (%s).", SCHEMA_DIR, strerror(errno));
1258 return 1;
1259 }
1260 }
1261
1262 session->ctx = ly_ctx_new(SCHEMA_DIR);
1263 } else {
1264 /* TODO */
1265 session->ctx = ly_ctx_new(NULL);
1266 }
1267
1268loop:
1269 /* download all the models first or load them directly */
1270 for (i = 0; i < json_object_array_length(array); ++i) {
1271 item = json_object_array_get_idx(array, i);
1272 capab = json_object_get_string(item);
1273 if (!strncmp(capab, "urn:ietf:params:netconf:capability", 34)
1274 || !strncmp(capab, "urn:ietf:params:netconf:base", 28)) {
1275 continue;
1276 }
1277
1278 /* get module */
1279 ptr = strstr(capab, "module=");
1280 if (!ptr) {
1281 ERROR("Unknown capability \"%s\" could not be parsed.", capab);
1282 continue;
1283 }
1284 ptr += 7;
1285 ptr2 = strchr(ptr, '&');
1286 if (!ptr2) {
1287 ptr2 = ptr + strlen(ptr);
1288 }
1289 model_name = strndup(ptr, ptr2 - ptr);
1290
1291 /* get revision */
1292 ptr = strstr(capab, "revision=");
1293 if (ptr) {
1294 ptr += 9;
1295 ptr2 = strchr(ptr, '&');
1296 if (!ptr2) {
1297 ptr2 = ptr + strlen(ptr);
1298 }
1299 revision = strndup(ptr, ptr2 - ptr);
1300 }
1301
1302 if (get_schema_support) {
1303 ctx_download_module(session, model_name, revision, SCHEMA_DIR);
1304 } else {
1305 module = ly_ctx_get_module(session->ctx, model_name, revision);
1306 if (!module) {
1307 module = ly_ctx_load_module(session->ctx, NULL, model_name, revision);
1308 if (module) {
1309 if (!strcmp(module->name, "ietf-netconf")) {
1310 ctx_enable_capabs(module, array);
1311 } else {
1312 ctx_enable_features(module, capab);
1313 }
1314 }
1315 }
1316 }
1317
1318 free(model_name);
1319 free(revision);
1320 revision = NULL;
1321 }
1322
1323 if (get_schema_support) {
1324 /* we have downloaded all the models, load them now */
1325 get_schema_support = 0;
1326 goto loop;
1327 }
1328
1329 return 0;
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001330}
1331
1332/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001333 * \brief Connect to NETCONF server
1334 *
1335 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
1336 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001337static unsigned int
1338netconf_connect(const char *host, const char *port, const char *user, const char *pass, struct nc_cpblts *cpblts)
Radek Krejci469aab82012-07-22 18:42:20 +02001339{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001340 struct nc_session* session = NULL;
1341 struct session_with_mutex *locked_session, *last_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001342
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001343 /* connect to the requested NETCONF server */
1344 password = (char*)pass;
1345 DEBUG("prepare to connect %s@%s:%s", user, host, port);
1346 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
1347 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +02001348
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001349 /* if connected successful, add session to the list */
1350 if (session != NULL) {
1351 if ((locked_session = calloc(1, sizeof(struct session_with_mutex))) == NULL || pthread_mutex_init (&locked_session->lock, NULL) != 0) {
1352 nc_session_free(session);
1353 session = NULL;
1354 free(locked_session);
1355 locked_session = NULL;
1356 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
1357 return 0;
1358 }
1359 locked_session->session = session;
1360 locked_session->last_activity = time(NULL);
1361 locked_session->hello_message = NULL;
1362 locked_session->closed = 0;
1363 pthread_mutex_init(&locked_session->lock, NULL);
1364 DEBUG("Before session_lock");
1365 /* get exclusive access to sessions_list (conns) */
1366 DEBUG("LOCK wrlock %s", __func__);
1367 if (pthread_rwlock_wrlock(&session_lock) != 0) {
1368 nc_session_free(session);
1369 free(locked_session);
1370 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1371 return 0;
1372 }
1373 locked_session->ntfc_subscribed = 0;
1374 DEBUG("Add connection to the list");
1375 if (!netconf_sessions_list) {
Michal Vaskoc3146782015-11-04 14:46:41 +01001376 netconf_sessions_list = locked_session;
1377 } else {
1378 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
1379 last_session->next = locked_session;
1380 locked_session->prev = last_session;
1381 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001382 DEBUG("Before session_unlock");
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001383
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001384 /* no need to lock session, noone can read it while we have wrlock */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001385
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001386 /* store information about session from hello message for future usage */
1387 prepare_status_message(locked_session, session);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001388
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001389 /* create context from the hello message cpabilities */
1390 if (prepare_context(locked_session)) {
1391 nc_session_free(session);
1392 free(locked_session);
1393 DEBUG("UNLOCK wrlock %s", __func__);
1394 pthread_rwlock_unlock(&session_lock);
1395 ERROR("Failed to prepare context");
1396 return 0;
1397 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001398
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001399 DEBUG("NETCONF session established");
1400 locked_session->session_key = session_key_generator;
1401 ++session_key_generator;
1402 if (session_key_generator == UINT_MAX) {
1403 session_key_generator = 1;
1404 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001405
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001406 /* unlock session list */
1407 DEBUG("UNLOCK wrlock %s", __func__);
1408 if (pthread_rwlock_unlock(&session_lock) != 0) {
1409 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1410 }
Radek Krejci469aab82012-07-22 18:42:20 +02001411
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001412 return locked_session->session_key;
1413 }
1414
1415 ERROR("Connection could not be established");
1416 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +02001417}
1418
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001419static int
1420close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +02001421{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001422 DEBUG("lock private lock.");
1423 DEBUG("LOCK mutex %s", __func__);
1424 if (pthread_mutex_lock(&locked_session->lock) != 0) {
1425 ERROR("Error while locking rwlock");
1426 }
1427 locked_session->ntfc_subscribed = 0;
1428 locked_session->closed = 1;
1429 if (locked_session->session != NULL) {
1430 nc_session_free(locked_session->session);
1431 locked_session->session = NULL;
1432 }
1433 DEBUG("session closed.");
1434 DEBUG("unlock private lock.");
1435 DEBUG("UNLOCK mutex %s", __func__);
1436 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
1437 ERROR("Error while locking rwlock");
1438 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001439
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001440 DEBUG("unlock session lock.");
1441 DEBUG("closed session, disabled notif(?), wait 0.5s");
1442 usleep(500000); /* let notification thread stop */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001443
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001444 /* session shouldn't be used by now */
1445 /** \todo free all notifications from queue */
1446 free(locked_session->notifications);
1447 pthread_mutex_destroy(&locked_session->lock);
1448 if (locked_session->hello_message != NULL) {
1449 json_object_put(locked_session->hello_message);
1450 locked_session->hello_message = NULL;
1451 }
1452 locked_session->session = NULL;
1453 ly_ctx_destroy(locked_session->ctx);
1454 free(locked_session);
1455 locked_session = NULL;
1456 DEBUG("NETCONF session closed, everything cleared.");
1457 return (EXIT_SUCCESS);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001458}
1459
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001460static int
1461netconf_close(unsigned int session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +02001462{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001463 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001464
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001465 DEBUG("Session to close: %u", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001466
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001467 /* get exclusive (write) access to sessions_list (conns) */
1468 DEBUG("lock session lock.");
1469 DEBUG("LOCK wrlock %s", __func__);
1470 if (pthread_rwlock_wrlock (&session_lock) != 0) {
1471 ERROR("Error while locking rwlock");
1472 (*reply) = create_error_reply("Internal: Error while locking.");
1473 return EXIT_FAILURE;
1474 }
1475 /* remove session from the active sessions list -> nobody new can now work with session */
1476 for (locked_session = netconf_sessions_list;
1477 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01001478 locked_session = locked_session->next);
1479
1480 if (!locked_session) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001481 ERROR("Could not find the session %u to close.", session_key);
1482 (*reply) = create_error_reply("Internal: Error while finding a session.");
Michal Vaskoc3146782015-11-04 14:46:41 +01001483 return EXIT_FAILURE;
1484 }
1485
1486 if (!locked_session->prev) {
1487 netconf_sessions_list = netconf_sessions_list->next;
1488 netconf_sessions_list->prev = NULL;
1489 } else {
1490 locked_session->prev->next = locked_session->next;
1491 if (locked_session->next) {
1492 locked_session->next->prev = locked_session->prev;
1493 }
1494 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001495
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001496 DEBUG("UNLOCK wrlock %s", __func__);
1497 if (pthread_rwlock_unlock (&session_lock) != 0) {
1498 ERROR("Error while unlocking rwlock");
1499 (*reply) = create_error_reply("Internal: Error while unlocking.");
1500 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001501
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001502 if ((locked_session != NULL) && (locked_session->session != NULL)) {
1503 return close_and_free_session(locked_session);
1504 } else {
1505 ERROR("Unknown session to close");
1506 (*reply) = create_error_reply("Internal: Unkown session to close.");
1507 return (EXIT_FAILURE);
1508 }
1509 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +02001510}
1511
Tomas Cejkac7929632013-10-24 19:25:15 +02001512/**
1513 * Test reply message type and return error message.
1514 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001515 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +01001516 * \param[in] session_key session ID, 0 to disable disconnect on error
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001517 * \param[in] msgt RPC-REPLY message type
Tomas Cejkac7929632013-10-24 19:25:15 +02001518 * \param[out] data
1519 * \return NULL on success
1520 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001521json_object *
1522netconf_test_reply(struct nc_session *session, unsigned int session_key, NC_MSG_TYPE msgt, nc_reply *reply, char **data)
Tomas Cejkac7929632013-10-24 19:25:15 +02001523{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001524 NC_REPLY_TYPE replyt;
1525 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001526
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001527 /* process the result of the operation */
1528 switch (msgt) {
1529 case NC_MSG_UNKNOWN:
1530 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
1531 ERROR("mod_netconf: receiving rpc-reply failed");
1532 if (session_key) {
1533 netconf_close(session_key, &err);
1534 }
1535 if (err != NULL) {
1536 return err;
1537 }
1538 return create_error_reply("Internal: Receiving RPC-REPLY failed.");
1539 }
1540 case NC_MSG_NONE:
1541 /* there is error handled by callback */
1542 if (data != NULL) {
1543 free(*data);
1544 (*data) = NULL;
1545 }
1546 return NULL;
1547 case NC_MSG_REPLY:
1548 switch (replyt = nc_reply_get_type(reply)) {
1549 case NC_REPLY_OK:
1550 if ((data != NULL) && (*data != NULL)) {
1551 free(*data);
1552 (*data) = NULL;
1553 }
1554 return create_ok_reply();
1555 case NC_REPLY_DATA:
1556 if (((*data) = nc_reply_get_data(reply)) == NULL) {
1557 ERROR("mod_netconf: no data from reply");
1558 return create_error_reply("Internal: No data from reply received.");
1559 } else {
1560 return NULL;
1561 }
1562 break;
1563 case NC_REPLY_ERROR:
1564 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
1565 if (data != NULL) {
1566 free(*data);
1567 (*data) = NULL;
1568 }
1569 return create_error_reply(nc_reply_get_errormsg(reply));
1570 default:
1571 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
1572 if (data != NULL) {
1573 free(*data);
1574 (*data) = NULL;
1575 }
1576 return create_error_reply("Unknown type of NETCONF reply.");
1577 }
1578 break;
1579 default:
1580 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
1581 if (data != NULL) {
1582 free(*data);
1583 (*data) = NULL;
1584 }
1585 return create_error_reply("Internal: Unexpected RPC-REPLY message type.");
1586 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001587}
1588
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001589json_object *
1590netconf_unlocked_op(struct nc_session *session, nc_rpc *rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001591{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001592 nc_reply* reply = NULL;
1593 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001594
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001595 /* check requests */
1596 if (rpc == NULL) {
1597 ERROR("mod_netconf: rpc is not created");
1598 return create_error_reply("Internal error: RPC is not created");
1599 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001600
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001601 if (session != NULL) {
1602 /* send the request and get the reply */
1603 msgt = netconf_send_recv_timed(session, rpc, 50000, &reply);
1604 /* process the result of the operation */
1605 return netconf_test_reply(session, 0, msgt, reply, NULL);
1606 } else {
1607 ERROR("Unknown session to process.");
1608 return create_error_reply("Internal error: Unknown session to process.");
1609 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001610}
1611
Tomas Cejkac7929632013-10-24 19:25:15 +02001612/**
1613 * Perform RPC method that returns data.
1614 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001615 * \param[in] session_id session identifier
1616 * \param[in] rpc RPC message to perform
1617 * \param[out] received_data received data string, can be NULL when no data expected, value can be set to NULL if no data received
Tomas Cejkac7929632013-10-24 19:25:15 +02001618 * \return NULL on success, json object with error otherwise
1619 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001620static json_object *
1621netconf_op(unsigned int session_key, nc_rpc *rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +02001622{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001623 struct session_with_mutex * locked_session;
1624 nc_reply* reply = NULL;
1625 json_object *res = NULL;
1626 char *data = NULL;
1627 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +02001628
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001629 /* check requests */
1630 if (rpc == NULL) {
1631 ERROR("mod_netconf: rpc is not created");
1632 res = create_error_reply("Internal: RPC could not be created.");
1633 data = NULL;
1634 goto finished;
1635 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001636
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001637 locked_session = session_get_locked(session_key, &res);
1638 if (!locked_session) {
1639 ERROR("Unknown session or locking failed.");
1640 goto finished;
1641 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001642
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001643 locked_session->last_activity = time(NULL);
Tomas Cejkac7929632013-10-24 19:25:15 +02001644
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001645 /* send the request and get the reply */
1646 msgt = netconf_send_recv_timed(locked_session->session, rpc, 2000000, &reply);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001647
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001648 session_unlock(locked_session);
Tomas Cejkac7929632013-10-24 19:25:15 +02001649
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001650 res = netconf_test_reply(locked_session->session, session_key, msgt, reply, &data);
Radek Krejcia332b692012-11-12 16:15:54 +01001651
Tomas Cejkac7929632013-10-24 19:25:15 +02001652finished:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001653 nc_reply_free(reply);
1654 if (received_data != NULL) {
1655 (*received_data) = data;
1656 } else {
1657 if (data != NULL) {
1658 free(data);
1659 data = NULL;
1660 }
1661 }
1662 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001663}
1664
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001665static char *
1666netconf_getconfig(unsigned int session_key, NC_DATASTORE source, const char *filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001667{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001668 nc_rpc* rpc;
1669 struct nc_filter *f = NULL;
1670 struct session_with_mutex *locked_session;
1671 char* data = NULL, *data_xml;
1672 json_object *res = NULL, *data_cjson;
1673 enum json_tokener_error tok_err;
1674 struct lyd_node *node, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001675
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001676 /* create filter if set */
1677 if (filter != NULL) {
1678 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
1679 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001680
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001681 /* create requests */
1682 rpc = nc_rpc_getconfig(source, f);
1683 nc_filter_free(f);
1684 if (rpc == NULL) {
1685 ERROR("mod_netconf: creating rpc request failed");
1686 return (NULL);
1687 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001688
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001689 /* tell server to show all elements even if they have default values */
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001690#ifdef HAVE_WITHDEFAULTS_TAGGED
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001691 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001692#else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001693 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET))
1694 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001695#endif
Michal Vaskoc3146782015-11-04 14:46:41 +01001696 {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001697 ERROR("mod_netconf: setting withdefaults failed");
1698 }
Tomas Cejka94674662013-09-13 15:55:24 +02001699
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001700 res = netconf_op(session_key, rpc, &data);
1701 nc_rpc_free(rpc);
1702 if (res != NULL) {
1703 (*err) = res;
1704 } else {
1705 (*err) = NULL;
1706 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001707
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001708 if (data) {
1709 for (locked_session = netconf_sessions_list;
1710 locked_session && (locked_session->session_key != session_key);
1711 locked_session = locked_session->next);
1712 /* won't fail */
1713
1714 asprintf(&data_xml, "<get-config>%s</get-config>", data);
1715 node = lyd_parse(locked_session->ctx, data_xml, LYD_XML, LYD_OPT_GETCONFIG | (strict ? LYD_OPT_STRICT : 0));
1716 free(data_xml);
1717 free(data);
1718 if (!node) {
1719 ERROR("Parsing <get-config> data failed.");
1720 return NULL;
1721 }
1722
1723 /* replace XML data with JSON data */
1724 if (lyd_print_mem(&data, node, LYD_JSON)) {
1725 ERROR("Printing JSON <get-config> data failed.");
1726 LY_TREE_FOR(node, sibling) {
1727 lyd_free(sibling);
1728 }
1729 return NULL;
1730 }
1731
1732 /* parse JSON data into cjson */
1733 pthread_mutex_lock(&json_lock);
1734 data_cjson = json_tokener_parse_verbose(data, &tok_err);
1735 if (!data_cjson) {
1736 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1737 pthread_mutex_unlock(&json_lock);
1738 LY_TREE_FOR(node, sibling) {
1739 lyd_free(sibling);
1740 }
1741 free(data);
1742 return NULL;
1743 }
1744 free(data);
1745
1746 /* go simultaneously through both trees and add metadata */
1747 LY_TREE_FOR_SAFE(node, next, sibling) {
1748 node_add_metadata_recursive(sibling, NULL, data_cjson);
1749 lyd_free(sibling);
1750 }
1751
1752 data = strdup(json_object_to_json_string_ext(data_cjson, 0));
1753 json_object_put(data_cjson);
1754 pthread_mutex_unlock(&json_lock);
1755 }
1756
1757 return (data);
Radek Krejci8e4632a2012-07-26 13:40:34 +02001758}
1759
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001760static char *
1761netconf_getschema(unsigned int session_key, const char *identifier, const char *version, const char *format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001762{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001763 nc_rpc* rpc;
1764 char* data = NULL;
1765 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001766
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001767 /* create requests */
1768 rpc = nc_rpc_getschema(identifier, version, format);
1769 if (rpc == NULL) {
1770 ERROR("mod_netconf: creating rpc request failed");
1771 return (NULL);
1772 }
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001773
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001774 res = netconf_op(session_key, rpc, &data);
1775 nc_rpc_free (rpc);
1776 if (res != NULL) {
1777 (*err) = res;
1778 } else {
1779 (*err) = NULL;
1780 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001781
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001782 return (data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001783}
1784
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001785static char *
1786netconf_get(unsigned int session_key, const char* filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001787{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001788 nc_rpc* rpc;
1789 struct nc_filter *f = NULL;
1790 char* data = NULL, *data_xml;
1791 json_object *res = NULL, *data_cjson;
1792 enum json_tokener_error tok_err;
1793 struct session_with_mutex *locked_session;
1794 struct lyd_node *node, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001795
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001796 /* create filter if set */
1797 if (filter != NULL) {
1798 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
1799 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001800
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001801 /* create requests */
1802 rpc = nc_rpc_get(f);
1803 nc_filter_free(f);
1804 if (rpc == NULL) {
1805 ERROR("mod_netconf: creating rpc request failed");
1806 return (NULL);
1807 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001808
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001809 /* tell server to show all elements even if they have default values */
1810 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
1811 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
1812 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
1813 ERROR("mod_netconf: setting withdefaults failed");
1814 }
Tomas Cejka94674662013-09-13 15:55:24 +02001815
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001816 res = netconf_op(session_key, rpc, &data);
1817 nc_rpc_free(rpc);
1818 if (res != NULL) {
1819 (*err) = res;
1820 } else {
1821 (*err) = NULL;
1822 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001823
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001824 if (data) {
1825 for (locked_session = netconf_sessions_list;
1826 locked_session && (locked_session->session_key != session_key);
1827 locked_session = locked_session->next);
1828 /* won't fail */
1829
1830 asprintf(&data_xml, "<get>%s</get>", data);
1831 node = lyd_parse(locked_session->ctx, data_xml, LYD_XML, LYD_OPT_GET | (strict ? LYD_OPT_STRICT : 0));
1832 free(data_xml);
1833 free(data);
1834 if (!node) {
1835 ERROR("Parsing <get> data failed.");
1836 return NULL;
1837 }
1838
1839 /* replace XML data with JSON data */
1840 if (lyd_print_mem(&data, node, LYD_JSON)) {
1841 ERROR("Printing JSON <get> data failed.");
1842 LY_TREE_FOR(node, sibling) {
1843 lyd_free(sibling);
1844 }
1845 return NULL;
1846 }
1847
1848 /* parse JSON data into cjson */
1849 pthread_mutex_lock(&json_lock);
1850 data_cjson = json_tokener_parse_verbose(data, &tok_err);
1851 if (!data_cjson) {
1852 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1853 pthread_mutex_unlock(&json_lock);
1854 LY_TREE_FOR(node, sibling) {
1855 lyd_free(sibling);
1856 }
1857 free(data);
1858 return NULL;
1859 }
1860 free(data);
1861
1862 /* go simultaneously through both trees and add metadata */
1863 LY_TREE_FOR_SAFE(node, next, sibling) {
1864 node_add_metadata_recursive(sibling, NULL, data_cjson);
1865 lyd_free(sibling);
1866 }
1867
1868 data = strdup(json_object_to_json_string_ext(data_cjson, 0));
1869 json_object_put(data_cjson);
1870 pthread_mutex_unlock(&json_lock);
1871 }
1872
1873 return data;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001874}
1875
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001876static json_object *
1877netconf_copyconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, const char *config,
1878 const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001879{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001880 nc_rpc* rpc;
1881 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001882
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001883 /* create requests */
1884 if (source == NC_DATASTORE_CONFIG) {
1885 if (target == NC_DATASTORE_URL) {
1886 /* config, url */
1887 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
1888 } else {
1889 /* config, datastore */
1890 rpc = nc_rpc_copyconfig(source, target, config);
1891 }
1892 } else if (source == NC_DATASTORE_URL) {
1893 if (target == NC_DATASTORE_URL) {
1894 /* url, url */
1895 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
1896 } else {
1897 /* url, datastore */
1898 rpc = nc_rpc_copyconfig(source, target, uri_src);
1899 }
1900 } else {
1901 if (target == NC_DATASTORE_URL) {
1902 /* datastore, url */
1903 rpc = nc_rpc_copyconfig(source, target, uri_trg);
1904 } else {
1905 /* datastore, datastore */
1906 rpc = nc_rpc_copyconfig(source, target);
1907 }
1908 }
1909 if (rpc == NULL) {
1910 ERROR("mod_netconf: creating rpc request failed");
1911 return create_error_reply("Internal: Creating rpc request failed");
1912 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001913
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001914 res = netconf_op(session_key, rpc, NULL);
1915 nc_rpc_free(rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001916
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001917 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001918}
Radek Krejci035bf4e2012-07-25 10:59:09 +02001919
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001920static json_object *
1921netconf_editconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, NC_EDIT_DEFOP_TYPE defop,
1922 NC_EDIT_ERROPT_TYPE erropt, NC_EDIT_TESTOPT_TYPE testopt, const char *config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +02001923{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001924 nc_rpc* rpc;
1925 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001926
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001927 /* create requests */
1928 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
1929 if (rpc == NULL) {
1930 ERROR("mod_netconf: creating rpc request failed");
1931 return create_error_reply("Internal: Creating rpc request failed");
1932 }
Radek Krejci62ab34b2012-07-26 13:42:05 +02001933
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001934 res = netconf_op(session_key, rpc, NULL);
1935 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001936
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001937 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001938}
1939
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001940static json_object *
1941netconf_killsession(unsigned int session_key, const char *sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001942{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001943 nc_rpc *rpc;
1944 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001945
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001946 /* create requests */
1947 rpc = nc_rpc_killsession(sid);
1948 if (rpc == NULL) {
1949 ERROR("mod_netconf: creating rpc request failed");
1950 return create_error_reply("Internal: Creating rpc request failed");
1951 }
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001952
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001953 res = netconf_op(session_key, rpc, NULL);
1954 nc_rpc_free(rpc);
1955 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001956}
1957
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001958static json_object *
1959netconf_onlytargetop(unsigned int session_key, NC_DATASTORE target, nc_rpc *(*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +02001960{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001961 nc_rpc* rpc;
1962 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +02001963
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001964 /* create requests */
1965 rpc = op_func(target);
1966 if (rpc == NULL) {
1967 ERROR("mod_netconf: creating rpc request failed");
1968 return create_error_reply("Internal: Creating rpc request failed");
1969 }
Radek Krejci2f318372012-07-26 14:22:35 +02001970
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001971 res = netconf_op(session_key, rpc, NULL);
1972 nc_rpc_free (rpc);
1973 return res;
Radek Krejci2f318372012-07-26 14:22:35 +02001974}
1975
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001976static json_object *
1977netconf_deleteconfig(unsigned int session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001978{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001979 nc_rpc *rpc = NULL;
1980 json_object *res = NULL;
1981 if (target != NC_DATASTORE_URL) {
1982 rpc = nc_rpc_deleteconfig(target);
1983 } else {
1984 rpc = nc_rpc_deleteconfig(target, url);
1985 }
1986 if (rpc == NULL) {
1987 ERROR("mod_netconf: creating rpc request failed");
1988 return create_error_reply("Internal: Creating rpc request failed");
1989 }
Tomas Cejka404d37e2013-04-13 02:31:35 +02001990
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001991 res = netconf_op(session_key, rpc, NULL);
1992 nc_rpc_free (rpc);
1993 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +02001994}
1995
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001996static json_object *
1997netconf_lock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001998{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001999 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02002000}
2001
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002002static json_object *
2003netconf_unlock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02002004{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002005 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02002006}
2007
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002008static json_object *
2009netconf_generic(unsigned int session_key, const char *content, char **data)
Radek Krejci80c10d92012-07-30 08:38:50 +02002010{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002011 nc_rpc* rpc = NULL;
2012 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +02002013
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002014 /* create requests */
2015 rpc = nc_rpc_generic(content);
2016 if (rpc == NULL) {
2017 ERROR("mod_netconf: creating rpc request failed");
2018 return create_error_reply("Internal: Creating rpc request failed");
2019 }
Radek Krejci80c10d92012-07-30 08:38:50 +02002020
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002021 if (data != NULL) {
2022 // TODO ?free(*data);
2023 (*data) = NULL;
2024 }
Radek Krejci80c10d92012-07-30 08:38:50 +02002025
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002026 /* get session where send the RPC */
2027 res = netconf_op(session_key, rpc, data);
2028 nc_rpc_free (rpc);
2029 return res;
2030}
2031
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002032static int
2033node_add_metadata(struct lys_node *node, struct lys_module *module, json_object *parent)
2034{
2035 struct lys_module *cur_module;
2036 json_object *meta_obj;
2037 char *obj_name;
2038
Michal Vaskoa45770b2015-11-23 15:49:41 +01002039 if (node->nodetype == LYS_INPUT) {
2040 /* silently skipped */
2041 return 0;
2042 }
2043
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002044 cur_module = node->module;
2045 if (cur_module->type) {
2046 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
2047 }
2048 if (cur_module == module) {
2049 asprintf(&obj_name, "$@%s", node->name);
2050 } else {
2051 asprintf(&obj_name, "$@%s:%s", cur_module->name, node->name);
2052 }
2053
2054 /* in (leaf-)lists the metadata could have already been added */
Michal Vaskoa45770b2015-11-23 15:49:41 +01002055 if ((node->nodetype & (LYS_LEAFLIST | LYS_LIST)) && (json_object_object_get_ex(parent, obj_name, NULL) == TRUE)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002056 free(obj_name);
2057 return 1;
2058 }
2059
2060 meta_obj = json_object_new_object();
2061
2062 switch (node->nodetype) {
2063 case LYS_CONTAINER:
2064 node_metadata_container((struct lys_node_container *)node, meta_obj);
2065 break;
Michal Vasko3fda9a92015-11-23 10:10:57 +01002066 case LYS_CHOICE:
2067 node_metadata_choice((struct lys_node_choice *)node, meta_obj);
2068 break;
2069 case LYS_LEAF:
2070 node_metadata_leaf((struct lys_node_leaf *)node, meta_obj);
2071 break;
2072 case LYS_LEAFLIST:
2073 node_metadata_leaflist((struct lys_node_leaflist *)node, meta_obj);
2074 break;
2075 case LYS_LIST:
2076 node_metadata_list((struct lys_node_list *)node, meta_obj);
2077 break;
2078 case LYS_ANYXML:
2079 node_metadata_anyxml((struct lys_node_anyxml *)node, meta_obj);
2080 break;
2081 case LYS_CASE:
2082 node_metadata_case((struct lys_node_case *)node, meta_obj);
2083 break;
Michal Vaskoa45770b2015-11-23 15:49:41 +01002084 case LYS_RPC:
2085 node_metadata_rpc((struct lys_node_rpc *)node, meta_obj);
2086 break;
2087 default: /* LYS_OUTPUT */
Michal Vasko3fda9a92015-11-23 10:10:57 +01002088 ERROR("Internal: unuxpected nodetype (%s:%d)", __FILE__, __LINE__);
2089 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002090 }
2091
2092 /* just a precaution */
2093 if (json_object_get_type(parent) != json_type_object) {
2094 ERROR("Internal: wrong JSON type (%s:%d)", __FILE__, __LINE__);
2095 free(obj_name);
2096 return 1;
2097 }
2098
2099 json_object_object_add(parent, obj_name, meta_obj);
2100 free(obj_name);
2101 return 0;
2102}
2103
2104static void
2105node_add_metadata_recursive(struct lyd_node *data_tree, struct lys_module *module, json_object *data_json_parent)
2106{
2107 struct lys_module *cur_module;
2108 struct lys_node *list_schema;
2109 struct lyd_node *child, *list_item;
2110 json_object *child_json, *list_child_json;
2111 char *child_name;
2112 int list_idx;
2113
Michal Vaskoa45770b2015-11-23 15:49:41 +01002114 if (data_tree->schema->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
2115 return;
2116 }
2117
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002118 /* add data_tree metadata */
2119 if (node_add_metadata(data_tree->schema, module, data_json_parent)) {
2120 return;
2121 }
2122
2123 /* get data_tree module */
2124 cur_module = data_tree->schema->module;
2125 if (cur_module->type) {
2126 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
2127 }
2128
2129 if (!(data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
2130 /* print correct data_tree JSON name */
2131 if (cur_module == module) {
2132 asprintf(&child_name, "%s", data_tree->schema->name);
2133 } else {
2134 asprintf(&child_name, "%s:%s", cur_module->name, data_tree->schema->name);
2135 }
2136
2137 /* go down in JSON object */
2138 if (json_object_object_get_ex(data_json_parent, child_name, &child_json) == FALSE) {
2139 ERROR("Internal: failed to get JSON object \"%s\".", child_name);
2140 free(child_name);
2141 return;
2142 }
2143 free(child_name);
2144
2145 if (data_tree->schema->nodetype == LYS_LIST) {
2146 if (json_object_get_type(child_json) != json_type_array) {
2147 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
2148 return;
2149 }
2150 /* go down in data tree for every item, we process them all now, skip later
2151 * (metadata duplicate will be detected at the beginning of this function) */
2152 list_idx = 0;
2153 list_schema = data_tree->schema;
2154
2155 LY_TREE_FOR(data_tree, list_item) {
2156 /* another list member */
2157 if (list_item->schema == list_schema) {
2158 list_child_json = json_object_array_get_idx(child_json, list_idx);
2159 if (!list_child_json) {
2160 ERROR("Internal: list \"%s\" idx out-of-bounds", list_schema->name);
2161 return;
2162 }
2163 LY_TREE_FOR(list_item->child, child) {
2164 node_add_metadata_recursive(child, cur_module, list_child_json);
2165 }
2166
2167 ++list_idx;
2168 }
2169 }
2170 } else {
2171 if (json_object_get_type(child_json) != json_type_object) {
2172 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
2173 return;
2174 }
2175 /* go down in data tree */
2176 LY_TREE_FOR(data_tree->child, child) {
2177 node_add_metadata_recursive(child, cur_module, child_json);
2178 }
2179 }
2180 }
2181}
2182
2183static void
Michal Vaskoa45770b2015-11-23 15:49:41 +01002184node_add_model_metadata(struct lys_module *module, json_object *parent)
2185{
2186 json_object *obj;
2187 char *str;
2188
2189 obj = json_object_new_object();
2190 node_metadata_model(module, obj);
2191 asprintf(&str, "$@@%s", module->name);
2192 json_object_object_add(parent, str, obj);
2193 free(str);
2194}
2195
2196static void
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002197node_add_children_with_metadata_recursive(struct lys_node *node, struct lys_module *module, json_object *parent)
2198{
2199 struct lys_module *cur_module;
2200 struct lys_node *child;
2201 json_object *node_json;
2202 char *json_name;
2203
Michal Vaskoa45770b2015-11-23 15:49:41 +01002204 if (node->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
2205 return;
2206 }
2207
2208 if (node->nodetype & LYS_USES) {
2209 cur_module = module;
2210 node_json = parent;
2211 goto children;
2212 }
2213
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002214 /* add node metadata */
2215 if (node_add_metadata(node, module, parent)) {
2216 ERROR("Internal: metadata duplicate for \"%s\".", node->name);
2217 return;
2218 }
2219
Michal Vaskoa45770b2015-11-23 15:49:41 +01002220 /* no other metadata */
2221 if (!node->child) {
2222 return;
2223 }
2224
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002225 /* get node module */
2226 cur_module = node->module;
2227 if (cur_module->type) {
2228 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
2229 }
2230
2231 /* create JSON object for child metadata */
2232 node_json = json_object_new_object();
2233 if (cur_module == module) {
2234 json_object_object_add(parent, node->name, node_json);
2235 } else {
2236 asprintf(&json_name, "%s:%s", cur_module->name, node->name);
2237 json_object_object_add(parent, json_name, node_json);
2238 free(json_name);
2239 }
2240
Michal Vaskoa45770b2015-11-23 15:49:41 +01002241children:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002242 LY_TREE_FOR(node->child, child) {
2243 node_add_children_with_metadata_recursive(child, cur_module, node_json);
2244 }
2245}
2246
2247static json_object *
2248libyang_query(unsigned int session_key, const char *filter, int load_children)
2249{
2250 struct lys_node *node;
Michal Vaskoa45770b2015-11-23 15:49:41 +01002251 struct lys_module *module = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002252 struct session_with_mutex *locked_session;
2253 json_object *ret = NULL, *data;
2254
2255 locked_session = session_get_locked(session_key, &ret);
2256 if (!locked_session) {
2257 ERROR("Locking failed or session not found.");
2258 goto finish;
2259 }
2260
2261 locked_session->last_activity = time(NULL);
2262
Michal Vaskoa45770b2015-11-23 15:49:41 +01002263 if (filter[0] == '/') {
2264 node = ly_ctx_get_node(locked_session->ctx, filter);
2265 if (!node) {
2266 ret = create_error_reply("Failed to resolve XPath filter node.");
2267 goto finish;
2268 }
2269 } else {
2270 module = ly_ctx_get_module(locked_session->ctx, filter, NULL);
2271 if (!module) {
2272 ret = create_error_reply("Failed to find model.");
2273 goto finish;
2274 }
2275 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002276
Michal Vaskoa45770b2015-11-23 15:49:41 +01002277 pthread_mutex_lock(&json_lock);
2278 data = json_object_new_object();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002279
Michal Vaskoa45770b2015-11-23 15:49:41 +01002280 if (module) {
2281 node_add_model_metadata(module, data);
2282 if (load_children) {
2283 LY_TREE_FOR(module->data, node) {
2284 node_add_children_with_metadata_recursive(node, NULL, data);
2285 }
2286 }
2287 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002288 if (load_children) {
2289 node_add_children_with_metadata_recursive(node, NULL, data);
2290 } else {
2291 node_add_metadata(node, NULL, data);
2292 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002293 }
2294
Michal Vaskoa45770b2015-11-23 15:49:41 +01002295 pthread_mutex_unlock(&json_lock);
2296 ret = create_data_reply(json_object_to_json_string(data));
2297 json_object_put(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002298
2299finish:
Michal Vaskoa45770b2015-11-23 15:49:41 +01002300 session_unlock(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002301 return ret;
2302}
2303
2304static json_object *
2305libyang_merge(unsigned int session_key, const char *config)
2306{
2307 struct lyd_node *data_tree = NULL, *sibling;
2308 struct session_with_mutex *locked_session;
2309 json_object *ret = NULL, *data_json = NULL;
2310 enum json_tokener_error err = 0;
2311
2312 locked_session = session_get_locked(session_key, &ret);
2313 if (!locked_session) {
2314 ERROR("Locking failed or session not found.");
2315 goto finish;
2316 }
2317
2318 locked_session->last_activity = time(NULL);
2319
2320 data_tree = lyd_parse(locked_session->ctx, config, LYD_JSON, LYD_OPT_STRICT);
2321 if (!data_tree) {
2322 ERROR("Creating data tree failed.");
2323 ret = create_error_reply("Failed to create data tree from JSON config.");
2324 session_unlock(locked_session);
2325 goto finish;
2326 }
2327
2328 session_unlock(locked_session);
2329
2330 pthread_mutex_lock(&json_lock);
2331 data_json = json_tokener_parse_verbose(config, &err);
2332 if (!data_json) {
2333 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(err));
2334 pthread_mutex_unlock(&json_lock);
2335 ret = create_error_reply(json_tokener_error_desc(err));
2336 goto finish;
2337 }
2338
2339 /* go simultaneously through both trees and add metadata */
2340 LY_TREE_FOR(data_tree, sibling) {
2341 node_add_metadata_recursive(sibling, NULL, data_json);
2342 }
2343 pthread_mutex_unlock(&json_lock);
2344 ret = create_data_reply(json_object_to_json_string(data_json));
2345
2346finish:
2347 LY_TREE_FOR(data_tree, sibling) {
2348 lyd_free(sibling);
2349 }
2350 json_object_put(data_json);
2351 return ret;
Radek Krejci80c10d92012-07-30 08:38:50 +02002352}
2353
Tomas Cejka0a4bba82013-04-19 11:51:28 +02002354/**
2355 * @}
2356 *//* netconf_operations */
2357
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002358void
2359clb_print(NC_VERB_LEVEL level, const char *msg)
Radek Krejci469aab82012-07-22 18:42:20 +02002360{
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002361#define FOREACH(I) \
Tomas Cejkacf44e522015-04-24 17:29:21 +02002362 I(NC_VERB_ERROR) I(NC_VERB_WARNING)
2363
2364#define CASE(VAL) case VAL: ERROR("%s: %s", #VAL, msg); \
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002365 break;
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002366
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002367 switch (level) {
2368 FOREACH(CASE);
2369 case NC_VERB_VERBOSE:
2370 case NC_VERB_DEBUG:
2371 DEBUG("DEBUG: %s", msg);
2372 break;
2373 }
2374 if (level == NC_VERB_ERROR) {
2375 /* return global error */
2376 netconf_callback_error_process(NULL /* tag */, NULL /* type */,
2377 NULL /* severity */, NULL /* apptag */,
2378 NULL /* path */, msg, NULL /* attribute */,
2379 NULL /* element */, NULL /* ns */, NULL /* sid */);
2380 }
Radek Krejci469aab82012-07-22 18:42:20 +02002381}
2382
Tomas Cejka64b87482013-06-03 16:30:53 +02002383/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02002384 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02002385 * Caller should free message memory.
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002386 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02002387 * \return pointer to message
2388 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002389char *
2390get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02002391{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002392 /* read json in chunked framing */
2393 unsigned int buffer_size = 0;
2394 ssize_t buffer_len = 0;
2395 char *buffer = NULL;
2396 char c;
2397 ssize_t ret;
2398 int i, chunk_len;
2399 char chunk_len_str[12];
Tomas Cejka64b87482013-06-03 16:30:53 +02002400
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002401 while (1) {
2402 /* read chunk length */
2403 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
2404 if (buffer != NULL) {
2405 free (buffer);
2406 buffer = NULL;
2407 }
2408 break;
2409 }
2410 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
2411 if (buffer != NULL) {
2412 free (buffer);
2413 buffer = NULL;
2414 }
2415 break;
2416 }
2417 i=0;
2418 memset (chunk_len_str, 0, 12);
2419 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
2420 if (i==0 && c == '#') {
2421 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
2422 /* end but invalid */
2423 if (buffer != NULL) {
2424 free (buffer);
2425 buffer = NULL;
2426 }
2427 }
2428 /* end of message, double-loop break */
2429 goto msg_complete;
2430 }
2431 chunk_len_str[i++] = c;
2432 if (i==11) {
2433 ERROR("Message is too long, buffer for length is not big enought!!!!");
2434 break;
2435 }
2436 }
2437 if (c != '\n') {
2438 if (buffer != NULL) {
2439 free (buffer);
2440 buffer = NULL;
2441 }
2442 break;
2443 }
2444 chunk_len_str[i] = 0;
2445 if ((chunk_len = atoi (chunk_len_str)) == 0) {
2446 if (buffer != NULL) {
2447 free (buffer);
2448 buffer = NULL;
2449 }
2450 break;
2451 }
2452 buffer_size += chunk_len+1;
2453 buffer = realloc (buffer, sizeof(char)*buffer_size);
2454 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
2455 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
2456 if (buffer != NULL) {
2457 free (buffer);
2458 buffer = NULL;
2459 }
2460 break;
2461 }
2462 buffer_len += ret;
2463 }
Tomas Cejka64b87482013-06-03 16:30:53 +02002464msg_complete:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002465 return buffer;
Tomas Cejka64b87482013-06-03 16:30:53 +02002466}
2467
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002468NC_DATASTORE
2469parse_datastore(const char *ds)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002470{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002471 if (strcmp(ds, "running") == 0) {
2472 return NC_DATASTORE_RUNNING;
2473 } else if (strcmp(ds, "startup") == 0) {
2474 return NC_DATASTORE_STARTUP;
2475 } else if (strcmp(ds, "candidate") == 0) {
2476 return NC_DATASTORE_CANDIDATE;
2477 } else if (strcmp(ds, "url") == 0) {
2478 return NC_DATASTORE_URL;
2479 } else if (strcmp(ds, "config") == 0) {
2480 return NC_DATASTORE_CONFIG;
2481 }
2482 return -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002483}
2484
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002485NC_EDIT_TESTOPT_TYPE
2486parse_testopt(const char *t)
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002487{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002488 if (strcmp(t, "notset") == 0) {
2489 return NC_EDIT_TESTOPT_NOTSET;
2490 } else if (strcmp(t, "testset") == 0) {
2491 return NC_EDIT_TESTOPT_TESTSET;
2492 } else if (strcmp(t, "set") == 0) {
2493 return NC_EDIT_TESTOPT_SET;
2494 } else if (strcmp(t, "test") == 0) {
2495 return NC_EDIT_TESTOPT_TEST;
2496 }
2497 return NC_EDIT_TESTOPT_ERROR;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002498}
2499
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002500json_object *
2501create_error_reply(const char *errmess)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002502{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002503 json_object *reply, *array;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002504
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002505 pthread_mutex_lock(&json_lock);
2506 reply = json_object_new_object();
2507 array = json_object_new_array();
2508 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2509 json_object_array_add(array, json_object_new_string(errmess));
2510 json_object_object_add(reply, "errors", array);
2511 pthread_mutex_unlock(&json_lock);
2512
2513 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002514}
2515
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002516json_object *
2517create_data_reply(const char *data)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002518{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002519 pthread_mutex_lock(&json_lock);
2520 json_object *reply = json_object_new_object();
2521 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
2522 json_object_object_add(reply, "data", json_object_new_string(data));
2523 pthread_mutex_unlock(&json_lock);
2524 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002525}
2526
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002527json_object *
2528create_ok_reply(void)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002529{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002530 pthread_mutex_lock(&json_lock);
2531 json_object *reply = json_object_new_object();
2532 reply = json_object_new_object();
2533 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2534 pthread_mutex_unlock(&json_lock);
2535 return reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002536}
2537
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002538json_object *
2539create_replies(void)
Tomas Cejka09629492014-07-10 15:58:06 +02002540{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002541 json_object *replies;
2542
2543 pthread_mutex_lock(&json_lock);
2544 replies = json_object_new_object();
2545 pthread_mutex_unlock(&json_lock);
2546
2547 return replies;
Tomas Cejka09629492014-07-10 15:58:06 +02002548}
2549
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002550void
2551add_reply(json_object *replies, json_object *reply, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002552{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002553 char *str;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002554
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002555 asprintf(&str, "%u", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002556
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002557 pthread_mutex_lock(&json_lock);
2558 json_object_object_add(replies, str, reply);
2559 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002560
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002561 free(str);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002562}
2563
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002564char *
2565get_param_string(json_object *data, const char *name)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002566{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002567 json_object *js_tmp = NULL;
2568 char *res = NULL;
2569 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
2570 res = strdup(json_object_get_string(js_tmp));
2571 }
2572 return res;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002573}
2574
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002575json_object *
2576handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002577{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002578 char *host = NULL;
2579 char *port = NULL;
2580 char *user = NULL;
2581 char *pass = NULL;
2582 json_object *reply = NULL;
2583 unsigned int session_key = 0;
2584 struct nc_cpblts* cpblts = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002585
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002586 DEBUG("Request: connect");
2587 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002588
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002589 host = get_param_string(request, "host");
2590 port = get_param_string(request, "port");
2591 user = get_param_string(request, "user");
2592 pass = get_param_string(request, "pass");
Tomas Cejkad5b53772013-06-08 23:01:07 +02002593
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002594 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002595
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002596 DEBUG("host: %s, port: %s, user: %s", host, port, user);
2597 if ((host == NULL) || (user == NULL)) {
2598 ERROR("Cannot connect - insufficient input.");
2599 session_key = 0;
2600 } else {
2601 session_key = netconf_connect(host, port, user, pass, cpblts);
2602 DEBUG("Session key: %u", session_key);
2603 }
2604 if (cpblts != NULL) {
2605 nc_cpblts_free(cpblts);
2606 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002607
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002608 GETSPEC_ERR_REPLY
2609
2610 pthread_mutex_lock(&json_lock);
2611 if (session_key == 0) {
2612 /* negative reply */
2613 if (err_reply == NULL) {
2614 reply = json_object_new_object();
2615 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2616 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
2617 ERROR("Connection failed.");
2618 } else {
2619 /* use filled err_reply from libnetconf's callback */
2620 reply = err_reply;
2621 ERROR("Connect - error from libnetconf's callback.");
2622 }
2623 } else {
2624 /* positive reply */
2625 reply = json_object_new_object();
2626 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2627 json_object_object_add(reply, "session", json_object_new_int(session_key));
2628 }
2629 memset(pass, 0, strlen(pass));
2630 pthread_mutex_unlock(&json_lock);
2631 CHECK_AND_FREE(host);
2632 CHECK_AND_FREE(user);
2633 CHECK_AND_FREE(port);
2634 CHECK_AND_FREE(pass);
2635 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002636}
2637
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002638json_object *
2639handle_op_disconnect(json_object *UNUSED(request), unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002640{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002641 json_object *reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002642
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002643 DEBUG("Request: disconnect (session %u)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002644
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002645 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
2646 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
2647 } else {
2648 reply = create_ok_reply();
2649 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002650
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002651 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002652}
2653
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002654json_object *
2655handle_op_get(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002656{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002657 char *filter = NULL;
2658 char *data = NULL;
2659 json_object *reply = NULL, *obj;
2660 int strict;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002661
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002662 DEBUG("Request: get (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002663
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002664 pthread_mutex_lock(&json_lock);
2665 filter = get_param_string(request, "filter");
2666 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2667 pthread_mutex_unlock(&json_lock);
2668 reply = create_error_reply("Missing strict parameter.");
2669 return reply;
2670 }
2671 strict = json_object_get_boolean(obj);
2672 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002673
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002674 if ((data = netconf_get(session_key, filter, strict, &reply)) == NULL) {
2675 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
2676 } else {
2677 reply = create_data_reply(data);
2678 free(data);
2679 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002680
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002681 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002682}
2683
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002684json_object *
2685handle_op_getconfig(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002686{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002687 NC_DATASTORE ds_type_s = -1;
2688 char *filter = NULL;
2689 char *data = NULL;
2690 char *source = NULL;
2691 json_object *reply = NULL, *obj;
2692 int strict;
Tomas Cejkab4d05872014-02-14 22:44:38 +01002693
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002694 DEBUG("Request: get-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002695
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002696 pthread_mutex_lock(&json_lock);
2697 filter = get_param_string(request, "filter");
2698 source = get_param_string(request, "source");
2699 if (source != NULL) {
2700 ds_type_s = parse_datastore(source);
2701 }
2702 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2703 pthread_mutex_unlock(&json_lock);
2704 reply = create_error_reply("Missing strict parameter.");
2705 return reply;
2706 }
2707 strict = json_object_get_boolean(obj);
2708 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002709
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002710 if ((int)ds_type_s == -1) {
2711 reply = create_error_reply("Invalid source repository type requested.");
2712 goto finalize;
2713 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002714
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002715 if ((data = netconf_getconfig(session_key, ds_type_s, filter, strict, &reply)) == NULL) {
2716 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
2717 } else {
2718 reply = create_data_reply(data);
2719 free(data);
2720 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002721
Tomas Cejka09629492014-07-10 15:58:06 +02002722finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002723 CHECK_AND_FREE(filter);
2724 CHECK_AND_FREE(source);
2725 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002726}
2727
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002728json_object *
2729handle_op_editconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002730{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002731 NC_DATASTORE ds_type_s = -1;
2732 NC_DATASTORE ds_type_t = -1;
2733 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
2734 NC_EDIT_ERROPT_TYPE erropt_type = 0;
2735 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
2736 char *defop = NULL;
2737 char *erropt = NULL;
2738 char *config = NULL;
2739 char *source = NULL;
2740 char *target = NULL;
2741 char *testopt = NULL;
2742 char *urisource = NULL;
2743 json_object *reply = NULL, *configs, *obj;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002744
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002745 DEBUG("Request: edit-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002746
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002747 pthread_mutex_lock(&json_lock);
2748 /* get parameters */
2749 target = get_param_string(request, "target");
2750 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2751 pthread_mutex_unlock(&json_lock);
2752 reply = create_error_reply("Missing configs parameter.");
2753 goto finalize;
2754 }
2755 obj = json_object_array_get_idx(configs, idx);
2756 config = strdup(json_object_get_string(obj));
Tomas Cejkad5b53772013-06-08 23:01:07 +02002757
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002758 source = get_param_string(request, "source");
2759 defop = get_param_string(request, "default-operation");
2760 erropt = get_param_string(request, "error-option");
2761 urisource = get_param_string(request, "uri-source");
2762 testopt = get_param_string(request, "test-option");
2763 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002764
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002765 if (target != NULL) {
2766 ds_type_t = parse_datastore(target);
2767 }
2768 if (source != NULL) {
2769 ds_type_s = parse_datastore(source);
2770 } else {
2771 /* source is optional, default value is config */
2772 ds_type_s = NC_DATASTORE_CONFIG;
2773 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002774
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002775 if (defop != NULL) {
2776 if (strcmp(defop, "merge") == 0) {
2777 defop_type = NC_EDIT_DEFOP_MERGE;
2778 } else if (strcmp(defop, "replace") == 0) {
2779 defop_type = NC_EDIT_DEFOP_REPLACE;
2780 } else if (strcmp(defop, "none") == 0) {
2781 defop_type = NC_EDIT_DEFOP_NONE;
2782 } else {
2783 reply = create_error_reply("Invalid default-operation parameter.");
2784 goto finalize;
2785 }
2786 } else {
2787 defop_type = NC_EDIT_DEFOP_NOTSET;
2788 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002789
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002790 if (erropt != NULL) {
2791 if (strcmp(erropt, "continue-on-error") == 0) {
2792 erropt_type = NC_EDIT_ERROPT_CONT;
2793 } else if (strcmp(erropt, "stop-on-error") == 0) {
2794 erropt_type = NC_EDIT_ERROPT_STOP;
2795 } else if (strcmp(erropt, "rollback-on-error") == 0) {
2796 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
2797 } else {
2798 reply = create_error_reply("Invalid error-option parameter.");
2799 goto finalize;
2800 }
2801 } else {
2802 erropt_type = 0;
2803 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002804
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002805 if ((int)ds_type_t == -1) {
2806 reply = create_error_reply("Invalid target repository type requested.");
2807 goto finalize;
2808 }
2809 if (ds_type_s == NC_DATASTORE_CONFIG) {
2810 if (config == NULL) {
2811 reply = create_error_reply("Invalid config data parameter.");
2812 goto finalize;
2813 }
2814 } else if (ds_type_s == NC_DATASTORE_URL){
2815 if (urisource == NULL) {
2816 reply = create_error_reply("Invalid uri-source parameter.");
2817 goto finalize;
2818 }
2819 config = urisource;
2820 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002821
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002822 if (testopt != NULL) {
2823 testopt_type = parse_testopt(testopt);
2824 } else {
2825 testopt_type = NC_EDIT_TESTOPT_TESTSET;
2826 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002827
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002828 reply = netconf_editconfig(session_key, ds_type_s, ds_type_t, defop_type, erropt_type, testopt_type, config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002829
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002830 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002831
Tomas Cejka09629492014-07-10 15:58:06 +02002832finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002833 CHECK_AND_FREE(defop);
2834 CHECK_AND_FREE(erropt);
2835 CHECK_AND_FREE(config);
2836 CHECK_AND_FREE(source);
2837 CHECK_AND_FREE(urisource);
2838 CHECK_AND_FREE(target);
2839 CHECK_AND_FREE(testopt);
2840
2841 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002842}
2843
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002844json_object *
2845handle_op_copyconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002846{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002847 NC_DATASTORE ds_type_s = -1;
2848 NC_DATASTORE ds_type_t = -1;
2849 char *config = NULL;
2850 char *target = NULL;
2851 char *source = NULL;
2852 char *uri_src = NULL;
2853 char *uri_trg = NULL;
2854 json_object *reply = NULL, *configs, *obj;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002855
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002856 DEBUG("Request: copy-config (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002857
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002858 /* get parameters */
2859 pthread_mutex_lock(&json_lock);
2860 target = get_param_string(request, "target");
2861 source = get_param_string(request, "source");
2862 uri_src = get_param_string(request, "uri-source");
2863 uri_trg = get_param_string(request, "uri-target");
2864 if (!strcmp(source, "config")) {
2865 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2866 pthread_mutex_unlock(&json_lock);
2867 reply = create_error_reply("Missing configs parameter.");
2868 goto finalize;
2869 }
2870 obj = json_object_array_get_idx(configs, idx);
2871 if (!obj) {
2872 pthread_mutex_unlock(&json_lock);
2873 reply = create_error_reply("Configs array parameter shorter than sessions.");
2874 goto finalize;
2875 }
2876 config = strdup(json_object_get_string(obj));
2877 }
2878 pthread_mutex_unlock(&json_lock);
2879
2880 if (target != NULL) {
2881 ds_type_t = parse_datastore(target);
2882 }
2883 if (source != NULL) {
2884 ds_type_s = parse_datastore(source);
2885 }
2886
2887 if ((int)ds_type_s == -1) {
2888 /* invalid source datastore specified */
2889 reply = create_error_reply("Invalid source repository type requested.");
2890 goto finalize;
2891 }
2892
2893 if ((int)ds_type_t == -1) {
2894 /* invalid target datastore specified */
2895 reply = create_error_reply("Invalid target repository type requested.");
2896 goto finalize;
2897 }
2898
2899 if (ds_type_s == NC_DATASTORE_URL) {
2900 if (uri_src == NULL) {
2901 uri_src = "";
2902 }
2903 }
2904 if (ds_type_t == NC_DATASTORE_URL) {
2905 if (uri_trg == NULL) {
2906 uri_trg = "";
2907 }
2908 }
2909 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
2910
2911 CHECK_ERR_SET_REPLY
2912
2913finalize:
2914 CHECK_AND_FREE(config);
2915 CHECK_AND_FREE(target);
2916 CHECK_AND_FREE(source);
2917 CHECK_AND_FREE(uri_src);
2918 CHECK_AND_FREE(uri_trg);
2919
2920 return reply;
2921}
2922
2923json_object *
2924handle_op_deleteconfig(json_object *request, unsigned int session_key)
2925{
2926 json_object *reply;
2927 NC_DATASTORE ds_type = -1;
2928 char *target, *url;
2929
2930 DEBUG("Request: delete-config (session %u)", session_key);
2931
2932 pthread_mutex_lock(&json_lock);
2933 target = get_param_string(request, "target");
2934 url = get_param_string(request, "url");
2935 pthread_mutex_unlock(&json_lock);
2936
2937 if (target != NULL) {
2938 ds_type = parse_datastore(target);
2939 }
2940 if ((int)ds_type == -1) {
2941 reply = create_error_reply("Invalid target repository type requested.");
2942 goto finalize;
2943 }
2944 if (ds_type == NC_DATASTORE_URL) {
2945 if (!url) {
2946 url = "";
2947 }
2948 }
2949
2950 reply = netconf_deleteconfig(session_key, ds_type, url);
2951
2952 CHECK_ERR_SET_REPLY
2953 if (reply == NULL) {
2954 reply = create_ok_reply();
2955 }
2956
2957finalize:
2958 CHECK_AND_FREE(target);
2959 CHECK_AND_FREE(url);
2960 return reply;
2961}
2962
2963json_object *
2964handle_op_lock(json_object *request, unsigned int session_key)
2965{
2966 json_object *reply;
2967 NC_DATASTORE ds_type = -1;
2968 char *target;
2969
2970 DEBUG("Request: lock (session %u)", session_key);
2971
2972 pthread_mutex_lock(&json_lock);
2973 target = get_param_string(request, "target");
2974 pthread_mutex_unlock(&json_lock);
2975
2976 if (target != NULL) {
2977 ds_type = parse_datastore(target);
2978 }
2979 if ((int)ds_type == -1) {
2980 reply = create_error_reply("Invalid target repository type requested.");
2981 goto finalize;
2982 }
2983
2984 reply = netconf_lock(session_key, ds_type);
2985
2986 CHECK_ERR_SET_REPLY
2987 if (reply == NULL) {
2988 reply = create_ok_reply();
2989 }
2990
2991finalize:
2992 CHECK_AND_FREE(target);
2993 return reply;
2994}
2995
2996json_object *
2997handle_op_unlock(json_object *request, unsigned int session_key)
2998{
2999 json_object *reply;
3000 NC_DATASTORE ds_type = -1;
3001 char *target;
3002
3003 DEBUG("Request: unlock (session %u)", session_key);
3004
3005 pthread_mutex_lock(&json_lock);
3006 target = get_param_string(request, "target");
3007 pthread_mutex_unlock(&json_lock);
3008
3009 if (target != NULL) {
3010 ds_type = parse_datastore(target);
3011 }
3012 if ((int)ds_type == -1) {
3013 reply = create_error_reply("Invalid target repository type requested.");
3014 goto finalize;
3015 }
3016
3017 reply = netconf_unlock(session_key, ds_type);
3018
3019 CHECK_ERR_SET_REPLY
3020 if (reply == NULL) {
3021 reply = create_ok_reply();
3022 }
3023
3024finalize:
3025 CHECK_AND_FREE(target);
3026 return reply;
3027}
3028
3029json_object *
3030handle_op_kill(json_object *request, unsigned int session_key)
3031{
3032 json_object *reply = NULL;
3033 char *sid = NULL;
3034
3035 DEBUG("Request: kill-session (session %u)", session_key);
3036
3037 pthread_mutex_lock(&json_lock);
3038 sid = get_param_string(request, "session-id");
3039 pthread_mutex_unlock(&json_lock);
3040
3041 if (sid == NULL) {
3042 reply = create_error_reply("Missing session-id parameter.");
3043 goto finalize;
3044 }
3045
3046 reply = netconf_killsession(session_key, sid);
3047
3048 CHECK_ERR_SET_REPLY
3049
3050finalize:
3051 CHECK_AND_FREE(sid);
3052 return reply;
3053}
3054
3055json_object *
3056handle_op_info(json_object *UNUSED(request), unsigned int session_key)
3057{
3058 json_object *reply = NULL;
3059 struct session_with_mutex *locked_session = NULL;
3060 DEBUG("Request: get info about session %u", session_key);
3061
3062 DEBUG("LOCK wrlock %s", __func__);
3063 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3064 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3065 }
3066
3067 for (locked_session = netconf_sessions_list;
3068 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003069 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003070 if (locked_session != NULL) {
3071 DEBUG("LOCK mutex %s", __func__);
3072 pthread_mutex_lock(&locked_session->lock);
3073 DEBUG("UNLOCK wrlock %s", __func__);
3074 if (pthread_rwlock_unlock(&session_lock) != 0) {
3075 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3076 }
3077 if (locked_session->hello_message != NULL) {
3078 reply = locked_session->hello_message;
3079 } else {
3080 reply = create_error_reply("Invalid session identifier.");
3081 }
3082 DEBUG("UNLOCK mutex %s", __func__);
3083 pthread_mutex_unlock(&locked_session->lock);
3084 } else {
3085 DEBUG("UNLOCK wrlock %s", __func__);
3086 if (pthread_rwlock_unlock(&session_lock) != 0) {
3087 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3088 }
3089 reply = create_error_reply("Invalid session identifier.");
3090 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02003091
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003092 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003093}
3094
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003095json_object *
3096handle_op_generic(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02003097{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003098 json_object *reply = NULL, *contents, *obj;
3099 char *config = NULL;
3100 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003101
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003102 DEBUG("Request: generic request (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02003103
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003104 pthread_mutex_lock(&json_lock);
3105 if (json_object_object_get_ex(request, "contents", &contents) == FALSE) {
3106 pthread_mutex_unlock(&json_lock);
3107 reply = create_error_reply("Missing contents parameter.");
3108 goto finalize;
3109 }
3110 obj = json_object_array_get_idx(contents, idx);
3111 if (!obj) {
3112 pthread_mutex_unlock(&json_lock);
3113 reply = create_error_reply("Contents array parameter shorter than sessions.");
3114 goto finalize;
3115 }
3116 config = strdup(json_object_get_string(obj));
3117 pthread_mutex_unlock(&json_lock);
3118
3119 reply = netconf_generic(session_key, config, &data);
3120 if (reply == NULL) {
3121 GETSPEC_ERR_REPLY
3122 if (err_reply != NULL) {
3123 /* use filled err_reply from libnetconf's callback */
3124 reply = err_reply;
3125 }
3126 } else {
3127 if (data == NULL) {
3128 pthread_mutex_lock(&json_lock);
3129 reply = json_object_new_object();
3130 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
3131 pthread_mutex_unlock(&json_lock);
3132 } else {
3133 reply = create_data_reply(data);
3134 free(data);
3135 }
3136 }
3137
3138finalize:
3139 CHECK_AND_FREE(config);
3140 return reply;
3141}
3142
3143json_object *
3144handle_op_getschema(json_object *request, unsigned int session_key)
3145{
3146 char *data = NULL;
3147 char *identifier = NULL;
3148 char *version = NULL;
3149 char *format = NULL;
3150 json_object *reply = NULL;
3151
3152 DEBUG("Request: get-schema (session %u)", session_key);
3153
3154 pthread_mutex_lock(&json_lock);
3155 identifier = get_param_string(request, "identifier");
3156 version = get_param_string(request, "version");
3157 format = get_param_string(request, "format");
3158 pthread_mutex_unlock(&json_lock);
3159
3160 if (identifier == NULL) {
3161 reply = create_error_reply("No identifier for get-schema supplied.");
3162 goto finalize;
3163 }
3164
3165 DEBUG("get-schema(version: %s, format: %s)", version, format);
3166 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
3167 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
3168 } else {
3169 reply = create_data_reply(data);
3170 free(data);
3171 }
3172
3173finalize:
3174 CHECK_AND_FREE(identifier);
3175 CHECK_AND_FREE(version);
3176 CHECK_AND_FREE(format);
3177 return reply;
3178}
3179
3180json_object *
3181handle_op_reloadhello(json_object *UNUSED(request), unsigned int session_key)
3182{
3183 struct nc_session *temp_session = NULL;
3184 struct session_with_mutex * locked_session = NULL;
3185 json_object *reply = NULL;
3186
3187 DEBUG("Request: reload hello (session %u)", session_key);
3188
3189 DEBUG("LOCK wrlock %s", __func__);
3190 if (pthread_rwlock_wrlock(&session_lock) != 0) {
3191 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3192 return NULL;
3193 }
3194
3195 for (locked_session = netconf_sessions_list;
3196 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003197 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003198 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
3199 DEBUG("LOCK mutex %s", __func__);
3200 pthread_mutex_lock(&locked_session->lock);
3201 DEBUG("creating temporary NC session.");
3202 temp_session = nc_session_connect_channel(locked_session->session, NULL);
3203 if (temp_session != NULL) {
3204 prepare_status_message(locked_session, temp_session);
3205 DEBUG("closing temporal NC session.");
3206 nc_session_free(temp_session);
3207 temp_session = NULL;
3208 } else {
3209 DEBUG("Reload hello failed due to channel establishment");
3210 reply = create_error_reply("Reload was unsuccessful, connection failed.");
3211 }
3212 DEBUG("UNLOCK mutex %s", __func__);
3213 pthread_mutex_unlock(&locked_session->lock);
3214 DEBUG("UNLOCK wrlock %s", __func__);
3215 if (pthread_rwlock_unlock(&session_lock) != 0) {
3216 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3217 }
3218 } else {
3219 DEBUG("UNLOCK wrlock %s", __func__);
3220 if (pthread_rwlock_unlock(&session_lock) != 0) {
3221 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3222 }
3223 reply = create_error_reply("Invalid session identifier.");
3224 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02003225
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003226 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
3227 reply = locked_session->hello_message;
3228 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02003229
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003230 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003231}
3232
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003233void
3234notification_history(time_t eventtime, const char *content)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003235{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003236 json_object *notif_history_array = (json_object *)pthread_getspecific(notif_history_key);
3237 if (notif_history_array == NULL) {
3238 ERROR("No list of notification history found.");
3239 return;
3240 }
3241 DEBUG("Got notification from history %lu.", (long unsigned)eventtime);
3242 pthread_mutex_lock(&json_lock);
3243 json_object *notif = json_object_new_object();
3244 if (notif == NULL) {
3245 ERROR("Could not allocate memory for notification (json).");
3246 goto failed;
3247 }
3248 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
3249 json_object_object_add(notif, "content", json_object_new_string(content));
3250 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003251failed:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003252 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003253}
3254
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003255json_object *
3256handle_op_ntfgethistory(json_object *request, unsigned int session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003257{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003258 json_object *reply = NULL;
3259 json_object *js_tmp = NULL;
3260 struct session_with_mutex *locked_session = NULL;
3261 struct nc_session *temp_session = NULL;
3262 nc_rpc *rpc = NULL;
3263 time_t start = 0;
3264 time_t stop = 0;
3265 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003266
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003267 DEBUG("Request: get notification history (session %u)", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003268
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003269 pthread_mutex_lock(&json_lock);
3270 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
3271 from = json_object_get_int64(js_tmp);
3272 }
3273 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
3274 to = json_object_get_int64(js_tmp);
3275 }
3276 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02003277
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003278 start = time(NULL) + from;
3279 stop = time(NULL) + to;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003280
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003281 DEBUG("notification history interval %li %li", (long int)from, (long int)to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003282
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003283 DEBUG("LOCK wrlock %s", __func__);
3284 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3285 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3286 reply = create_error_reply("Internal lock failed.");
3287 goto finalize;
3288 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003289
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003290 for (locked_session = netconf_sessions_list;
3291 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003292 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003293 if (locked_session != NULL) {
3294 DEBUG("LOCK mutex %s", __func__);
3295 pthread_mutex_lock(&locked_session->lock);
3296 DEBUG("UNLOCK wrlock %s", __func__);
3297 if (pthread_rwlock_unlock(&session_lock) != 0) {
3298 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3299 }
3300 DEBUG("creating temporal NC session.");
3301 temp_session = nc_session_connect_channel(locked_session->session, NULL);
3302 if (temp_session != NULL) {
3303 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
3304 if (rpc == NULL) {
3305 DEBUG("UNLOCK mutex %s", __func__);
3306 pthread_mutex_unlock(&locked_session->lock);
3307 DEBUG("notifications: creating an rpc request failed.");
3308 reply = create_error_reply("notifications: creating an rpc request failed.");
3309 goto finalize;
3310 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003311
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003312 DEBUG("Send NC subscribe.");
3313 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
3314 json_object *res = netconf_unlocked_op(temp_session, rpc);
3315 if (res != NULL) {
3316 DEBUG("UNLOCK mutex %s", __func__);
3317 pthread_mutex_unlock(&locked_session->lock);
3318 DEBUG("Subscription RPC failed.");
3319 reply = res;
3320 goto finalize;
3321 }
3322 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejka6b886e02013-07-05 09:53:17 +02003323
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003324 DEBUG("UNLOCK mutex %s", __func__);
3325 pthread_mutex_unlock(&locked_session->lock);
3326 DEBUG("LOCK mutex %s", __func__);
3327 pthread_mutex_lock(&ntf_history_lock);
3328 pthread_mutex_lock(&json_lock);
3329 json_object *notif_history_array = json_object_new_array();
3330 pthread_mutex_unlock(&json_lock);
3331 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
3332 ERROR("notif_history: cannot set thread-specific hash value.");
3333 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003334
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003335 ncntf_dispatch_receive(temp_session, notification_history);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003336
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003337 pthread_mutex_lock(&json_lock);
3338 reply = json_object_new_object();
3339 json_object_object_add(reply, "notifications", notif_history_array);
3340 //json_object_put(notif_history_array);
3341 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003342
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003343 DEBUG("UNLOCK mutex %s", __func__);
3344 pthread_mutex_unlock(&ntf_history_lock);
3345 DEBUG("closing temporal NC session.");
3346 nc_session_free(temp_session);
3347 temp_session = NULL;
3348 } else {
3349 DEBUG("UNLOCK mutex %s", __func__);
3350 pthread_mutex_unlock(&locked_session->lock);
3351 DEBUG("Get history of notification failed due to channel establishment");
3352 reply = create_error_reply("Get history of notification was unsuccessful, connection failed.");
3353 }
3354 } else {
3355 DEBUG("UNLOCK wrlock %s", __func__);
3356 if (pthread_rwlock_unlock(&session_lock) != 0) {
3357 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3358 }
3359 reply = create_error_reply("Invalid session identifier.");
3360 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003361
Tomas Cejka09629492014-07-10 15:58:06 +02003362finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003363 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02003364}
3365
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003366json_object *
3367handle_op_validate(json_object *request, unsigned int session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02003368{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003369 json_object *reply = NULL;
3370 char *target = NULL;
3371 char *url = NULL;
3372 nc_rpc *rpc = NULL;
3373 NC_DATASTORE target_ds;
Tomas Cejka4003a702013-10-01 00:02:45 +02003374
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003375 DEBUG("Request: validate datastore (session %u)", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02003376
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003377 pthread_mutex_lock(&json_lock);
3378 target = get_param_string(request, "target");
3379 url = get_param_string(request, "url");
3380 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02003381
3382
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003383 if (target == NULL) {
3384 reply = create_error_reply("Missing target parameter.");
3385 goto finalize;
3386 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003387
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003388 /* validation */
3389 target_ds = parse_datastore(target);
3390 if (target_ds == NC_DATASTORE_URL) {
3391 if (url != NULL) {
3392 rpc = nc_rpc_validate(target_ds, url);
3393 }
3394 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
3395 || (target_ds == NC_DATASTORE_CANDIDATE)) {
3396 rpc = nc_rpc_validate(target_ds);
3397 }
3398 if (rpc == NULL) {
3399 DEBUG("mod_netconf: creating rpc request failed");
3400 reply = create_error_reply("Creation of RPC request failed.");
3401 goto finalize;
3402 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003403
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003404 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
3405 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003406
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003407 if (reply == NULL) {
3408 DEBUG("Request: validation ok.");
3409 reply = create_ok_reply();
3410 }
3411 }
3412 nc_rpc_free (rpc);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003413
Tomas Cejka09629492014-07-10 15:58:06 +02003414finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003415 CHECK_AND_FREE(target);
3416 CHECK_AND_FREE(url);
3417 return reply;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003418}
3419
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003420json_object *
3421handle_op_query(json_object *request, unsigned int session_key, int idx)
David Kupka8e60a372012-09-04 09:15:20 +02003422{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003423 json_object *reply = NULL, *filters, *obj;
3424 char *filter = NULL;
3425 int load_children = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003426
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003427 DEBUG("Request: query (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003428
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003429 pthread_mutex_lock(&json_lock);
3430 if (json_object_object_get_ex(request, "filters", &filters) == FALSE) {
3431 pthread_mutex_unlock(&json_lock);
3432 reply = create_error_reply("Missing filters parameter.");
3433 goto finalize;
3434 }
3435 obj = json_object_array_get_idx(filters, idx);
3436 if (!obj) {
3437 pthread_mutex_unlock(&json_lock);
3438 reply = create_error_reply("Filters array parameter shorter than sessions.");
3439 goto finalize;
3440 }
3441 filter = strdup(json_object_get_string(obj));
3442 if (json_object_object_get_ex(request, "load_children", &obj) == TRUE) {
3443 load_children = json_object_get_boolean(obj);
3444 }
3445 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02003446
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003447 reply = libyang_query(session_key, filter, load_children);
David Kupka8e60a372012-09-04 09:15:20 +02003448
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003449 CHECK_ERR_SET_REPLY
3450 if (!reply) {
3451 reply = create_error_reply("Query failed.");
3452 }
David Kupka8e60a372012-09-04 09:15:20 +02003453
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003454finalize:
3455 CHECK_AND_FREE(filter);
3456 return reply;
3457}
David Kupka8e60a372012-09-04 09:15:20 +02003458
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003459json_object *
3460handle_op_merge(json_object *request, unsigned int session_key, int idx)
3461{
3462 json_object *reply = NULL, *configs, *obj;
3463 char *config = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003464
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003465 DEBUG("Request: merge (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003466
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003467 pthread_mutex_lock(&json_lock);
3468 if (json_object_object_get_ex(request, "configurations", &configs) == FALSE) {
3469 pthread_mutex_unlock(&json_lock);
3470 reply = create_error_reply("Missing configurations parameter.");
3471 goto finalize;
3472 }
3473 obj = json_object_array_get_idx(configs, idx);
3474 if (!obj) {
3475 pthread_mutex_unlock(&json_lock);
3476 reply = create_error_reply("Filters array parameter shorter than sessions.");
3477 goto finalize;
3478 }
3479 config = strdup(json_object_get_string(obj));
3480 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02003481
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003482 reply = libyang_merge(session_key, config);
David Kupka8e60a372012-09-04 09:15:20 +02003483
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003484 CHECK_ERR_SET_REPLY
3485 if (!reply) {
3486 reply = create_error_reply("Merge failed.");
3487 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003488
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003489finalize:
3490 CHECK_AND_FREE(config);
3491 return reply;
3492}
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003493
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003494void *
3495thread_routine(void *arg)
3496{
3497 void *retval = NULL;
3498 struct pollfd fds;
3499 json_object *request = NULL, *replies = NULL, *reply, *sessions = NULL;
3500 json_object *js_tmp = NULL;
3501 int operation = (-1), count, i;
3502 int status = 0;
3503 const char *msgtext;
3504 unsigned int session_key = 0;
3505 char *chunked_out_msg = NULL;
3506 int client = ((struct pass_to_thread *)arg)->client;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003507
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003508 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003509
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003510 /* init thread specific err_reply memory */
3511 create_err_reply_p();
David Kupka8e60a372012-09-04 09:15:20 +02003512
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003513 while (!isterminated) {
3514 fds.fd = client;
3515 fds.events = POLLIN;
3516 fds.revents = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003517
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003518 status = poll(&fds, 1, 1000);
Tomas Cejkad5b53772013-06-08 23:01:07 +02003519
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003520 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
3521 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
3522 continue;
3523 } else if (status < 0) {
3524 /* 0: poll time outed
3525 * close socket and ignore this request from the client, it can try it again
3526 * -1: poll failed
3527 * something wrong happend, close this socket and wait for another request
3528 */
3529 close(client);
3530 break;
3531 }
3532 /* status > 0 */
David Kupka8e60a372012-09-04 09:15:20 +02003533
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003534 /* check the status of the socket */
David Kupka8e60a372012-09-04 09:15:20 +02003535
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003536 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
3537 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
3538 /* close client's socket (it's probably already closed by client */
3539 close(client);
3540 break;
3541 }
Tomas Cejka09629492014-07-10 15:58:06 +02003542
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003543 DEBUG("Get framed message...");
3544 buffer = get_framed_message(client);
Tomas Cejka09629492014-07-10 15:58:06 +02003545
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003546 DEBUG("Check read buffer.");
3547 if (buffer != NULL) {
3548 enum json_tokener_error jerr;
3549 pthread_mutex_lock(&json_lock);
3550 request = json_tokener_parse_verbose(buffer, &jerr);
3551 if (jerr != json_tokener_success) {
3552 ERROR("JSON parsing error");
3553 pthread_mutex_unlock(&json_lock);
3554 continue;
3555 }
3556
3557 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
3558 operation = json_object_get_int(js_tmp);
3559 }
3560 pthread_mutex_unlock(&json_lock);
3561 if (operation == -1) {
3562 replies = create_replies();
3563 add_reply(replies, create_error_reply("Missing operation type from frontend."), 0);
3564 goto send_reply;
3565 }
3566
3567 if ((operation < 4) || ((operation > 19) && (operation < 100)) || (operation > 101)) {
3568 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
3569 replies = create_replies();
3570 add_reply(replies, create_error_reply("Operation not supported."), 0);
3571 goto send_reply;
3572 }
3573
3574 DEBUG("operation %d", operation);
3575
3576 /* null global JSON error-reply */
3577 clean_err_reply();
3578
3579 /* clean replies envelope */
3580 if (replies != NULL) {
3581 pthread_mutex_lock(&json_lock);
3582 json_object_put(replies);
3583 pthread_mutex_unlock(&json_lock);
3584 }
3585 replies = create_replies();
3586
3587 if (operation == MSG_CONNECT) {
3588 count = 1;
3589 } else {
3590 pthread_mutex_lock(&json_lock);
3591 if (json_object_object_get_ex(request, "sessions", &sessions) == FALSE) {
3592 add_reply(replies, create_error_reply("Operation missing \"sessions\" arg"), 0);
3593 goto send_reply;
3594 }
3595 count = json_object_array_length(sessions);
3596 pthread_mutex_unlock(&json_lock);
3597 }
3598
3599 for (i = 0; i < count; ++i) {
3600 if (operation != MSG_CONNECT) {
3601 js_tmp = json_object_array_get_idx(sessions, i);
3602 session_key = json_object_get_int(js_tmp);
3603 }
3604
3605 /* process required operation */
3606 switch (operation) {
3607 case MSG_CONNECT:
3608 reply = handle_op_connect(request);
3609 break;
3610 case MSG_DISCONNECT:
3611 reply = handle_op_disconnect(request, session_key);
3612 break;
3613 case MSG_GET:
3614 reply = handle_op_get(request, session_key);
3615 break;
3616 case MSG_GETCONFIG:
3617 reply = handle_op_getconfig(request, session_key);
3618 break;
3619 case MSG_EDITCONFIG:
3620 reply = handle_op_editconfig(request, session_key, i);
3621 break;
3622 case MSG_COPYCONFIG:
3623 reply = handle_op_copyconfig(request, session_key, i);
3624 break;
3625 case MSG_DELETECONFIG:
3626 reply = handle_op_deleteconfig(request, session_key);
3627 break;
3628 case MSG_LOCK:
3629 reply = handle_op_lock(request, session_key);
3630 break;
3631 case MSG_UNLOCK:
3632 reply = handle_op_unlock(request, session_key);
3633 break;
3634 case MSG_KILL:
3635 reply = handle_op_kill(request, session_key);
3636 break;
3637 case MSG_INFO:
3638 reply = handle_op_info(request, session_key);
3639 break;
3640 case MSG_GENERIC:
3641 reply = handle_op_generic(request, session_key, i);
3642 break;
3643 case MSG_GETSCHEMA:
3644 reply = handle_op_getschema(request, session_key);
3645 break;
3646 case MSG_RELOADHELLO:
3647 reply = handle_op_reloadhello(request, session_key);
3648 break;
3649 case MSG_NTF_GETHISTORY:
3650 reply = handle_op_ntfgethistory(request, session_key);
3651 break;
3652 case MSG_VALIDATE:
3653 reply = handle_op_validate(request, session_key);
3654 break;
3655 case SCH_QUERY:
3656 reply = handle_op_query(request, session_key, i);
3657 break;
3658 case SCH_MERGE:
3659 reply = handle_op_merge(request, session_key, i);
3660 break;
3661 }
3662
3663 add_reply(replies, reply, session_key);
3664 }
3665
3666 /* free parameters */
3667 operation = (-1);
3668
3669 DEBUG("Clean request json object.");
3670 if (request != NULL) {
3671 pthread_mutex_lock(&json_lock);
3672 json_object_put(request);
3673 pthread_mutex_unlock(&json_lock);
3674 }
3675 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02003676
3677send_reply:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003678 /* send reply to caller */
3679 if (replies) {
3680 pthread_mutex_lock(&json_lock);
3681 msgtext = json_object_to_json_string(replies);
3682 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
3683 if (buffer != NULL) {
3684 free(buffer);
3685 buffer = NULL;
3686 }
3687 pthread_mutex_unlock(&json_lock);
3688 break;
3689 }
3690 pthread_mutex_unlock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003691
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003692 DEBUG("Send framed reply json object.");
3693 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
3694 DEBUG("Clean reply json object.");
3695 pthread_mutex_lock(&json_lock);
3696 json_object_put(replies);
3697 replies = NULL;
3698 DEBUG("Clean message buffer.");
3699 CHECK_AND_FREE(chunked_out_msg);
3700 chunked_out_msg = NULL;
3701 if (buffer) {
3702 free(buffer);
3703 buffer = NULL;
3704 }
3705 pthread_mutex_unlock(&json_lock);
3706 clean_err_reply();
3707 } else {
3708 ERROR("Reply is NULL, shouldn't be...");
3709 continue;
3710 }
3711 }
3712 }
3713 free(arg);
3714 free_err_reply();
David Kupka8e60a372012-09-04 09:15:20 +02003715
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003716 return retval;
David Kupka8e60a372012-09-04 09:15:20 +02003717}
3718
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003719/**
3720 * \brief Close all open NETCONF sessions.
3721 *
3722 * During termination of mod_netconf, it is useful to close all remaining
3723 * sessions. This function iterates over the list of sessions and close them
3724 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003725 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003726static void
3727close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003728{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003729 struct session_with_mutex *locked_session, *next_session;
3730 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003731
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003732 /* get exclusive access to sessions_list (conns) */
3733 DEBUG("LOCK wrlock %s", __func__);
3734 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
3735 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3736 return;
3737 }
3738 for (next_session = netconf_sessions_list; next_session;) {
3739 locked_session = next_session;
3740 next_session = locked_session->next;
Tomas Cejka47387fd2013-06-10 20:37:46 +02003741
Michal Vaskoc3146782015-11-04 14:46:41 +01003742 /* close_and_free_session handles locking on its own */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003743 DEBUG("Closing NETCONF session %u (SID %s).", locked_session->session_key, nc_session_get_id(locked_session->session));
3744 close_and_free_session(locked_session);
3745 }
3746 netconf_sessions_list = NULL;
3747
3748 /* get exclusive access to sessions_list (conns) */
3749 DEBUG("UNLOCK wrlock %s", __func__);
3750 if (pthread_rwlock_unlock (&session_lock) != 0) {
3751 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3752 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003753}
3754
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003755static void
3756check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003757{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003758 struct nc_session *ns = NULL;
3759 struct session_with_mutex *locked_session = NULL;
3760 time_t current_time = time(NULL);
3761 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003762
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003763 /* get exclusive access to sessions_list (conns) */
3764 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
3765 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3766 return;
3767 }
3768 for (locked_session = netconf_sessions_list; locked_session; locked_session = locked_session->next) {
3769 ns = locked_session->session;
3770 if (ns == NULL) {
3771 continue;
3772 }
3773 pthread_mutex_lock(&locked_session->lock);
3774 if ((current_time - locked_session->last_activity) > ACTIVITY_TIMEOUT) {
3775 DEBUG("Closing NETCONF session %u (SID %s).", locked_session->session_key, nc_session_get_id(locked_session->session));
Tomas Cejka47387fd2013-06-10 20:37:46 +02003776
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003777 /* close_and_free_session handles locking on its own */
3778 close_and_free_session(locked_session);
3779 } else {
3780 pthread_mutex_unlock(&locked_session->lock);
3781 }
3782 }
3783 /* get exclusive access to sessions_list (conns) */
3784 if (pthread_rwlock_unlock(&session_lock) != 0) {
3785 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3786 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003787}
3788
3789
3790/**
Radek Krejcif23850c2012-07-23 16:14:17 +02003791 * This is actually implementation of NETCONF client
3792 * - requests are received from UNIX socket in the predefined format
3793 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01003794 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02003795 *
3796 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003797static void
3798forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02003799{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003800 struct timeval tv;
3801 struct sockaddr_un local, remote;
3802 int lsock, client, ret, i, pthread_count = 0;
3803 unsigned int olds = 0, timediff = 0;
3804 socklen_t len;
3805 struct pass_to_thread *arg;
3806 pthread_t *ptids = calloc(1, sizeof(pthread_t));
3807 struct timespec maxtime;
3808 pthread_rwlockattr_t lock_attrs;
3809 #ifdef WITH_NOTIFICATIONS
3810 char use_notifications = 0;
3811 #endif
David Kupka8e60a372012-09-04 09:15:20 +02003812
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003813 /* wait at most 5 seconds for every thread to terminate */
3814 maxtime.tv_sec = 5;
3815 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02003816
Tomas Cejka04e08f42014-03-27 19:52:34 +01003817#ifdef HAVE_UNIXD_SETUP_CHILD
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003818 /* change uid and gid of process for security reasons */
3819 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01003820#else
3821# ifdef SU_GROUP
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003822 if (strlen(SU_GROUP) > 0) {
3823 struct group *g = getgrnam(SU_GROUP);
3824 if (g == NULL) {
3825 ERROR("GID (%s) was not found.", SU_GROUP);
3826 return;
3827 }
3828 if (setgid(g->gr_gid) != 0) {
3829 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
3830 return;
3831 }
3832 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003833# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003834 DEBUG("no SU_GROUP");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003835# endif
3836# ifdef SU_USER
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003837 if (strlen(SU_USER) > 0) {
3838 struct passwd *p = getpwnam(SU_USER);
3839 if (p == NULL) {
3840 ERROR("UID (%s) was not found.", SU_USER);
3841 return;
3842 }
3843 if (setuid(p->pw_uid) != 0) {
3844 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
3845 return;
3846 }
3847 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003848# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003849 DEBUG("no SU_USER");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003850# endif
3851#endif
Radek Krejci469aab82012-07-22 18:42:20 +02003852
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003853 /* try to remove if exists */
3854 unlink(sockname);
Tomas Cejka04e08f42014-03-27 19:52:34 +01003855
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003856 /* create listening UNIX socket to accept incoming connections */
3857 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
3858 ERROR("Creating socket failed (%s)", strerror(errno));
3859 goto error_exit;
3860 }
Radek Krejci469aab82012-07-22 18:42:20 +02003861
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003862 local.sun_family = AF_UNIX;
3863 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
3864 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
Radek Krejci469aab82012-07-22 18:42:20 +02003865
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003866 if (bind(lsock, (struct sockaddr *)&local, len) == -1) {
3867 if (errno == EADDRINUSE) {
3868 ERROR("mod_netconf socket address already in use");
3869 goto error_exit;
3870 }
3871 ERROR("Binding socket failed (%s)", strerror(errno));
3872 goto error_exit;
3873 }
Radek Krejci469aab82012-07-22 18:42:20 +02003874
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003875 if (listen(lsock, MAX_SOCKET_CL) == -1) {
3876 ERROR("Setting up listen socket failed (%s)", strerror(errno));
3877 goto error_exit;
3878 }
3879 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02003880
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003881 uid_t user = -1;
3882 if (strlen(CHOWN_USER) > 0) {
3883 struct passwd *p = getpwnam(CHOWN_USER);
3884 if (p != NULL) {
3885 user = p->pw_uid;
3886 }
3887 }
3888 gid_t group = -1;
3889 if (strlen(CHOWN_GROUP) > 0) {
3890 struct group *g = getgrnam(CHOWN_GROUP);
3891 if (g != NULL) {
3892 group = g->gr_gid;
3893 }
3894 }
3895 if (chown(sockname, user, group) == -1) {
3896 ERROR("Chown on socket file failed (%s).", strerror(errno));
3897 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003898
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003899 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02003900
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003901 #ifdef WITH_NOTIFICATIONS
3902 if (notification_init() == -1) {
3903 ERROR("libwebsockets initialization failed");
3904 use_notifications = 0;
3905 } else {
3906 use_notifications = 1;
3907 }
3908 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003909
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003910 /* setup libnetconf's callbacks */
3911 nc_verbosity(NC_VERB_DEBUG);
3912 nc_callback_print(clb_print);
3913 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
3914 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
3915 nc_callback_sshauth_password(netconf_callback_sshauth_password);
3916 nc_callback_sshauth_passphrase(netconf_callback_sshauth_passphrase);
3917 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02003918
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003919 /* disable publickey authentication */
3920 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
Radek Krejci469aab82012-07-22 18:42:20 +02003921
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003922 /* create mutex protecting session list */
3923 pthread_rwlockattr_init(&lock_attrs);
3924 /* rwlock is shared only with threads in this process */
3925 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
3926 /* create rw lock */
3927 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
3928 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
3929 goto error_exit;
3930 }
3931 pthread_mutex_init(&ntf_history_lock, NULL);
3932 pthread_mutex_init(&json_lock, NULL);
3933 DEBUG("Initialization of notification history.");
3934 if (pthread_key_create(&notif_history_key, NULL) != 0) {
3935 ERROR("Initialization of notification history failed.");
3936 }
3937 if (pthread_key_create(&err_reply_key, NULL) != 0) {
3938 ERROR("Initialization of reply key failed.");
3939 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02003940
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003941 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
3942 while (isterminated == 0) {
3943 gettimeofday(&tv, NULL);
3944 timediff = (unsigned int)tv.tv_sec - olds;
3945 #ifdef WITH_NOTIFICATIONS
3946 if (use_notifications == 1) {
3947 notification_handle();
3948 }
3949 #endif
3950 if (timediff > ACTIVITY_CHECK_INTERVAL) {
3951 check_timeout_and_close();
3952 }
Radek Krejci469aab82012-07-22 18:42:20 +02003953
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003954 /* open incoming connection if any */
3955 len = sizeof(remote);
3956 client = accept(lsock, (struct sockaddr *) &remote, &len);
3957 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
3958 usleep(SLEEP_TIME * 1000);
3959 continue;
3960 } else if (client == -1 && (errno == EINTR)) {
3961 continue;
3962 } else if (client == -1) {
3963 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
3964 continue;
3965 }
Radek Krejci469aab82012-07-22 18:42:20 +02003966
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003967 /* set client's socket as non-blocking */
3968 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02003969
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003970 arg = malloc(sizeof(struct pass_to_thread));
3971 arg->client = client;
3972 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02003973
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003974 /* start new thread. It will serve this particular request and then terminate */
3975 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void *)arg)) != 0) {
3976 ERROR("Creating POSIX thread failed: %d\n", ret);
3977 } else {
3978 DEBUG("Thread %lu created", ptids[pthread_count]);
3979 pthread_count++;
3980 ptids = realloc (ptids, sizeof(pthread_t) * (pthread_count+1));
3981 ptids[pthread_count] = 0;
3982 }
Radek Krejci469aab82012-07-22 18:42:20 +02003983
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003984 /* check if some thread already terminated, free some resources by joining it */
3985 for (i = 0; i < pthread_count; i++) {
3986 if (pthread_tryjoin_np(ptids[i], (void **)&arg) == 0) {
3987 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
3988 pthread_count--;
3989 if (pthread_count > 0) {
3990 /* place last Thread ID on the place of joined one */
3991 ptids[i] = ptids[pthread_count];
3992 }
3993 }
3994 }
3995 DEBUG("Running %d threads", pthread_count);
3996 }
Radek Krejci469aab82012-07-22 18:42:20 +02003997
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003998 DEBUG("mod_netconf terminating...");
3999 /* join all threads */
4000 for (i = 0; i < pthread_count; i++) {
4001 pthread_timedjoin_np(ptids[i], (void **)&arg, &maxtime);
4002 }
Radek Krejci469aab82012-07-22 18:42:20 +02004003
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004004 #ifdef WITH_NOTIFICATIONS
4005 notification_close();
4006 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01004007
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004008 /* close all NETCONF sessions */
4009 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02004010
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004011 /* destroy rwlock */
4012 pthread_rwlock_destroy(&session_lock);
4013 pthread_rwlockattr_destroy(&lock_attrs);
David Kupka8e60a372012-09-04 09:15:20 +02004014
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004015 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02004016
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004017 free(ptids);
4018 close(lsock);
4019 exit(0);
4020 return;
4021
Tomas Cejkaef531ee2013-11-12 16:07:00 +01004022error_exit:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004023 close(lsock);
4024 free(ptids);
4025 return;
Radek Krejci469aab82012-07-22 18:42:20 +02004026}
4027
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004028int
4029main(int argc, char **argv)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01004030{
Michal Vaskoc3146782015-11-04 14:46:41 +01004031 struct sigaction action;
4032 sigset_t block_mask;
4033
4034 if (argc > 1) {
4035 sockname = argv[1];
4036 } else {
4037 sockname = SOCKET_FILENAME;
4038 }
4039
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004040 sigfillset(&block_mask);
Michal Vaskoc3146782015-11-04 14:46:41 +01004041 action.sa_handler = signal_handler;
4042 action.sa_mask = block_mask;
4043 action.sa_flags = 0;
4044 sigaction(SIGINT, &action, NULL);
4045 sigaction(SIGTERM, &action, NULL);
4046
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004047 forked_proc();
4048 DEBUG("Terminated");
4049 return 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01004050}