blob: 88f6e3d0d742b487d5ed0eb787ac86596c24db2d [file] [log] [blame]
Radek Krejci469aab82012-07-22 18:42:20 +02001/*!
Michal Vasko30aa3822015-11-25 10:41:54 +01002 * \file netopeerguid.c
3 * \brief NetopeerGUI daemon
Radek Krejci469aab82012-07-22 18:42:20 +02004 * \author Tomas Cejka <cejkat@cesnet.cz>
5 * \author Radek Krejci <rkrejci@cesnet.cz>
Michal Vaskoa53ef882015-11-24 11:02:01 +01006 * \author Michal Vasko <mvasko@cesnet.cz>
Radek Krejci469aab82012-07-22 18:42:20 +02007 * \date 2011
8 * \date 2012
Tomas Cejka94da2c52013-01-08 18:20:30 +01009 * \date 2013
Michal Vaskoa53ef882015-11-24 11:02:01 +010010 * \date 2015
Radek Krejci469aab82012-07-22 18:42:20 +020011 */
12/*
Michal Vaskoa53ef882015-11-24 11:02:01 +010013 * Copyright (C) 2011-2015 CESNET
Radek Krejci469aab82012-07-22 18:42:20 +020014 *
15 * LICENSE TERMS
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in
24 * the documentation and/or other materials provided with the
25 * distribution.
26 * 3. Neither the name of the Company nor the names of its contributors
27 * may be used to endorse or promote products derived from this
28 * software without specific prior written permission.
29 *
30 * ALTERNATIVELY, provided that this notice is retained in full, this
31 * product may be distributed under the terms of the GNU General Public
32 * License (GPL) version 2 or later, in which case the provisions
33 * of the GPL apply INSTEAD OF those given above.
34 *
35 * This software is provided ``as is'', and any express or implied
36 * warranties, including, but not limited to, the implied warranties of
37 * merchantability and fitness for a particular purpose are disclaimed.
38 * In no event shall the company or contributors be liable for any
39 * direct, indirect, incidental, special, exemplary, or consequential
40 * damages (including, but not limited to, procurement of substitute
41 * goods or services; loss of use, data, or profits; or business
42 * interruption) however caused and on any theory of liability, whether
43 * in contract, strict liability, or tort (including negligence or
44 * otherwise) arising in any way out of the use of this software, even
45 * if advised of the possibility of such damage.
46 *
47 */
Michal Vaskoc3146782015-11-04 14:46:41 +010048#define _GNU_SOURCE
Radek Krejci469aab82012-07-22 18:42:20 +020049
Radek Krejci7b4ddd02012-07-30 08:09:58 +020050#include <unistd.h>
51#include <poll.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010052#include <time.h>
Radek Krejci469aab82012-07-22 18:42:20 +020053#include <sys/types.h>
54#include <sys/socket.h>
55#include <sys/un.h>
Tomas Cejkad340dbf2013-03-24 20:36:57 +010056#include <sys/fcntl.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010057#include <sys/stat.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010058#include <pwd.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010059#include <errno.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010060#include <limits.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010061#include <grp.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010062#include <signal.h>
David Kupka8e60a372012-09-04 09:15:20 +020063#include <pthread.h>
64#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020065
Radek Krejci469aab82012-07-22 18:42:20 +020066#include <libnetconf.h>
Tomas Cejkab3cc64f2013-05-03 19:44:54 +020067#include <libnetconf_ssh.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020068
Tomas Cejka04e08f42014-03-27 19:52:34 +010069#include "../config.h"
70
Tomas Cejkad340dbf2013-03-24 20:36:57 +010071#ifdef WITH_NOTIFICATIONS
Michal Vaskoa53ef882015-11-24 11:02:01 +010072#include "notification_server.h"
Tomas Cejkad340dbf2013-03-24 20:36:57 +010073#endif
74
Tomas Cejka94da2c52013-01-08 18:20:30 +010075#include "message_type.h"
Michal Vaskoa53ef882015-11-24 11:02:01 +010076#include "netopeerguid.h"
Radek Krejci469aab82012-07-22 18:42:20 +020077
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010078#define SCHEMA_DIR "/tmp/yang_models"
Radek Krejci469aab82012-07-22 18:42:20 +020079#define MAX_PROCS 5
Michal Vaskoa53ef882015-11-24 11:02:01 +010080#define SOCKET_FILENAME "/var/run/netopeerguid.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020081#define MAX_SOCKET_CL 10
82#define BUFFER_SIZE 4096
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010083#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
84#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020085
Michal Vasko7732dee2015-11-05 10:22:15 +010086/* sleep in master process for non-blocking socket reading, in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020087#define SLEEP_TIME 200
88
89#ifndef offsetof
90#define offsetof(type, member) ((size_t) ((type *) 0)->member)
91#endif
92
Tomas Cejka027f3bc2012-11-10 20:28:36 +010093/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020094struct timeval timeout = { 1, 0 };
95
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010096#define NCWITHDEFAULTS NCWD_MODE_NOTSET
Tomas Cejka5064c232013-01-17 09:30:58 +010097
Radek Krejci469aab82012-07-22 18:42:20 +020098#define MSG_OK 0
99#define MSG_OPEN 1
100#define MSG_DATA 2
101#define MSG_CLOSE 3
102#define MSG_ERROR 4
103#define MSG_UNKNOWN 5
104
Tomas Cejka47387fd2013-06-10 20:37:46 +0200105pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200106pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100107pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100108pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
109
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100110unsigned int session_key_generator = 1;
Michal Vaskoc3146782015-11-04 14:46:41 +0100111struct session_with_mutex *netconf_sessions_list = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +0100112static const char *sockname;
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200113static pthread_key_t notif_history_key;
Tomas Cejka442258e2014-04-01 18:17:18 +0200114pthread_key_t err_reply_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200115volatile int isterminated = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200116static char* password;
117
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100118json_object *create_ok_reply(void);
119json_object *create_data_reply(const char *data);
120static char *netconf_getschema(unsigned int session_key, const char *identifier, const char *version,
121 const char *format, json_object **err);
122static void node_add_metadata_recursive(struct lyd_node *data_tree, struct lys_module *module,
123 json_object *data_json_parent);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100124static void node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100125
126static void
127signal_handler(int sign)
Radek Krejci469aab82012-07-22 18:42:20 +0200128{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100129 switch (sign) {
130 case SIGINT:
131 case SIGTERM:
132 isterminated = 1;
133 break;
134 }
Radek Krejci469aab82012-07-22 18:42:20 +0200135}
136
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100137int
138netconf_callback_ssh_hostkey_check(const char* UNUSED(hostname), ssh_session UNUSED(session))
Radek Krejci469aab82012-07-22 18:42:20 +0200139{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100140 /* always approve */
141 return (EXIT_SUCCESS);
Radek Krejci469aab82012-07-22 18:42:20 +0200142}
143
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100144char *
145netconf_callback_sshauth_passphrase(const char *UNUSED(username), const char *UNUSED(hostname), const char *UNUSED(priv_key_file))
Radek Krejci469aab82012-07-22 18:42:20 +0200146{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100147 char *buf;
148 buf = strdup(password);
149 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200150}
151
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100152char *
153netconf_callback_sshauth_password(const char *UNUSED(username), const char *UNUSED(hostname))
Radek Krejci469aab82012-07-22 18:42:20 +0200154{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100155 char *buf;
156 buf = strdup(password);
157 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200158}
159
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100160char *
161netconf_callback_sshauth_interactive(const char *UNUSED(name), const char *UNUSED(instruction),
162 const char *UNUSED(prompt), int UNUSED(echo))
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200163{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100164 char *buf;
165 buf = strdup(password);
166 return (buf);
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200167}
168
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100169void
170netconf_callback_error_process(const char *UNUSED(tag),
171 const char *UNUSED(type),
172 const char *UNUSED(severity),
173 const char *UNUSED(apptag),
174 const char *UNUSED(path),
175 const char *message,
176 const char *UNUSED(attribute),
177 const char *UNUSED(element),
178 const char *UNUSED(ns),
179 const char *UNUSED(sid))
Radek Krejcic11fd862012-07-26 12:41:21 +0200180{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100181 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
182 if (err_reply_p == NULL) {
183 ERROR("Error message was not allocated. %s", __func__);
184 return;
185 }
186 json_object *err_reply = *err_reply_p;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100187
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100188 json_object *array = NULL;
189 if (err_reply == NULL) {
190 ERROR("error calback: empty error list");
191 pthread_mutex_lock(&json_lock);
192 err_reply = json_object_new_object();
193 array = json_object_new_array();
194 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
195 json_object_object_add(err_reply, "errors", array);
196 if (message != NULL) {
197 json_object_array_add(array, json_object_new_string(message));
198 }
199 pthread_mutex_unlock(&json_lock);
200 (*err_reply_p) = err_reply;
201 } else {
202 ERROR("error calback: nonempty error list");
203 pthread_mutex_lock(&json_lock);
204 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
205 if (message != NULL) {
206 json_object_array_add(array, json_object_new_string(message));
207 }
208 }
209 pthread_mutex_unlock(&json_lock);
210 }
211 pthread_setspecific(err_reply_key, err_reply_p);
212 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200213}
214
Tomas Cejka47387fd2013-06-10 20:37:46 +0200215/**
216 * should be used in locked area
217 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100218void
219prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200220{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100221 json_object *json_obj = NULL;
222 json_object *js_tmp = NULL;
223 char *old_sid = NULL;
224 const char *j_old_sid = NULL;
225 const char *cpbltstr;
226 struct nc_cpblts* cpblts = NULL;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200227
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100228 if (s == NULL) {
229 ERROR("No session given.");
230 return;
231 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200232
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100233 pthread_mutex_lock(&json_lock);
234 if (s->hello_message != NULL) {
235 ERROR("clean previous hello message");
236 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
237 j_old_sid = json_object_get_string(js_tmp);
238 if (j_old_sid != NULL) {
239 old_sid = strdup(j_old_sid);
240 }
241 }
242 json_object_put(s->hello_message);
243 s->hello_message = NULL;
244 }
245 s->hello_message = json_object_new_object();
246 if (session != NULL) {
247 if (old_sid != NULL) {
248 /* use previous sid */
249 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
250 free(old_sid);
251 old_sid = NULL;
252 } else {
253 /* we don't have old sid */
254 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
255 }
256 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
257 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
258 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
259 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
260 cpblts = nc_session_get_cpblts (session);
261 if (cpblts != NULL) {
262 json_obj = json_object_new_array();
263 nc_cpblts_iter_start (cpblts);
264 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
265 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
266 }
267 json_object_object_add(s->hello_message, "capabilities", json_obj);
268 }
269 DEBUG("%s", json_object_to_json_string(s->hello_message));
270 } else {
271 ERROR("Session was not given.");
272 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
273 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
274 }
275 DEBUG("Status info from hello message prepared");
276 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200277
278}
279
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100280void
281create_err_reply_p()
Tomas Cejka442258e2014-04-01 18:17:18 +0200282{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100283 json_object **err_reply = calloc(1, sizeof(json_object **));
284 if (err_reply == NULL) {
285 ERROR("Allocation of err_reply storage failed!");
286 return;
287 }
288 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
289 ERROR("cannot set thread-specific value.");
290 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200291}
292
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100293void
294clean_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200295{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100296 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
297 if (err_reply != NULL) {
298 if (*err_reply != NULL) {
299 pthread_mutex_lock(&json_lock);
300 json_object_put(*err_reply);
301 pthread_mutex_unlock(&json_lock);
302 }
303 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
304 ERROR("Cannot set thread-specific hash value.");
305 }
306 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200307}
308
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100309void
310free_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200311{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100312 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
313 if (err_reply != NULL) {
314 if (*err_reply != NULL) {
315 pthread_mutex_lock(&json_lock);
316 json_object_put(*err_reply);
317 pthread_mutex_unlock(&json_lock);
318 }
319 free(err_reply);
320 err_reply = NULL;
321 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
322 ERROR("Cannot set thread-specific hash value.");
323 }
324 }
325}
326
327static struct session_with_mutex *
328session_get_locked(unsigned int session_key, json_object **err)
329{
330 struct session_with_mutex *locked_session;
331
332 /* get non-exclusive (read) access to sessions_list (conns) */
333 DEBUG("LOCK wrlock %s", __func__);
334 if (pthread_rwlock_rdlock(&session_lock) != 0) {
335 if (*err) {
336 *err = create_error_reply("Locking failed.");
337 }
338 return NULL;
339 }
340 /* get session where send the RPC */
341 for (locked_session = netconf_sessions_list;
342 locked_session && (locked_session->session_key != session_key);
343 locked_session = locked_session->next);
344 if (!locked_session) {
345 if (*err) {
346 *err = create_error_reply("Session not found.");
347 }
348 return NULL;
349 }
350
351 /* get exclusive access to session */
352 DEBUG("LOCK mutex %s", __func__);
353 if (pthread_mutex_lock(&locked_session->lock) != 0) {
354 if (*err) {
355 *err = create_error_reply("Locking failed.");
356 }
357 goto wrlock_fail;
358 }
359 return locked_session;
360
361wrlock_fail:
362 DEBUG("UNLOCK wrlock %s", __func__);
363 pthread_rwlock_unlock(&session_lock);
364 return NULL;
365}
366
367static void
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100368session_user_activity(const char *username)
369{
370 struct session_with_mutex *sess;
371
372 for (sess = netconf_sessions_list; sess; sess = sess->next) {
373 if (!strcmp(nc_session_get_user(sess->session), username)) {
374 sess->last_activity = time(NULL);
375 }
376 }
377}
378
379static void
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100380session_unlock(struct session_with_mutex *locked_session)
381{
382 DEBUG("UNLOCK mutex %s", __func__);
383 pthread_mutex_unlock(&locked_session->lock);
384 DEBUG("UNLOCK wrlock %s", __func__);
385 pthread_rwlock_unlock(&session_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +0200386}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200387
Michal Vasko3fda9a92015-11-23 10:10:57 +0100388static void
389node_metadata_text(const char *text, const char *name, json_object *parent)
390{
391 json_object *obj;
392
393 if (!text) {
394 return;
395 }
396
397 obj = json_object_new_string(text);
398 json_object_object_add(parent, name, obj);
399}
400
401static void
402node_metadata_restr(struct lys_restr *restr, const char *name, json_object *parent)
403{
404 json_object *obj;
405
406 if (!restr) {
407 return;
408 }
409
410 obj = json_object_new_string(restr->expr);
411 json_object_object_add(parent, name, obj);
412}
413
414static void
415node_metadata_must(uint8_t must_size, struct lys_restr *must, json_object *parent)
416{
417 uint8_t i;
418 json_object *array, *obj;
419
420 if (!must_size || !must) {
421 return;
422 }
423
424 array = json_object_new_array();
425
426 for (i = 0; i < must_size; ++i) {
427 obj = json_object_new_string(must[i].expr);
428 json_object_array_add(array, obj);
429 }
430
431 json_object_object_add(parent, "must", array);
432}
433
434static void
435node_metadata_basic(struct lys_node *node, json_object *parent)
436{
437 json_object *obj;
438
439 /* description */
440 node_metadata_text(node->dsc, "description", parent);
441
442 /* reference */
443 node_metadata_text(node->ref, "reference", parent);
444
445 /* config */
446 if (node->flags & LYS_CONFIG_R) {
447 obj = json_object_new_boolean(0);
448 } else {
449 obj = json_object_new_boolean(1);
450 }
451 json_object_object_add(parent, "config", obj);
452
453 /* status */
454 if (node->flags & LYS_STATUS_DEPRC) {
455 obj = json_object_new_string("deprecated");
456 } else if (node->flags & LYS_STATUS_OBSLT) {
457 obj = json_object_new_string("obsolete");
458 } else {
459 obj = json_object_new_string("current");
460 }
461 json_object_object_add(parent, "status", obj);
462
463 /* mandatory */
464 if (node->flags & LYS_MAND_TRUE) {
465 obj = json_object_new_boolean(1);
466 } else {
467 obj = json_object_new_boolean(0);
468 }
469 json_object_object_add(parent, "mandatory", obj);
470
471 /* NACM extensions */
472 if (node->nacm) {
473 if (node->nacm & LYS_NACM_DENYW) {
474 obj = json_object_new_string("default-deny-write");
475 } else {
476 obj = json_object_new_string("default-deny-all");
477 }
478 json_object_object_add(parent, "ext", obj);
479 }
480}
481
482static void
483node_metadata_when(struct lys_when *when, json_object *parent)
484{
485 json_object *obj;
486
487 if (!when) {
488 return;
489 }
490
491 obj = json_object_new_string(when->cond);
492 json_object_object_add(parent, "when", obj);
493}
494
495static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100496node_metadata_children_recursive(struct lys_node *node, json_object **child_array, json_object **choice_array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100497{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100498 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100499 struct lys_node *child;
500
501 if (!node->child) {
502 return;
503 }
504
505 LY_TREE_FOR(node->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100506 if (child->nodetype == LYS_USES) {
507 node_metadata_children_recursive(child, child_array, choice_array);
508 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100509 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100510 if (!*child_array) {
511 *child_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100512 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100513 json_object_array_add(*child_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100514 } else if (child->nodetype == LYS_CHOICE) {
515 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100516 if (!*choice_array) {
517 *choice_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100518 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100519 json_object_array_add(*choice_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100520 }
521 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100522}
523
524static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100525node_metadata_cases_recursive(struct lys_node_choice *choice, json_object *array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100526{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100527 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100528 struct lys_node *child;
529
530 if (!choice->child) {
531 return;
532 }
533
Michal Vasko3fda9a92015-11-23 10:10:57 +0100534 LY_TREE_FOR(choice->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100535 if (child->nodetype == LYS_USES) {
536 node_metadata_cases_recursive((struct lys_node_choice *)child, array);
537 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_CASE)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100538 obj = json_object_new_string(child->name);
539 json_object_array_add(array, obj);
540 }
541 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100542}
543
544static void
545node_metadata_min_max(uint32_t min, uint32_t max, json_object *parent)
546{
547 json_object *obj;
548
549 if (min) {
550 obj = json_object_new_int(min);
551 json_object_object_add(parent, "min-elements", obj);
552 }
553
554 if (max) {
555 obj = json_object_new_int(max);
556 json_object_object_add(parent, "max-elements", obj);
557 }
558}
559
560static void
561node_metadata_ident_recursive(struct lys_ident *ident, json_object *array)
562{
563 struct lys_ident_der *cur;
564 json_object *obj;
565
566 if (!ident) {
567 return;
568 }
569
570 obj = json_object_new_string(ident->name);
571 json_object_array_add(array, obj);
572
573 for (cur = ident->der; cur; cur = cur->next) {
574 node_metadata_ident_recursive(cur->ident, array);
575 }
576}
577
578static void
579node_metadata_type(struct lys_type *type, struct lys_module *module, json_object *parent)
580{
581 json_object *obj, *array, *item;
582 char *str;
583 int i;
584
585 /* built-in YANG type */
586 if (!type->der->module) {
587 switch (type->base) {
588 case LY_TYPE_BINARY:
589 node_metadata_text("binary", "type", parent);
590 node_metadata_restr(type->info.binary.length, "length", parent);
591 break;
592 case LY_TYPE_BITS:
593 node_metadata_text("bits", "type", parent);
594
595 array = json_object_new_array();
596 for (i = 0; i < type->info.bits.count; ++i) {
597 item = json_object_new_object();
598 obj = json_object_new_string(type->info.bits.bit[i].name);
599 json_object_object_add(item, "name", obj);
600 obj = json_object_new_int(type->info.bits.bit[i].pos);
601 json_object_object_add(item, "position", obj);
602 json_object_array_add(array, item);
603 }
604 json_object_object_add(parent, "bits", array);
605 break;
606 case LY_TYPE_BOOL:
607 node_metadata_text("bool", "type", parent);
608 break;
609 case LY_TYPE_DEC64:
610 node_metadata_text("decimal64", "type", parent);
611 node_metadata_restr(type->info.dec64.range, "range", parent);
612 obj = json_object_new_int(type->info.dec64.dig);
613 json_object_object_add(parent, "fraction-digits", obj);
614 break;
615 case LY_TYPE_EMPTY:
616 node_metadata_text("empty", "type", parent);
617 break;
618 case LY_TYPE_ENUM:
619 node_metadata_text("enumeration", "type", parent);
620
621 array = json_object_new_array();
622 for (i = 0; i < type->info.enums.count; ++i) {
623 obj = json_object_new_string(type->info.enums.enm[i].name);
624 json_object_array_add(array, obj);
625 }
626 json_object_object_add(parent, "enumval", array);
627 break;
628 case LY_TYPE_IDENT:
629 node_metadata_text("identityref", "type", parent);
630
631 array = json_object_new_array();
632 node_metadata_ident_recursive(type->info.ident.ref, array);
633 json_object_object_add(parent, "identityval", array);
634 break;
635 case LY_TYPE_INST:
636 node_metadata_text("instance-identifier", "type", parent);
637 if (type->info.inst.req == -1) {
638 obj = json_object_new_boolean(0);
639 } else {
640 obj = json_object_new_boolean(1);
641 }
642 json_object_object_add(parent, "require-instance", obj);
643 break;
644 case LY_TYPE_LEAFREF:
645 node_metadata_text("leafref", "type", parent);
646 node_metadata_text(type->info.lref.path, "path", parent);
647 break;
648 case LY_TYPE_STRING:
649 node_metadata_text("string", "type", parent);
650 node_metadata_restr(type->info.str.length, "length", parent);
651 if (type->info.str.pat_count) {
652 array = json_object_new_array();
653 for (i = 0; i < type->info.str.pat_count; ++i) {
654 obj = json_object_new_string(type->info.str.patterns[i].expr);
655 json_object_array_add(array, obj);
656 }
657 json_object_object_add(parent, "pattern", array);
658 }
659 break;
660 case LY_TYPE_UNION:
661 node_metadata_text("union", "type", parent);
662 array = json_object_new_array();
663 for (i = 0; i < type->info.uni.count; ++i) {
664 obj = json_object_new_object();
665 node_metadata_type(&type->info.uni.types[i], module, obj);
666 json_object_array_add(array, obj);
667 }
668 json_object_object_add(parent, "types", array);
669 break;
670 case LY_TYPE_INT8:
671 node_metadata_text("int8", "type", parent);
672 node_metadata_restr(type->info.num.range, "range", parent);
673 break;
674 case LY_TYPE_UINT8:
675 node_metadata_text("uint8", "type", parent);
676 node_metadata_restr(type->info.num.range, "range", parent);
677 break;
678 case LY_TYPE_INT16:
679 node_metadata_text("int16", "type", parent);
680 node_metadata_restr(type->info.num.range, "range", parent);
681 break;
682 case LY_TYPE_UINT16:
683 node_metadata_text("uint16", "type", parent);
684 node_metadata_restr(type->info.num.range, "range", parent);
685 break;
686 case LY_TYPE_INT32:
687 node_metadata_text("int32", "type", parent);
688 node_metadata_restr(type->info.num.range, "range", parent);
689 break;
690 case LY_TYPE_UINT32:
691 node_metadata_text("uint32", "type", parent);
692 node_metadata_restr(type->info.num.range, "range", parent);
693 break;
694 case LY_TYPE_INT64:
695 node_metadata_text("int64", "type", parent);
696 node_metadata_restr(type->info.num.range, "range", parent);
697 break;
698 case LY_TYPE_UINT64:
699 node_metadata_text("uint64", "type", parent);
700 node_metadata_restr(type->info.num.range, "range", parent);
701 break;
702 default:
703 ERROR("Internal: unknown type (%s:%d)", __FILE__, __LINE__);
704 break;
705 }
706
707 /* typedef */
708 } else {
709 if (!module || !type->module_name || !strcmp(type->module_name, module->name)) {
710 node_metadata_text(type->der->name, "type", parent);
711 } else {
712 asprintf(&str, "%s:%s", type->module_name, type->der->name);
713 node_metadata_text(str, "type", parent);
714 free(str);
715 }
716 obj = json_object_new_object();
717 node_metadata_typedef(type->der, obj);
718 json_object_object_add(parent, "typedef", obj);
719 }
720}
721
722static void
723node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent)
724{
725 json_object *obj;
726
727 /* description */
728 node_metadata_text(tpdf->dsc, "description", parent);
729
730 /* reference */
731 node_metadata_text(tpdf->ref, "reference", parent);
732
733 /* status */
734 if (tpdf->flags & LYS_STATUS_DEPRC) {
735 obj = json_object_new_string("deprecated");
736 } else if (tpdf->flags & LYS_STATUS_OBSLT) {
737 obj = json_object_new_string("obsolete");
738 } else {
739 obj = json_object_new_string("current");
740 }
741 json_object_object_add(parent, "status", obj);
742
743 /* type */
744 node_metadata_type(&tpdf->type, tpdf->module, parent);
745
746 /* units */
747 node_metadata_text(tpdf->units, "units", parent);
748
749 /* default */
750 node_metadata_text(tpdf->dflt, "default", parent);
751}
752
753static void
754node_metadata_container(struct lys_node_container *cont, json_object *parent)
755{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100756 json_object *obj, *child_array = NULL, *choice_array = NULL;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100757
758 /* element type */
759 obj = json_object_new_string("container");
760 json_object_object_add(parent, "eltype", obj);
761
762 /* shared info */
763 node_metadata_basic((struct lys_node *)cont, parent);
764
765 /* must */
766 node_metadata_must(cont->must_size, cont->must, parent);
767
768 /* presence */
769 node_metadata_text(cont->presence, "presence", parent);
770
771 /* when */
772 node_metadata_when(cont->when, parent);
773
774 /* children & choice */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100775 node_metadata_children_recursive((struct lys_node *)cont, &child_array, &choice_array);
776 if (child_array) {
777 json_object_object_add(parent, "children", child_array);
778 }
779 if (choice_array) {
780 json_object_object_add(parent, "choice", choice_array);
781 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100782}
783
784static void
785node_metadata_choice(struct lys_node_choice *choice, json_object *parent)
786{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100787 json_object *obj, *array;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100788
789 /* element type */
790 obj = json_object_new_string("choice");
791 json_object_object_add(parent, "eltype", obj);
792
793 /* shared info */
794 node_metadata_basic((struct lys_node *)choice, parent);
795
796 /* default */
797 node_metadata_text(choice->dflt->name, "default", parent);
798
799 /* when */
800 node_metadata_when(choice->when, parent);
801
802 /* cases */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100803 if (choice->child) {
804 array = json_object_new_array();
805 node_metadata_cases_recursive(choice, array);
806 json_object_object_add(parent, "cases", array);
807 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100808}
809
810static void
811node_metadata_leaf(struct lys_node_leaf *leaf, json_object *parent)
812{
813 json_object *obj;
814 struct lys_node_list *list;
815 int is_key, i;
816
817 /* element type */
818 obj = json_object_new_string("leaf");
819 json_object_object_add(parent, "eltype", obj);
820
821 /* shared info */
822 node_metadata_basic((struct lys_node *)leaf, parent);
823
824 /* type */
825 node_metadata_type(&leaf->type, leaf->module, parent);
826
827 /* units */
828 node_metadata_text(leaf->units, "units", parent);
829
830 /* default */
831 node_metadata_text(leaf->dflt, "default", parent);
832
833 /* must */
834 node_metadata_must(leaf->must_size, leaf->must, parent);
835
836 /* when */
837 node_metadata_when(leaf->when, parent);
838
839 /* iskey */
840 is_key = 0;
841 list = (struct lys_node_list *)lys_parent((struct lys_node *)leaf);
842 if (list && (list->nodetype == LYS_LIST)) {
843 for (i = 0; i < list->keys_size; ++i) {
844 if (list->keys[i] == leaf) {
845 is_key = 1;
846 break;
847 }
848 }
849 }
850 obj = json_object_new_boolean(is_key);
851 json_object_object_add(parent, "iskey", obj);
852}
853
854static void
855node_metadata_leaflist(struct lys_node_leaflist *llist, json_object *parent)
856{
857 json_object *obj;
858
859 /* element type */
860 obj = json_object_new_string("leaf-list");
861 json_object_object_add(parent, "eltype", obj);
862
863 /* shared info */
864 node_metadata_basic((struct lys_node *)llist, parent);
865
866 /* type */
867 node_metadata_type(&llist->type, llist->module, parent);
868
869 /* units */
870 node_metadata_text(llist->units, "units", parent);
871
872 /* must */
873 node_metadata_must(llist->must_size, llist->must, parent);
874
875 /* when */
876 node_metadata_when(llist->when, parent);
877
878 /* min/max-elements */
879 node_metadata_min_max(llist->min, llist->max, parent);
880}
881
882static void
883node_metadata_list(struct lys_node_list *list, json_object *parent)
884{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100885 json_object *obj, *array, *child_array = NULL, *choice_array = NULL;;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100886 int i;
887 unsigned int j;
888
889 /* element type */
890 obj = json_object_new_string("list");
891 json_object_object_add(parent, "eltype", obj);
892
893 /* shared info */
894 node_metadata_basic((struct lys_node *)list, parent);
895
896 /* must */
897 node_metadata_must(list->must_size, list->must, parent);
898
899 /* when */
900 node_metadata_when(list->when, parent);
901
902 /* min/max-elements */
903 node_metadata_min_max(list->min, list->max, parent);
904
905 /* keys */
906 if (list->keys_size) {
907 array = json_object_new_array();
908 for (i = 0; i < list->keys_size; ++i) {
909 obj = json_object_new_string(list->keys[i]->name);
910 json_object_array_add(array, obj);
911 }
912 json_object_object_add(parent, "keys", array);
913 }
914
915 /* unique */
916 if (list->unique_size) {
917 array = json_object_new_array();
918 for (i = 0; i < list->unique_size; ++i) {
919 for (j = 0; j < list->unique[i].expr_size; ++j) {
920 obj = json_object_new_string(list->unique[i].expr[j]);
921 json_object_array_add(array, obj);
922 }
923 }
924 json_object_object_add(parent, "unique", array);
925 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100926
927 /* children & choice */
928 node_metadata_children_recursive((struct lys_node *)list, &child_array, &choice_array);
929 if (child_array) {
930 json_object_object_add(parent, "children", child_array);
931 }
932 if (choice_array) {
933 json_object_object_add(parent, "choice", choice_array);
934 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100935}
936
937static void
938node_metadata_anyxml(struct lys_node_anyxml *anyxml, json_object *parent)
939{
940 json_object *obj;
941
942 /* element type */
943 obj = json_object_new_string("anyxml");
944 json_object_object_add(parent, "eltype", obj);
945
946 /* shared info */
947 node_metadata_basic((struct lys_node *)anyxml, parent);
948
949 /* must */
950 node_metadata_must(anyxml->must_size, anyxml->must, parent);
951
952 /* when */
953 node_metadata_when(anyxml->when, parent);
954
955}
956
957static void
958node_metadata_case(struct lys_node_case *cas, json_object *parent)
959{
960 json_object *obj;
961
962 /* element type */
963 obj = json_object_new_string("case");
964 json_object_object_add(parent, "eltype", obj);
965
966 /* shared info */
967 node_metadata_basic((struct lys_node *)cas, parent);
968
969 /* when */
970 node_metadata_when(cas->when, parent);
971}
972
Michal Vaskoa45770b2015-11-23 15:49:41 +0100973static void
974node_metadata_rpc(struct lys_node_rpc *rpc, json_object *parent)
975{
976 json_object *obj;
977
978 /* element type */
979 obj = json_object_new_string("rpc");
980 json_object_object_add(parent, "eltype", obj);
981
982 /* description */
983 node_metadata_text(rpc->dsc, "description", parent);
984
985 /* reference */
986 node_metadata_text(rpc->ref, "reference", parent);
987
988 /* status */
989 if (rpc->flags & LYS_STATUS_DEPRC) {
990 obj = json_object_new_string("deprecated");
991 } else if (rpc->flags & LYS_STATUS_OBSLT) {
992 obj = json_object_new_string("obsolete");
993 } else {
994 obj = json_object_new_string("current");
995 }
996 json_object_object_add(parent, "status", obj);
997}
998
999static void
1000node_metadata_model(struct lys_module *module, json_object *parent)
1001{
1002 json_object *obj, *array, *item;
1003 int i;
1004
1005 /* yang-version */
1006 if (module->version == 2) {
1007 obj = json_object_new_string("1.1");
1008 } else {
1009 obj = json_object_new_string("1.0");
1010 }
1011 json_object_object_add(parent, "yang-version", obj);
1012
1013 /* namespace */
1014 node_metadata_text(module->ns, "namespace", parent);
1015
1016 /* prefix */
1017 node_metadata_text(module->prefix, "prefix", parent);
1018
1019 /* contact */
1020 node_metadata_text(module->contact, "contact", parent);
1021
1022 /* organization */
1023 node_metadata_text(module->org, "organization", parent);
1024
1025 /* revision */
1026 if (module->rev_size) {
1027 node_metadata_text(module->rev[0].date, "revision", parent);
1028 }
1029
1030 /* description */
1031 node_metadata_text(module->dsc, "description", parent);
1032
1033 /* import */
1034 if (module->imp_size) {
1035 array = json_object_new_array();
1036 for (i = 0; i < module->imp_size; ++i) {
1037 item = json_object_new_object();
1038
1039 node_metadata_text(module->imp[i].module->name, "name", item);
1040 node_metadata_text(module->imp[i].prefix, "prefix", item);
1041 if (module->imp[i].rev && module->imp[i].rev[0]) {
1042 node_metadata_text(module->imp[i].rev, "revision", item);
1043 }
1044
1045 json_object_array_add(array, item);
1046 }
1047 json_object_object_add(parent, "imports", array);
1048 }
1049
1050 /* include */
1051 if (module->inc_size) {
1052 array = json_object_new_array();
1053 for (i = 0; i < module->inc_size; ++i) {
1054 item = json_object_new_object();
1055
1056 node_metadata_text(module->inc[i].submodule->name, "name", item);
1057 if (module->inc[i].rev && module->inc[i].rev[0]) {
1058 node_metadata_text(module->inc[i].rev, "revision", item);
1059 }
1060
1061 json_object_array_add(array, item);
1062 }
1063 json_object_object_add(parent, "includes", array);
1064 }
1065}
1066
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001067/**
1068 * \defgroup netconf_operations NETCONF operations
1069 * The list of NETCONF operations that mod_netconf supports.
1070 * @{
1071 */
1072
1073/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001074 * \brief Send RPC and wait for reply with timeout.
1075 *
1076 * \param[in] session libnetconf session
1077 * \param[in] rpc prepared RPC message
1078 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
1079 * \param[out] reply reply from the server
1080 * \return Value from nc_session_recv_reply() or NC_MSG_UNKNOWN when send_rpc() fails.
1081 * On success, it returns NC_MSG_REPLY.
1082 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001083NC_MSG_TYPE
1084netconf_send_recv_timed(struct nc_session *session, nc_rpc *rpc, int timeout, nc_reply **reply)
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001085{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001086 const nc_msgid msgid = NULL;
1087 NC_MSG_TYPE ret = NC_MSG_UNKNOWN;
1088 msgid = nc_session_send_rpc(session, rpc);
1089 if (msgid == NULL) {
1090 return ret;
1091 }
1092 do {
1093 ret = nc_session_recv_reply(session, timeout, reply);
1094 if (ret == NC_MSG_HELLO) {
1095 ERROR("<hello> received instead reply, it will be lost.");
1096 nc_reply_free(*reply);
1097 }
1098 if (ret == NC_MSG_WOULDBLOCK) {
1099 ERROR("Timeout for receiving RPC reply expired.");
1100 break;
1101 }
1102 } while (ret == NC_MSG_HELLO || ret == NC_MSG_NOTIFICATION);
1103 return ret;
1104}
1105
1106static int
1107ctx_download_module(struct session_with_mutex *session, const char *model_name, const char *revision, const char *schema_dir)
1108{
1109 json_object *err = NULL;
1110 char *model_data = NULL, *model_path;
1111 size_t length;
1112 FILE *file;
1113
1114 DEBUG("UNLOCK rwlock %s", __func__);
1115 if (pthread_rwlock_unlock(&session_lock) != 0) {
1116 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1117 return 1;
1118 }
1119
1120 model_data = netconf_getschema(session->session_key, model_name, revision, "yin", &err);
1121
1122 DEBUG("LOCK rwlock %s", __func__);
1123 if (pthread_rwlock_wrlock(&session_lock) != 0) {
1124 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1125 return 1;
1126 }
1127
1128 if (!model_data) {
1129 if (err) {
1130 json_object_put(err);
1131 }
1132 ERROR("Failed to get-schema of \"%s\".", model_name);
1133 return 1;
1134 }
1135
1136 if (revision) {
1137 asprintf(&model_path, "%s/%s@%s.yin", schema_dir, model_name, revision);
1138 } else {
1139 asprintf(&model_path, "%s/%s.yin", schema_dir, model_name);
1140 }
1141
1142 file = fopen(model_path, "w");
1143 if (!file) {
1144 ERROR("Failed to open \"%s\" for writing (%s).", model_path, strerror(errno));
1145 free(model_data);
1146 free(model_path);
1147 return 1;
1148 }
1149 free(model_path);
1150
1151 length = strlen(model_data);
1152 if (fwrite(model_data, 1, length, file) < length) {
1153 ERROR("Failed to store the model \"%s\".", model_name);
1154 free(model_data);
1155 fclose(file);
1156 return 1;
1157 }
1158
1159 free(model_data);
1160 fclose(file);
1161 return 0;
1162}
1163
1164static void
1165ctx_enable_features(struct lys_module *module, const char *cpblt)
1166{
1167 char *ptr, *ptr2, *features = NULL;
1168
1169 /* parse features */
1170 ptr = strstr(cpblt, "features=");
1171 if (ptr) {
1172 ptr += 9;
1173 ptr2 = strchr(ptr, '&');
1174 if (!ptr2) {
1175 ptr2 = ptr + strlen(ptr);
1176 }
1177 features = strndup(ptr, ptr2 - ptr);
1178 }
1179
1180 /* enable features */
1181 if (features) {
1182 /* basically manual strtok_r (to avoid macro) */
1183 ptr2 = features;
1184 for (ptr = features; *ptr; ++ptr) {
1185 if (*ptr == ',') {
1186 *ptr = '\0';
1187 /* remember last feature */
1188 ptr2 = ptr + 1;
1189 }
1190 }
1191
1192 ptr = features;
1193 lys_features_enable(module, ptr);
1194 while (ptr != ptr2) {
1195 ptr += strlen(ptr) + 1;
1196 lys_features_enable(module, ptr);
1197 }
1198
1199 free(features);
1200 }
1201}
1202
1203static void
1204ctx_enable_capabs(struct lys_module *ietfnc, json_object *cpb_array)
1205{
1206 json_object *item;
1207 int i;
1208 const char *capab;
1209
1210 /* set supported capabilities from ietf-netconf */
1211 for (i = 0; i < json_object_array_length(cpb_array); ++i) {
1212 item = json_object_array_get_idx(cpb_array, i);
1213 capab = json_object_get_string(item);
1214
1215 if (!strncmp(capab, "urn:ietf:params:netconf:capability:", 35)) {
1216 if (!strncmp(capab, "writable-running", 16)) {
1217 lys_features_enable(ietfnc, "writable-running");
1218 } else if (!strncmp(capab, "candidate", 9)) {
1219 lys_features_enable(ietfnc, "candidate");
1220 } else if (!strcmp(capab, "confirmed-commit:1.1")) {
1221 lys_features_enable(ietfnc, "confirmed-commit");
1222 } else if (!strncmp(capab, "rollback-on-error", 17)) {
1223 lys_features_enable(ietfnc, "rollback-on-error");
1224 } else if (!strcmp(capab, "validate:1.1")) {
1225 lys_features_enable(ietfnc, "validate");
1226 } else if (!strncmp(capab, "startup", 7)) {
1227 lys_features_enable(ietfnc, "startup");
1228 } else if (!strncmp(capab, "url", 3)) {
1229 lys_features_enable(ietfnc, "url");
1230 } else if (!strncmp(capab, "xpath", 5)) {
1231 lys_features_enable(ietfnc, "xpath");
1232 }
1233 }
1234 }
1235}
1236
1237static int
1238prepare_context(struct session_with_mutex *session)
1239{
1240 struct lys_module *module;
1241 json_object *array, *item;
1242 char *ptr, *ptr2;
1243 char *model_name = NULL, *revision = NULL;
1244 const char *capab;
1245 int i, get_schema_support;
1246
1247 if (json_object_object_get_ex(session->hello_message, "capabilities", &array) == FALSE) {
1248 return 1;
1249 }
1250
1251 get_schema_support = 0;
1252 for (i = 0; i < json_object_array_length(array); ++i) {
1253 item = json_object_array_get_idx(array, i);
1254 capab = json_object_get_string(item);
1255
1256 if (!strncmp(capab, "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", 51)) {
1257 get_schema_support = 1;
1258 break;
1259 }
1260 }
1261
1262 if (get_schema_support) {
1263 errno = 0;
1264 if (eaccess(SCHEMA_DIR, W_OK)) {
1265 if (errno == ENOENT) {
1266 if (mkdir(SCHEMA_DIR, 00755)) {
1267 ERROR("Failed to create temp model dir \"%s\" (%s).", SCHEMA_DIR, strerror(errno));
1268 return 1;
1269 }
1270 } else {
1271 ERROR("Unable to write to temp model dir \"%s\" (%s).", SCHEMA_DIR, strerror(errno));
1272 return 1;
1273 }
1274 }
1275
1276 session->ctx = ly_ctx_new(SCHEMA_DIR);
1277 } else {
Michal Vaskoa53ef882015-11-24 11:02:01 +01001278 /* TODO try to load models from a local directory */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001279 session->ctx = ly_ctx_new(NULL);
1280 }
1281
1282loop:
1283 /* download all the models first or load them directly */
1284 for (i = 0; i < json_object_array_length(array); ++i) {
1285 item = json_object_array_get_idx(array, i);
1286 capab = json_object_get_string(item);
1287 if (!strncmp(capab, "urn:ietf:params:netconf:capability", 34)
1288 || !strncmp(capab, "urn:ietf:params:netconf:base", 28)) {
1289 continue;
1290 }
1291
1292 /* get module */
1293 ptr = strstr(capab, "module=");
1294 if (!ptr) {
1295 ERROR("Unknown capability \"%s\" could not be parsed.", capab);
1296 continue;
1297 }
1298 ptr += 7;
1299 ptr2 = strchr(ptr, '&');
1300 if (!ptr2) {
1301 ptr2 = ptr + strlen(ptr);
1302 }
1303 model_name = strndup(ptr, ptr2 - ptr);
1304
1305 /* get revision */
1306 ptr = strstr(capab, "revision=");
1307 if (ptr) {
1308 ptr += 9;
1309 ptr2 = strchr(ptr, '&');
1310 if (!ptr2) {
1311 ptr2 = ptr + strlen(ptr);
1312 }
1313 revision = strndup(ptr, ptr2 - ptr);
1314 }
1315
1316 if (get_schema_support) {
1317 ctx_download_module(session, model_name, revision, SCHEMA_DIR);
1318 } else {
1319 module = ly_ctx_get_module(session->ctx, model_name, revision);
1320 if (!module) {
Michal Vaskoe98e57d2015-11-25 10:42:09 +01001321 module = ly_ctx_load_module(session->ctx, model_name, revision);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001322 if (module) {
1323 if (!strcmp(module->name, "ietf-netconf")) {
1324 ctx_enable_capabs(module, array);
1325 } else {
1326 ctx_enable_features(module, capab);
1327 }
1328 }
1329 }
1330 }
1331
1332 free(model_name);
1333 free(revision);
1334 revision = NULL;
1335 }
1336
1337 if (get_schema_support) {
1338 /* we have downloaded all the models, load them now */
1339 get_schema_support = 0;
1340 goto loop;
1341 }
1342
1343 return 0;
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001344}
1345
1346/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001347 * \brief Connect to NETCONF server
1348 *
1349 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
1350 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001351static unsigned int
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001352netconf_connect(const char *host, const char *port, const char *user, const char *pass, const char *privkey, struct nc_cpblts *cpblts)
Radek Krejci469aab82012-07-22 18:42:20 +02001353{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001354 struct nc_session* session = NULL;
1355 struct session_with_mutex *locked_session, *last_session;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001356 char *pubkey;
Radek Krejci469aab82012-07-22 18:42:20 +02001357
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001358 /* connect to the requested NETCONF server */
1359 password = (char*)pass;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001360 if (privkey) {
1361 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, 3);
1362 asprintf(&pubkey, "%s.pub", privkey);
1363 nc_set_keypair_path(privkey, pubkey);
1364 free(pubkey);
1365 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001366 DEBUG("prepare to connect %s@%s:%s", user, host, port);
1367 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
1368 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +02001369
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001370 /* if connected successful, add session to the list */
1371 if (session != NULL) {
1372 if ((locked_session = calloc(1, sizeof(struct session_with_mutex))) == NULL || pthread_mutex_init (&locked_session->lock, NULL) != 0) {
1373 nc_session_free(session);
1374 session = NULL;
1375 free(locked_session);
1376 locked_session = NULL;
1377 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
1378 return 0;
1379 }
1380 locked_session->session = session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001381 locked_session->hello_message = NULL;
1382 locked_session->closed = 0;
1383 pthread_mutex_init(&locked_session->lock, NULL);
1384 DEBUG("Before session_lock");
1385 /* get exclusive access to sessions_list (conns) */
1386 DEBUG("LOCK wrlock %s", __func__);
1387 if (pthread_rwlock_wrlock(&session_lock) != 0) {
1388 nc_session_free(session);
1389 free(locked_session);
1390 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1391 return 0;
1392 }
1393 locked_session->ntfc_subscribed = 0;
1394 DEBUG("Add connection to the list");
1395 if (!netconf_sessions_list) {
Michal Vaskoc3146782015-11-04 14:46:41 +01001396 netconf_sessions_list = locked_session;
1397 } else {
1398 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
1399 last_session->next = locked_session;
1400 locked_session->prev = last_session;
1401 }
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001402 session_user_activity(nc_session_get_user(locked_session->session));
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001403
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001404 /* no need to lock session, noone can read it while we have wrlock */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001405
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001406 /* store information about session from hello message for future usage */
1407 prepare_status_message(locked_session, session);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001408
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001409 /* create context from the hello message cpabilities */
1410 if (prepare_context(locked_session)) {
1411 nc_session_free(session);
1412 free(locked_session);
1413 DEBUG("UNLOCK wrlock %s", __func__);
1414 pthread_rwlock_unlock(&session_lock);
1415 ERROR("Failed to prepare context");
1416 return 0;
1417 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001418
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001419 DEBUG("NETCONF session established");
1420 locked_session->session_key = session_key_generator;
1421 ++session_key_generator;
1422 if (session_key_generator == UINT_MAX) {
1423 session_key_generator = 1;
1424 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001425
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001426 DEBUG("Before session_unlock");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001427 /* unlock session list */
1428 DEBUG("UNLOCK wrlock %s", __func__);
1429 if (pthread_rwlock_unlock(&session_lock) != 0) {
1430 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1431 }
Radek Krejci469aab82012-07-22 18:42:20 +02001432
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001433 return locked_session->session_key;
1434 }
1435
1436 ERROR("Connection could not be established");
1437 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +02001438}
1439
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001440static int
1441close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +02001442{
Michal Vaskoa53ef882015-11-24 11:02:01 +01001443 int i;
1444
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001445 DEBUG("lock private lock.");
1446 DEBUG("LOCK mutex %s", __func__);
1447 if (pthread_mutex_lock(&locked_session->lock) != 0) {
1448 ERROR("Error while locking rwlock");
1449 }
1450 locked_session->ntfc_subscribed = 0;
1451 locked_session->closed = 1;
1452 if (locked_session->session != NULL) {
1453 nc_session_free(locked_session->session);
1454 locked_session->session = NULL;
1455 }
1456 DEBUG("session closed.");
1457 DEBUG("unlock private lock.");
1458 DEBUG("UNLOCK mutex %s", __func__);
1459 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
1460 ERROR("Error while locking rwlock");
1461 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001462
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001463 DEBUG("unlock session lock.");
1464 DEBUG("closed session, disabled notif(?), wait 0.5s");
1465 usleep(500000); /* let notification thread stop */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001466
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001467 /* session shouldn't be used by now */
Michal Vaskoa53ef882015-11-24 11:02:01 +01001468 for (i = 0; i < locked_session->notif_count; ++i) {
1469 free(locked_session->notifications[i].content);
1470 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001471 free(locked_session->notifications);
1472 pthread_mutex_destroy(&locked_session->lock);
1473 if (locked_session->hello_message != NULL) {
1474 json_object_put(locked_session->hello_message);
1475 locked_session->hello_message = NULL;
1476 }
1477 locked_session->session = NULL;
1478 ly_ctx_destroy(locked_session->ctx);
1479 free(locked_session);
1480 locked_session = NULL;
1481 DEBUG("NETCONF session closed, everything cleared.");
1482 return (EXIT_SUCCESS);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001483}
1484
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001485static int
1486netconf_close(unsigned int session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +02001487{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001488 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001489
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001490 DEBUG("Session to close: %u", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001491
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001492 /* get exclusive (write) access to sessions_list (conns) */
1493 DEBUG("lock session lock.");
1494 DEBUG("LOCK wrlock %s", __func__);
1495 if (pthread_rwlock_wrlock (&session_lock) != 0) {
1496 ERROR("Error while locking rwlock");
1497 (*reply) = create_error_reply("Internal: Error while locking.");
1498 return EXIT_FAILURE;
1499 }
1500 /* remove session from the active sessions list -> nobody new can now work with session */
1501 for (locked_session = netconf_sessions_list;
1502 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01001503 locked_session = locked_session->next);
1504
1505 if (!locked_session) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001506 ERROR("Could not find the session %u to close.", session_key);
1507 (*reply) = create_error_reply("Internal: Error while finding a session.");
Michal Vaskoc3146782015-11-04 14:46:41 +01001508 return EXIT_FAILURE;
1509 }
1510
1511 if (!locked_session->prev) {
1512 netconf_sessions_list = netconf_sessions_list->next;
1513 netconf_sessions_list->prev = NULL;
1514 } else {
1515 locked_session->prev->next = locked_session->next;
1516 if (locked_session->next) {
1517 locked_session->next->prev = locked_session->prev;
1518 }
1519 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001520
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001521 DEBUG("UNLOCK wrlock %s", __func__);
1522 if (pthread_rwlock_unlock (&session_lock) != 0) {
1523 ERROR("Error while unlocking rwlock");
1524 (*reply) = create_error_reply("Internal: Error while unlocking.");
1525 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001526
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001527 if ((locked_session != NULL) && (locked_session->session != NULL)) {
1528 return close_and_free_session(locked_session);
1529 } else {
1530 ERROR("Unknown session to close");
1531 (*reply) = create_error_reply("Internal: Unkown session to close.");
1532 return (EXIT_FAILURE);
1533 }
1534 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +02001535}
1536
Tomas Cejkac7929632013-10-24 19:25:15 +02001537/**
1538 * Test reply message type and return error message.
1539 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001540 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +01001541 * \param[in] session_key session ID, 0 to disable disconnect on error
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001542 * \param[in] msgt RPC-REPLY message type
Tomas Cejkac7929632013-10-24 19:25:15 +02001543 * \param[out] data
1544 * \return NULL on success
1545 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001546json_object *
1547netconf_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 +02001548{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001549 NC_REPLY_TYPE replyt;
1550 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001551
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001552 /* process the result of the operation */
1553 switch (msgt) {
1554 case NC_MSG_UNKNOWN:
1555 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
1556 ERROR("mod_netconf: receiving rpc-reply failed");
1557 if (session_key) {
1558 netconf_close(session_key, &err);
1559 }
1560 if (err != NULL) {
1561 return err;
1562 }
1563 return create_error_reply("Internal: Receiving RPC-REPLY failed.");
1564 }
1565 case NC_MSG_NONE:
1566 /* there is error handled by callback */
1567 if (data != NULL) {
1568 free(*data);
1569 (*data) = NULL;
1570 }
1571 return NULL;
1572 case NC_MSG_REPLY:
1573 switch (replyt = nc_reply_get_type(reply)) {
1574 case NC_REPLY_OK:
1575 if ((data != NULL) && (*data != NULL)) {
1576 free(*data);
1577 (*data) = NULL;
1578 }
1579 return create_ok_reply();
1580 case NC_REPLY_DATA:
1581 if (((*data) = nc_reply_get_data(reply)) == NULL) {
1582 ERROR("mod_netconf: no data from reply");
1583 return create_error_reply("Internal: No data from reply received.");
1584 } else {
1585 return NULL;
1586 }
1587 break;
1588 case NC_REPLY_ERROR:
1589 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
1590 if (data != NULL) {
1591 free(*data);
1592 (*data) = NULL;
1593 }
1594 return create_error_reply(nc_reply_get_errormsg(reply));
1595 default:
1596 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
1597 if (data != NULL) {
1598 free(*data);
1599 (*data) = NULL;
1600 }
1601 return create_error_reply("Unknown type of NETCONF reply.");
1602 }
1603 break;
1604 default:
1605 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
1606 if (data != NULL) {
1607 free(*data);
1608 (*data) = NULL;
1609 }
1610 return create_error_reply("Internal: Unexpected RPC-REPLY message type.");
1611 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001612}
1613
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001614json_object *
1615netconf_unlocked_op(struct nc_session *session, nc_rpc *rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001616{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001617 nc_reply* reply = NULL;
1618 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001619
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001620 /* check requests */
1621 if (rpc == NULL) {
1622 ERROR("mod_netconf: rpc is not created");
1623 return create_error_reply("Internal error: RPC is not created");
1624 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001625
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001626 if (session != NULL) {
1627 /* send the request and get the reply */
1628 msgt = netconf_send_recv_timed(session, rpc, 50000, &reply);
1629 /* process the result of the operation */
1630 return netconf_test_reply(session, 0, msgt, reply, NULL);
1631 } else {
1632 ERROR("Unknown session to process.");
1633 return create_error_reply("Internal error: Unknown session to process.");
1634 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001635}
1636
Tomas Cejkac7929632013-10-24 19:25:15 +02001637/**
1638 * Perform RPC method that returns data.
1639 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001640 * \param[in] session_id session identifier
1641 * \param[in] rpc RPC message to perform
1642 * \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 +02001643 * \return NULL on success, json object with error otherwise
1644 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001645static json_object *
1646netconf_op(unsigned int session_key, nc_rpc *rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +02001647{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001648 struct session_with_mutex * locked_session;
1649 nc_reply* reply = NULL;
1650 json_object *res = NULL;
1651 char *data = NULL;
1652 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +02001653
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001654 /* check requests */
1655 if (rpc == NULL) {
1656 ERROR("mod_netconf: rpc is not created");
1657 res = create_error_reply("Internal: RPC could not be created.");
1658 data = NULL;
1659 goto finished;
1660 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001661
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001662 locked_session = session_get_locked(session_key, &res);
1663 if (!locked_session) {
1664 ERROR("Unknown session or locking failed.");
1665 goto finished;
1666 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001667
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001668 session_user_activity(nc_session_get_user(locked_session->session));
Tomas Cejkac7929632013-10-24 19:25:15 +02001669
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001670 /* send the request and get the reply */
1671 msgt = netconf_send_recv_timed(locked_session->session, rpc, 2000000, &reply);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001672
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001673 session_unlock(locked_session);
Tomas Cejkac7929632013-10-24 19:25:15 +02001674
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001675 res = netconf_test_reply(locked_session->session, session_key, msgt, reply, &data);
Radek Krejcia332b692012-11-12 16:15:54 +01001676
Tomas Cejkac7929632013-10-24 19:25:15 +02001677finished:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001678 nc_reply_free(reply);
1679 if (received_data != NULL) {
1680 (*received_data) = data;
1681 } else {
1682 if (data != NULL) {
1683 free(data);
1684 data = NULL;
1685 }
1686 }
1687 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001688}
1689
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001690static char *
1691netconf_getconfig(unsigned int session_key, NC_DATASTORE source, const char *filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001692{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001693 nc_rpc* rpc;
1694 struct nc_filter *f = NULL;
1695 struct session_with_mutex *locked_session;
1696 char* data = NULL, *data_xml;
1697 json_object *res = NULL, *data_cjson;
1698 enum json_tokener_error tok_err;
1699 struct lyd_node *node, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001700
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001701 /* create filter if set */
1702 if (filter != NULL) {
1703 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
1704 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001705
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001706 /* create requests */
1707 rpc = nc_rpc_getconfig(source, f);
1708 nc_filter_free(f);
1709 if (rpc == NULL) {
1710 ERROR("mod_netconf: creating rpc request failed");
1711 return (NULL);
1712 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001713
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001714 /* tell server to show all elements even if they have default values */
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001715#ifdef HAVE_WITHDEFAULTS_TAGGED
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001716 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001717#else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001718 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET))
1719 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001720#endif
Michal Vaskoc3146782015-11-04 14:46:41 +01001721 {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001722 ERROR("mod_netconf: setting withdefaults failed");
1723 }
Tomas Cejka94674662013-09-13 15:55:24 +02001724
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001725 res = netconf_op(session_key, rpc, &data);
1726 nc_rpc_free(rpc);
1727 if (res != NULL) {
1728 (*err) = res;
1729 } else {
1730 (*err) = NULL;
1731 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001732
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001733 if (data) {
1734 for (locked_session = netconf_sessions_list;
1735 locked_session && (locked_session->session_key != session_key);
1736 locked_session = locked_session->next);
1737 /* won't fail */
1738
1739 asprintf(&data_xml, "<get-config>%s</get-config>", data);
1740 node = lyd_parse(locked_session->ctx, data_xml, LYD_XML, LYD_OPT_GETCONFIG | (strict ? LYD_OPT_STRICT : 0));
1741 free(data_xml);
1742 free(data);
1743 if (!node) {
1744 ERROR("Parsing <get-config> data failed.");
1745 return NULL;
1746 }
1747
1748 /* replace XML data with JSON data */
1749 if (lyd_print_mem(&data, node, LYD_JSON)) {
1750 ERROR("Printing JSON <get-config> data failed.");
1751 LY_TREE_FOR(node, sibling) {
1752 lyd_free(sibling);
1753 }
1754 return NULL;
1755 }
1756
1757 /* parse JSON data into cjson */
1758 pthread_mutex_lock(&json_lock);
1759 data_cjson = json_tokener_parse_verbose(data, &tok_err);
1760 if (!data_cjson) {
1761 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1762 pthread_mutex_unlock(&json_lock);
1763 LY_TREE_FOR(node, sibling) {
1764 lyd_free(sibling);
1765 }
1766 free(data);
1767 return NULL;
1768 }
1769 free(data);
1770
1771 /* go simultaneously through both trees and add metadata */
1772 LY_TREE_FOR_SAFE(node, next, sibling) {
1773 node_add_metadata_recursive(sibling, NULL, data_cjson);
1774 lyd_free(sibling);
1775 }
1776
1777 data = strdup(json_object_to_json_string_ext(data_cjson, 0));
1778 json_object_put(data_cjson);
1779 pthread_mutex_unlock(&json_lock);
1780 }
1781
1782 return (data);
Radek Krejci8e4632a2012-07-26 13:40:34 +02001783}
1784
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001785static char *
1786netconf_getschema(unsigned int session_key, const char *identifier, const char *version, const char *format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001787{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001788 nc_rpc* rpc;
1789 char* data = NULL;
1790 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001791
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001792 /* create requests */
1793 rpc = nc_rpc_getschema(identifier, version, format);
1794 if (rpc == NULL) {
1795 ERROR("mod_netconf: creating rpc request failed");
1796 return (NULL);
1797 }
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001798
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001799 res = netconf_op(session_key, rpc, &data);
1800 nc_rpc_free (rpc);
1801 if (res != NULL) {
1802 (*err) = res;
1803 } else {
1804 (*err) = NULL;
1805 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001806
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001807 return (data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001808}
1809
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001810static char *
1811netconf_get(unsigned int session_key, const char* filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001812{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001813 nc_rpc* rpc;
1814 struct nc_filter *f = NULL;
1815 char* data = NULL, *data_xml;
1816 json_object *res = NULL, *data_cjson;
1817 enum json_tokener_error tok_err;
1818 struct session_with_mutex *locked_session;
1819 struct lyd_node *node, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001820
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001821 /* create filter if set */
1822 if (filter != NULL) {
1823 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
1824 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001825
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001826 /* create requests */
1827 rpc = nc_rpc_get(f);
1828 nc_filter_free(f);
1829 if (rpc == NULL) {
1830 ERROR("mod_netconf: creating rpc request failed");
1831 return (NULL);
1832 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001833
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001834 /* tell server to show all elements even if they have default values */
1835 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
1836 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
1837 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
1838 ERROR("mod_netconf: setting withdefaults failed");
1839 }
Tomas Cejka94674662013-09-13 15:55:24 +02001840
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001841 res = netconf_op(session_key, rpc, &data);
1842 nc_rpc_free(rpc);
1843 if (res != NULL) {
1844 (*err) = res;
1845 } else {
1846 (*err) = NULL;
1847 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001848
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001849 if (data) {
1850 for (locked_session = netconf_sessions_list;
1851 locked_session && (locked_session->session_key != session_key);
1852 locked_session = locked_session->next);
1853 /* won't fail */
1854
1855 asprintf(&data_xml, "<get>%s</get>", data);
1856 node = lyd_parse(locked_session->ctx, data_xml, LYD_XML, LYD_OPT_GET | (strict ? LYD_OPT_STRICT : 0));
1857 free(data_xml);
1858 free(data);
1859 if (!node) {
1860 ERROR("Parsing <get> data failed.");
1861 return NULL;
1862 }
1863
1864 /* replace XML data with JSON data */
1865 if (lyd_print_mem(&data, node, LYD_JSON)) {
1866 ERROR("Printing JSON <get> data failed.");
1867 LY_TREE_FOR(node, sibling) {
1868 lyd_free(sibling);
1869 }
1870 return NULL;
1871 }
1872
1873 /* parse JSON data into cjson */
1874 pthread_mutex_lock(&json_lock);
1875 data_cjson = json_tokener_parse_verbose(data, &tok_err);
1876 if (!data_cjson) {
1877 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1878 pthread_mutex_unlock(&json_lock);
1879 LY_TREE_FOR(node, sibling) {
1880 lyd_free(sibling);
1881 }
1882 free(data);
1883 return NULL;
1884 }
1885 free(data);
1886
1887 /* go simultaneously through both trees and add metadata */
1888 LY_TREE_FOR_SAFE(node, next, sibling) {
1889 node_add_metadata_recursive(sibling, NULL, data_cjson);
1890 lyd_free(sibling);
1891 }
1892
1893 data = strdup(json_object_to_json_string_ext(data_cjson, 0));
1894 json_object_put(data_cjson);
1895 pthread_mutex_unlock(&json_lock);
1896 }
1897
1898 return data;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001899}
1900
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001901static json_object *
1902netconf_copyconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, const char *config,
1903 const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001904{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001905 nc_rpc* rpc;
1906 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001907
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001908 /* create requests */
1909 if (source == NC_DATASTORE_CONFIG) {
1910 if (target == NC_DATASTORE_URL) {
1911 /* config, url */
1912 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
1913 } else {
1914 /* config, datastore */
1915 rpc = nc_rpc_copyconfig(source, target, config);
1916 }
1917 } else if (source == NC_DATASTORE_URL) {
1918 if (target == NC_DATASTORE_URL) {
1919 /* url, url */
1920 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
1921 } else {
1922 /* url, datastore */
1923 rpc = nc_rpc_copyconfig(source, target, uri_src);
1924 }
1925 } else {
1926 if (target == NC_DATASTORE_URL) {
1927 /* datastore, url */
1928 rpc = nc_rpc_copyconfig(source, target, uri_trg);
1929 } else {
1930 /* datastore, datastore */
1931 rpc = nc_rpc_copyconfig(source, target);
1932 }
1933 }
1934 if (rpc == NULL) {
1935 ERROR("mod_netconf: creating rpc request failed");
1936 return create_error_reply("Internal: Creating rpc request failed");
1937 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001938
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001939 res = netconf_op(session_key, rpc, NULL);
1940 nc_rpc_free(rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001941
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001942 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001943}
Radek Krejci035bf4e2012-07-25 10:59:09 +02001944
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001945static json_object *
1946netconf_editconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, NC_EDIT_DEFOP_TYPE defop,
1947 NC_EDIT_ERROPT_TYPE erropt, NC_EDIT_TESTOPT_TYPE testopt, const char *config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +02001948{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001949 nc_rpc* rpc;
1950 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001951
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001952 /* create requests */
1953 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
1954 if (rpc == NULL) {
1955 ERROR("mod_netconf: creating rpc request failed");
1956 return create_error_reply("Internal: Creating rpc request failed");
1957 }
Radek Krejci62ab34b2012-07-26 13:42:05 +02001958
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001959 res = netconf_op(session_key, rpc, NULL);
1960 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001961
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001962 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001963}
1964
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001965static json_object *
1966netconf_killsession(unsigned int session_key, const char *sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001967{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001968 nc_rpc *rpc;
1969 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001970
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001971 /* create requests */
1972 rpc = nc_rpc_killsession(sid);
1973 if (rpc == NULL) {
1974 ERROR("mod_netconf: creating rpc request failed");
1975 return create_error_reply("Internal: Creating rpc request failed");
1976 }
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001977
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001978 res = netconf_op(session_key, rpc, NULL);
1979 nc_rpc_free(rpc);
1980 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001981}
1982
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001983static json_object *
1984netconf_onlytargetop(unsigned int session_key, NC_DATASTORE target, nc_rpc *(*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +02001985{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001986 nc_rpc* rpc;
1987 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +02001988
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001989 /* create requests */
1990 rpc = op_func(target);
1991 if (rpc == NULL) {
1992 ERROR("mod_netconf: creating rpc request failed");
1993 return create_error_reply("Internal: Creating rpc request failed");
1994 }
Radek Krejci2f318372012-07-26 14:22:35 +02001995
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001996 res = netconf_op(session_key, rpc, NULL);
1997 nc_rpc_free (rpc);
1998 return res;
Radek Krejci2f318372012-07-26 14:22:35 +02001999}
2000
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002001static json_object *
2002netconf_deleteconfig(unsigned int session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +02002003{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002004 nc_rpc *rpc = NULL;
2005 json_object *res = NULL;
2006 if (target != NC_DATASTORE_URL) {
2007 rpc = nc_rpc_deleteconfig(target);
2008 } else {
2009 rpc = nc_rpc_deleteconfig(target, url);
2010 }
2011 if (rpc == NULL) {
2012 ERROR("mod_netconf: creating rpc request failed");
2013 return create_error_reply("Internal: Creating rpc request failed");
2014 }
Tomas Cejka404d37e2013-04-13 02:31:35 +02002015
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002016 res = netconf_op(session_key, rpc, NULL);
2017 nc_rpc_free (rpc);
2018 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +02002019}
2020
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002021static json_object *
2022netconf_lock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02002023{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002024 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02002025}
2026
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002027static json_object *
2028netconf_unlock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02002029{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002030 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02002031}
2032
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002033static json_object *
2034netconf_generic(unsigned int session_key, const char *content, char **data)
Radek Krejci80c10d92012-07-30 08:38:50 +02002035{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002036 nc_rpc* rpc = NULL;
2037 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +02002038
Michal Vaskoa53ef882015-11-24 11:02:01 +01002039 assert(!data || !*data);
2040
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002041 /* create requests */
2042 rpc = nc_rpc_generic(content);
2043 if (rpc == NULL) {
2044 ERROR("mod_netconf: creating rpc request failed");
2045 return create_error_reply("Internal: Creating rpc request failed");
2046 }
Radek Krejci80c10d92012-07-30 08:38:50 +02002047
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002048 /* get session where send the RPC */
2049 res = netconf_op(session_key, rpc, data);
2050 nc_rpc_free (rpc);
2051 return res;
2052}
2053
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002054static int
2055node_add_metadata(struct lys_node *node, struct lys_module *module, json_object *parent)
2056{
2057 struct lys_module *cur_module;
2058 json_object *meta_obj;
2059 char *obj_name;
2060
Michal Vaskoa45770b2015-11-23 15:49:41 +01002061 if (node->nodetype == LYS_INPUT) {
2062 /* silently skipped */
2063 return 0;
2064 }
2065
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002066 cur_module = node->module;
2067 if (cur_module->type) {
2068 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
2069 }
2070 if (cur_module == module) {
2071 asprintf(&obj_name, "$@%s", node->name);
2072 } else {
2073 asprintf(&obj_name, "$@%s:%s", cur_module->name, node->name);
2074 }
2075
2076 /* in (leaf-)lists the metadata could have already been added */
Michal Vaskoa45770b2015-11-23 15:49:41 +01002077 if ((node->nodetype & (LYS_LEAFLIST | LYS_LIST)) && (json_object_object_get_ex(parent, obj_name, NULL) == TRUE)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002078 free(obj_name);
2079 return 1;
2080 }
2081
2082 meta_obj = json_object_new_object();
2083
2084 switch (node->nodetype) {
2085 case LYS_CONTAINER:
2086 node_metadata_container((struct lys_node_container *)node, meta_obj);
2087 break;
Michal Vasko3fda9a92015-11-23 10:10:57 +01002088 case LYS_CHOICE:
2089 node_metadata_choice((struct lys_node_choice *)node, meta_obj);
2090 break;
2091 case LYS_LEAF:
2092 node_metadata_leaf((struct lys_node_leaf *)node, meta_obj);
2093 break;
2094 case LYS_LEAFLIST:
2095 node_metadata_leaflist((struct lys_node_leaflist *)node, meta_obj);
2096 break;
2097 case LYS_LIST:
2098 node_metadata_list((struct lys_node_list *)node, meta_obj);
2099 break;
2100 case LYS_ANYXML:
2101 node_metadata_anyxml((struct lys_node_anyxml *)node, meta_obj);
2102 break;
2103 case LYS_CASE:
2104 node_metadata_case((struct lys_node_case *)node, meta_obj);
2105 break;
Michal Vaskoa45770b2015-11-23 15:49:41 +01002106 case LYS_RPC:
2107 node_metadata_rpc((struct lys_node_rpc *)node, meta_obj);
2108 break;
2109 default: /* LYS_OUTPUT */
Michal Vasko3fda9a92015-11-23 10:10:57 +01002110 ERROR("Internal: unuxpected nodetype (%s:%d)", __FILE__, __LINE__);
2111 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002112 }
2113
2114 /* just a precaution */
2115 if (json_object_get_type(parent) != json_type_object) {
2116 ERROR("Internal: wrong JSON type (%s:%d)", __FILE__, __LINE__);
2117 free(obj_name);
2118 return 1;
2119 }
2120
2121 json_object_object_add(parent, obj_name, meta_obj);
2122 free(obj_name);
2123 return 0;
2124}
2125
2126static void
2127node_add_metadata_recursive(struct lyd_node *data_tree, struct lys_module *module, json_object *data_json_parent)
2128{
2129 struct lys_module *cur_module;
2130 struct lys_node *list_schema;
2131 struct lyd_node *child, *list_item;
2132 json_object *child_json, *list_child_json;
2133 char *child_name;
2134 int list_idx;
2135
Michal Vaskoa45770b2015-11-23 15:49:41 +01002136 if (data_tree->schema->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
2137 return;
2138 }
2139
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002140 /* add data_tree metadata */
2141 if (node_add_metadata(data_tree->schema, module, data_json_parent)) {
2142 return;
2143 }
2144
2145 /* get data_tree module */
2146 cur_module = data_tree->schema->module;
2147 if (cur_module->type) {
2148 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
2149 }
2150
2151 if (!(data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
2152 /* print correct data_tree JSON name */
2153 if (cur_module == module) {
2154 asprintf(&child_name, "%s", data_tree->schema->name);
2155 } else {
2156 asprintf(&child_name, "%s:%s", cur_module->name, data_tree->schema->name);
2157 }
2158
2159 /* go down in JSON object */
2160 if (json_object_object_get_ex(data_json_parent, child_name, &child_json) == FALSE) {
2161 ERROR("Internal: failed to get JSON object \"%s\".", child_name);
2162 free(child_name);
2163 return;
2164 }
2165 free(child_name);
2166
2167 if (data_tree->schema->nodetype == LYS_LIST) {
2168 if (json_object_get_type(child_json) != json_type_array) {
2169 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
2170 return;
2171 }
2172 /* go down in data tree for every item, we process them all now, skip later
2173 * (metadata duplicate will be detected at the beginning of this function) */
2174 list_idx = 0;
2175 list_schema = data_tree->schema;
2176
2177 LY_TREE_FOR(data_tree, list_item) {
2178 /* another list member */
2179 if (list_item->schema == list_schema) {
2180 list_child_json = json_object_array_get_idx(child_json, list_idx);
2181 if (!list_child_json) {
2182 ERROR("Internal: list \"%s\" idx out-of-bounds", list_schema->name);
2183 return;
2184 }
2185 LY_TREE_FOR(list_item->child, child) {
2186 node_add_metadata_recursive(child, cur_module, list_child_json);
2187 }
2188
2189 ++list_idx;
2190 }
2191 }
2192 } else {
2193 if (json_object_get_type(child_json) != json_type_object) {
2194 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
2195 return;
2196 }
2197 /* go down in data tree */
2198 LY_TREE_FOR(data_tree->child, child) {
2199 node_add_metadata_recursive(child, cur_module, child_json);
2200 }
2201 }
2202 }
2203}
2204
2205static void
Michal Vaskoa45770b2015-11-23 15:49:41 +01002206node_add_model_metadata(struct lys_module *module, json_object *parent)
2207{
2208 json_object *obj;
2209 char *str;
2210
2211 obj = json_object_new_object();
2212 node_metadata_model(module, obj);
2213 asprintf(&str, "$@@%s", module->name);
2214 json_object_object_add(parent, str, obj);
2215 free(str);
2216}
2217
2218static void
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002219node_add_children_with_metadata_recursive(struct lys_node *node, struct lys_module *module, json_object *parent)
2220{
2221 struct lys_module *cur_module;
2222 struct lys_node *child;
2223 json_object *node_json;
2224 char *json_name;
2225
Michal Vaskoa45770b2015-11-23 15:49:41 +01002226 if (node->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
2227 return;
2228 }
2229
2230 if (node->nodetype & LYS_USES) {
2231 cur_module = module;
2232 node_json = parent;
2233 goto children;
2234 }
2235
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002236 /* add node metadata */
2237 if (node_add_metadata(node, module, parent)) {
2238 ERROR("Internal: metadata duplicate for \"%s\".", node->name);
2239 return;
2240 }
2241
Michal Vaskoa45770b2015-11-23 15:49:41 +01002242 /* no other metadata */
2243 if (!node->child) {
2244 return;
2245 }
2246
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002247 /* get node module */
2248 cur_module = node->module;
2249 if (cur_module->type) {
2250 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
2251 }
2252
2253 /* create JSON object for child metadata */
2254 node_json = json_object_new_object();
2255 if (cur_module == module) {
2256 json_object_object_add(parent, node->name, node_json);
2257 } else {
2258 asprintf(&json_name, "%s:%s", cur_module->name, node->name);
2259 json_object_object_add(parent, json_name, node_json);
2260 free(json_name);
2261 }
2262
Michal Vaskoa45770b2015-11-23 15:49:41 +01002263children:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002264 LY_TREE_FOR(node->child, child) {
2265 node_add_children_with_metadata_recursive(child, cur_module, node_json);
2266 }
2267}
2268
2269static json_object *
2270libyang_query(unsigned int session_key, const char *filter, int load_children)
2271{
2272 struct lys_node *node;
Michal Vaskoa45770b2015-11-23 15:49:41 +01002273 struct lys_module *module = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002274 struct session_with_mutex *locked_session;
2275 json_object *ret = NULL, *data;
2276
2277 locked_session = session_get_locked(session_key, &ret);
2278 if (!locked_session) {
2279 ERROR("Locking failed or session not found.");
2280 goto finish;
2281 }
2282
Michal Vaskoe32bcba2015-11-24 09:05:51 +01002283 session_user_activity(nc_session_get_user(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002284
Michal Vaskoa45770b2015-11-23 15:49:41 +01002285 if (filter[0] == '/') {
2286 node = ly_ctx_get_node(locked_session->ctx, filter);
2287 if (!node) {
2288 ret = create_error_reply("Failed to resolve XPath filter node.");
2289 goto finish;
2290 }
2291 } else {
2292 module = ly_ctx_get_module(locked_session->ctx, filter, NULL);
2293 if (!module) {
2294 ret = create_error_reply("Failed to find model.");
2295 goto finish;
2296 }
2297 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002298
Michal Vaskoa45770b2015-11-23 15:49:41 +01002299 pthread_mutex_lock(&json_lock);
2300 data = json_object_new_object();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002301
Michal Vaskoa45770b2015-11-23 15:49:41 +01002302 if (module) {
2303 node_add_model_metadata(module, data);
2304 if (load_children) {
2305 LY_TREE_FOR(module->data, node) {
2306 node_add_children_with_metadata_recursive(node, NULL, data);
2307 }
2308 }
2309 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002310 if (load_children) {
2311 node_add_children_with_metadata_recursive(node, NULL, data);
2312 } else {
2313 node_add_metadata(node, NULL, data);
2314 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002315 }
2316
Michal Vaskoa45770b2015-11-23 15:49:41 +01002317 pthread_mutex_unlock(&json_lock);
2318 ret = create_data_reply(json_object_to_json_string(data));
2319 json_object_put(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002320
2321finish:
Michal Vaskoa45770b2015-11-23 15:49:41 +01002322 session_unlock(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002323 return ret;
2324}
2325
2326static json_object *
2327libyang_merge(unsigned int session_key, const char *config)
2328{
2329 struct lyd_node *data_tree = NULL, *sibling;
2330 struct session_with_mutex *locked_session;
2331 json_object *ret = NULL, *data_json = NULL;
2332 enum json_tokener_error err = 0;
2333
2334 locked_session = session_get_locked(session_key, &ret);
2335 if (!locked_session) {
2336 ERROR("Locking failed or session not found.");
2337 goto finish;
2338 }
2339
Michal Vaskoe32bcba2015-11-24 09:05:51 +01002340 session_user_activity(nc_session_get_user(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002341
2342 data_tree = lyd_parse(locked_session->ctx, config, LYD_JSON, LYD_OPT_STRICT);
2343 if (!data_tree) {
2344 ERROR("Creating data tree failed.");
2345 ret = create_error_reply("Failed to create data tree from JSON config.");
2346 session_unlock(locked_session);
2347 goto finish;
2348 }
2349
2350 session_unlock(locked_session);
2351
2352 pthread_mutex_lock(&json_lock);
2353 data_json = json_tokener_parse_verbose(config, &err);
2354 if (!data_json) {
2355 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(err));
2356 pthread_mutex_unlock(&json_lock);
2357 ret = create_error_reply(json_tokener_error_desc(err));
2358 goto finish;
2359 }
2360
2361 /* go simultaneously through both trees and add metadata */
2362 LY_TREE_FOR(data_tree, sibling) {
2363 node_add_metadata_recursive(sibling, NULL, data_json);
2364 }
2365 pthread_mutex_unlock(&json_lock);
2366 ret = create_data_reply(json_object_to_json_string(data_json));
2367
2368finish:
2369 LY_TREE_FOR(data_tree, sibling) {
2370 lyd_free(sibling);
2371 }
2372 json_object_put(data_json);
2373 return ret;
Radek Krejci80c10d92012-07-30 08:38:50 +02002374}
2375
Tomas Cejka0a4bba82013-04-19 11:51:28 +02002376/**
2377 * @}
2378 *//* netconf_operations */
2379
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002380void
2381clb_print(NC_VERB_LEVEL level, const char *msg)
Radek Krejci469aab82012-07-22 18:42:20 +02002382{
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002383#define FOREACH(I) \
Tomas Cejkacf44e522015-04-24 17:29:21 +02002384 I(NC_VERB_ERROR) I(NC_VERB_WARNING)
2385
2386#define CASE(VAL) case VAL: ERROR("%s: %s", #VAL, msg); \
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002387 break;
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002388
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002389 switch (level) {
2390 FOREACH(CASE);
2391 case NC_VERB_VERBOSE:
2392 case NC_VERB_DEBUG:
2393 DEBUG("DEBUG: %s", msg);
2394 break;
2395 }
2396 if (level == NC_VERB_ERROR) {
2397 /* return global error */
2398 netconf_callback_error_process(NULL /* tag */, NULL /* type */,
2399 NULL /* severity */, NULL /* apptag */,
2400 NULL /* path */, msg, NULL /* attribute */,
2401 NULL /* element */, NULL /* ns */, NULL /* sid */);
2402 }
Radek Krejci469aab82012-07-22 18:42:20 +02002403}
2404
Tomas Cejka64b87482013-06-03 16:30:53 +02002405/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02002406 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02002407 * Caller should free message memory.
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002408 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02002409 * \return pointer to message
2410 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002411char *
2412get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02002413{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002414 /* read json in chunked framing */
2415 unsigned int buffer_size = 0;
2416 ssize_t buffer_len = 0;
2417 char *buffer = NULL;
2418 char c;
2419 ssize_t ret;
2420 int i, chunk_len;
2421 char chunk_len_str[12];
Tomas Cejka64b87482013-06-03 16:30:53 +02002422
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002423 while (1) {
2424 /* read chunk length */
2425 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
2426 if (buffer != NULL) {
2427 free (buffer);
2428 buffer = NULL;
2429 }
2430 break;
2431 }
2432 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
2433 if (buffer != NULL) {
2434 free (buffer);
2435 buffer = NULL;
2436 }
2437 break;
2438 }
2439 i=0;
2440 memset (chunk_len_str, 0, 12);
2441 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
2442 if (i==0 && c == '#') {
2443 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
2444 /* end but invalid */
2445 if (buffer != NULL) {
2446 free (buffer);
2447 buffer = NULL;
2448 }
2449 }
2450 /* end of message, double-loop break */
2451 goto msg_complete;
2452 }
2453 chunk_len_str[i++] = c;
2454 if (i==11) {
2455 ERROR("Message is too long, buffer for length is not big enought!!!!");
2456 break;
2457 }
2458 }
2459 if (c != '\n') {
2460 if (buffer != NULL) {
2461 free (buffer);
2462 buffer = NULL;
2463 }
2464 break;
2465 }
2466 chunk_len_str[i] = 0;
2467 if ((chunk_len = atoi (chunk_len_str)) == 0) {
2468 if (buffer != NULL) {
2469 free (buffer);
2470 buffer = NULL;
2471 }
2472 break;
2473 }
2474 buffer_size += chunk_len+1;
2475 buffer = realloc (buffer, sizeof(char)*buffer_size);
2476 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
2477 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
2478 if (buffer != NULL) {
2479 free (buffer);
2480 buffer = NULL;
2481 }
2482 break;
2483 }
2484 buffer_len += ret;
2485 }
Tomas Cejka64b87482013-06-03 16:30:53 +02002486msg_complete:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002487 return buffer;
Tomas Cejka64b87482013-06-03 16:30:53 +02002488}
2489
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002490NC_DATASTORE
2491parse_datastore(const char *ds)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002492{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002493 if (strcmp(ds, "running") == 0) {
2494 return NC_DATASTORE_RUNNING;
2495 } else if (strcmp(ds, "startup") == 0) {
2496 return NC_DATASTORE_STARTUP;
2497 } else if (strcmp(ds, "candidate") == 0) {
2498 return NC_DATASTORE_CANDIDATE;
2499 } else if (strcmp(ds, "url") == 0) {
2500 return NC_DATASTORE_URL;
2501 } else if (strcmp(ds, "config") == 0) {
2502 return NC_DATASTORE_CONFIG;
2503 }
2504 return -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002505}
2506
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002507NC_EDIT_TESTOPT_TYPE
2508parse_testopt(const char *t)
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002509{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002510 if (strcmp(t, "notset") == 0) {
2511 return NC_EDIT_TESTOPT_NOTSET;
2512 } else if (strcmp(t, "testset") == 0) {
2513 return NC_EDIT_TESTOPT_TESTSET;
2514 } else if (strcmp(t, "set") == 0) {
2515 return NC_EDIT_TESTOPT_SET;
2516 } else if (strcmp(t, "test") == 0) {
2517 return NC_EDIT_TESTOPT_TEST;
2518 }
2519 return NC_EDIT_TESTOPT_ERROR;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002520}
2521
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002522json_object *
2523create_error_reply(const char *errmess)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002524{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002525 json_object *reply, *array;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002526
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002527 pthread_mutex_lock(&json_lock);
2528 reply = json_object_new_object();
2529 array = json_object_new_array();
2530 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2531 json_object_array_add(array, json_object_new_string(errmess));
2532 json_object_object_add(reply, "errors", array);
2533 pthread_mutex_unlock(&json_lock);
2534
2535 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002536}
2537
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002538json_object *
2539create_data_reply(const char *data)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002540{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002541 pthread_mutex_lock(&json_lock);
2542 json_object *reply = json_object_new_object();
2543 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
2544 json_object_object_add(reply, "data", json_object_new_string(data));
2545 pthread_mutex_unlock(&json_lock);
2546 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002547}
2548
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002549json_object *
2550create_ok_reply(void)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002551{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002552 pthread_mutex_lock(&json_lock);
2553 json_object *reply = json_object_new_object();
2554 reply = json_object_new_object();
2555 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2556 pthread_mutex_unlock(&json_lock);
2557 return reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002558}
2559
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002560json_object *
2561create_replies(void)
Tomas Cejka09629492014-07-10 15:58:06 +02002562{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002563 json_object *replies;
2564
2565 pthread_mutex_lock(&json_lock);
2566 replies = json_object_new_object();
2567 pthread_mutex_unlock(&json_lock);
2568
2569 return replies;
Tomas Cejka09629492014-07-10 15:58:06 +02002570}
2571
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002572void
2573add_reply(json_object *replies, json_object *reply, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002574{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002575 char *str;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002576
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002577 asprintf(&str, "%u", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002578
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002579 pthread_mutex_lock(&json_lock);
2580 json_object_object_add(replies, str, reply);
2581 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002582
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002583 free(str);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002584}
2585
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002586char *
2587get_param_string(json_object *data, const char *name)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002588{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002589 json_object *js_tmp = NULL;
2590 char *res = NULL;
2591 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
2592 res = strdup(json_object_get_string(js_tmp));
2593 }
2594 return res;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002595}
2596
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002597json_object *
2598handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002599{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002600 char *host = NULL;
2601 char *port = NULL;
2602 char *user = NULL;
2603 char *pass = NULL;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002604 char *privkey = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002605 json_object *reply = NULL;
2606 unsigned int session_key = 0;
2607 struct nc_cpblts* cpblts = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002608
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002609 DEBUG("Request: connect");
2610 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002611
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002612 host = get_param_string(request, "host");
2613 port = get_param_string(request, "port");
2614 user = get_param_string(request, "user");
2615 pass = get_param_string(request, "pass");
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002616 privkey = get_param_string(request, "privatekey");
Tomas Cejkad5b53772013-06-08 23:01:07 +02002617
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002618 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002619
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002620 if (host == NULL) {
2621 host = "localhost";
2622 }
2623
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002624 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002625 if (user == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002626 ERROR("Cannot connect - insufficient input.");
2627 session_key = 0;
2628 } else {
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002629 session_key = netconf_connect(host, port, user, pass, privkey, cpblts);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002630 DEBUG("Session key: %u", session_key);
2631 }
2632 if (cpblts != NULL) {
2633 nc_cpblts_free(cpblts);
2634 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002635
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002636 GETSPEC_ERR_REPLY
2637
2638 pthread_mutex_lock(&json_lock);
2639 if (session_key == 0) {
2640 /* negative reply */
2641 if (err_reply == NULL) {
2642 reply = json_object_new_object();
2643 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2644 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
2645 ERROR("Connection failed.");
2646 } else {
2647 /* use filled err_reply from libnetconf's callback */
2648 reply = err_reply;
2649 ERROR("Connect - error from libnetconf's callback.");
2650 }
2651 } else {
2652 /* positive reply */
2653 reply = json_object_new_object();
2654 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2655 json_object_object_add(reply, "session", json_object_new_int(session_key));
2656 }
2657 memset(pass, 0, strlen(pass));
2658 pthread_mutex_unlock(&json_lock);
2659 CHECK_AND_FREE(host);
2660 CHECK_AND_FREE(user);
2661 CHECK_AND_FREE(port);
2662 CHECK_AND_FREE(pass);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002663 CHECK_AND_FREE(privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002664 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002665}
2666
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002667json_object *
2668handle_op_disconnect(json_object *UNUSED(request), unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002669{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002670 json_object *reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002671
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002672 DEBUG("Request: disconnect (session %u)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002673
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002674 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
2675 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
2676 } else {
2677 reply = create_ok_reply();
2678 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002679
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002680 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002681}
2682
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002683json_object *
2684handle_op_get(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002685{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002686 char *filter = NULL;
2687 char *data = NULL;
2688 json_object *reply = NULL, *obj;
2689 int strict;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002690
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002691 DEBUG("Request: get (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002692
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002693 pthread_mutex_lock(&json_lock);
2694 filter = get_param_string(request, "filter");
2695 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2696 pthread_mutex_unlock(&json_lock);
2697 reply = create_error_reply("Missing strict parameter.");
2698 return reply;
2699 }
2700 strict = json_object_get_boolean(obj);
2701 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002702
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002703 if ((data = netconf_get(session_key, filter, strict, &reply)) == NULL) {
2704 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
2705 } else {
2706 reply = create_data_reply(data);
2707 free(data);
2708 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002709
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002710 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002711}
2712
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002713json_object *
2714handle_op_getconfig(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002715{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002716 NC_DATASTORE ds_type_s = -1;
2717 char *filter = NULL;
2718 char *data = NULL;
2719 char *source = NULL;
2720 json_object *reply = NULL, *obj;
2721 int strict;
Tomas Cejkab4d05872014-02-14 22:44:38 +01002722
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002723 DEBUG("Request: get-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002724
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002725 pthread_mutex_lock(&json_lock);
2726 filter = get_param_string(request, "filter");
2727 source = get_param_string(request, "source");
2728 if (source != NULL) {
2729 ds_type_s = parse_datastore(source);
2730 }
2731 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2732 pthread_mutex_unlock(&json_lock);
2733 reply = create_error_reply("Missing strict parameter.");
2734 return reply;
2735 }
2736 strict = json_object_get_boolean(obj);
2737 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002738
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002739 if ((int)ds_type_s == -1) {
2740 reply = create_error_reply("Invalid source repository type requested.");
2741 goto finalize;
2742 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002743
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002744 if ((data = netconf_getconfig(session_key, ds_type_s, filter, strict, &reply)) == NULL) {
2745 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
2746 } else {
2747 reply = create_data_reply(data);
2748 free(data);
2749 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002750
Tomas Cejka09629492014-07-10 15:58:06 +02002751finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002752 CHECK_AND_FREE(filter);
2753 CHECK_AND_FREE(source);
2754 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002755}
2756
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002757json_object *
2758handle_op_editconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002759{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002760 NC_DATASTORE ds_type_s = -1;
2761 NC_DATASTORE ds_type_t = -1;
2762 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
2763 NC_EDIT_ERROPT_TYPE erropt_type = 0;
2764 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
2765 char *defop = NULL;
2766 char *erropt = NULL;
2767 char *config = NULL;
2768 char *source = NULL;
2769 char *target = NULL;
2770 char *testopt = NULL;
2771 char *urisource = NULL;
2772 json_object *reply = NULL, *configs, *obj;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002773
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002774 DEBUG("Request: edit-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002775
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002776 pthread_mutex_lock(&json_lock);
2777 /* get parameters */
2778 target = get_param_string(request, "target");
2779 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2780 pthread_mutex_unlock(&json_lock);
2781 reply = create_error_reply("Missing configs parameter.");
2782 goto finalize;
2783 }
2784 obj = json_object_array_get_idx(configs, idx);
2785 config = strdup(json_object_get_string(obj));
Tomas Cejkad5b53772013-06-08 23:01:07 +02002786
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002787 source = get_param_string(request, "source");
2788 defop = get_param_string(request, "default-operation");
2789 erropt = get_param_string(request, "error-option");
2790 urisource = get_param_string(request, "uri-source");
2791 testopt = get_param_string(request, "test-option");
2792 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002793
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002794 if (target != NULL) {
2795 ds_type_t = parse_datastore(target);
2796 }
2797 if (source != NULL) {
2798 ds_type_s = parse_datastore(source);
2799 } else {
2800 /* source is optional, default value is config */
2801 ds_type_s = NC_DATASTORE_CONFIG;
2802 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002803
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002804 if (defop != NULL) {
2805 if (strcmp(defop, "merge") == 0) {
2806 defop_type = NC_EDIT_DEFOP_MERGE;
2807 } else if (strcmp(defop, "replace") == 0) {
2808 defop_type = NC_EDIT_DEFOP_REPLACE;
2809 } else if (strcmp(defop, "none") == 0) {
2810 defop_type = NC_EDIT_DEFOP_NONE;
2811 } else {
2812 reply = create_error_reply("Invalid default-operation parameter.");
2813 goto finalize;
2814 }
2815 } else {
2816 defop_type = NC_EDIT_DEFOP_NOTSET;
2817 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002818
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002819 if (erropt != NULL) {
2820 if (strcmp(erropt, "continue-on-error") == 0) {
2821 erropt_type = NC_EDIT_ERROPT_CONT;
2822 } else if (strcmp(erropt, "stop-on-error") == 0) {
2823 erropt_type = NC_EDIT_ERROPT_STOP;
2824 } else if (strcmp(erropt, "rollback-on-error") == 0) {
2825 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
2826 } else {
2827 reply = create_error_reply("Invalid error-option parameter.");
2828 goto finalize;
2829 }
2830 } else {
2831 erropt_type = 0;
2832 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002833
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002834 if ((int)ds_type_t == -1) {
2835 reply = create_error_reply("Invalid target repository type requested.");
2836 goto finalize;
2837 }
2838 if (ds_type_s == NC_DATASTORE_CONFIG) {
2839 if (config == NULL) {
2840 reply = create_error_reply("Invalid config data parameter.");
2841 goto finalize;
2842 }
2843 } else if (ds_type_s == NC_DATASTORE_URL){
2844 if (urisource == NULL) {
2845 reply = create_error_reply("Invalid uri-source parameter.");
2846 goto finalize;
2847 }
2848 config = urisource;
2849 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002850
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002851 if (testopt != NULL) {
2852 testopt_type = parse_testopt(testopt);
2853 } else {
2854 testopt_type = NC_EDIT_TESTOPT_TESTSET;
2855 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002856
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002857 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 +02002858
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002859 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002860
Tomas Cejka09629492014-07-10 15:58:06 +02002861finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002862 CHECK_AND_FREE(defop);
2863 CHECK_AND_FREE(erropt);
2864 CHECK_AND_FREE(config);
2865 CHECK_AND_FREE(source);
2866 CHECK_AND_FREE(urisource);
2867 CHECK_AND_FREE(target);
2868 CHECK_AND_FREE(testopt);
2869
2870 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002871}
2872
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002873json_object *
2874handle_op_copyconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002875{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002876 NC_DATASTORE ds_type_s = -1;
2877 NC_DATASTORE ds_type_t = -1;
2878 char *config = NULL;
2879 char *target = NULL;
2880 char *source = NULL;
2881 char *uri_src = NULL;
2882 char *uri_trg = NULL;
2883 json_object *reply = NULL, *configs, *obj;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002884
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002885 DEBUG("Request: copy-config (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002886
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002887 /* get parameters */
2888 pthread_mutex_lock(&json_lock);
2889 target = get_param_string(request, "target");
2890 source = get_param_string(request, "source");
2891 uri_src = get_param_string(request, "uri-source");
2892 uri_trg = get_param_string(request, "uri-target");
2893 if (!strcmp(source, "config")) {
2894 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2895 pthread_mutex_unlock(&json_lock);
2896 reply = create_error_reply("Missing configs parameter.");
2897 goto finalize;
2898 }
2899 obj = json_object_array_get_idx(configs, idx);
2900 if (!obj) {
2901 pthread_mutex_unlock(&json_lock);
2902 reply = create_error_reply("Configs array parameter shorter than sessions.");
2903 goto finalize;
2904 }
2905 config = strdup(json_object_get_string(obj));
2906 }
2907 pthread_mutex_unlock(&json_lock);
2908
2909 if (target != NULL) {
2910 ds_type_t = parse_datastore(target);
2911 }
2912 if (source != NULL) {
2913 ds_type_s = parse_datastore(source);
2914 }
2915
2916 if ((int)ds_type_s == -1) {
2917 /* invalid source datastore specified */
2918 reply = create_error_reply("Invalid source repository type requested.");
2919 goto finalize;
2920 }
2921
2922 if ((int)ds_type_t == -1) {
2923 /* invalid target datastore specified */
2924 reply = create_error_reply("Invalid target repository type requested.");
2925 goto finalize;
2926 }
2927
2928 if (ds_type_s == NC_DATASTORE_URL) {
2929 if (uri_src == NULL) {
2930 uri_src = "";
2931 }
2932 }
2933 if (ds_type_t == NC_DATASTORE_URL) {
2934 if (uri_trg == NULL) {
2935 uri_trg = "";
2936 }
2937 }
2938 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
2939
2940 CHECK_ERR_SET_REPLY
2941
2942finalize:
2943 CHECK_AND_FREE(config);
2944 CHECK_AND_FREE(target);
2945 CHECK_AND_FREE(source);
2946 CHECK_AND_FREE(uri_src);
2947 CHECK_AND_FREE(uri_trg);
2948
2949 return reply;
2950}
2951
2952json_object *
2953handle_op_deleteconfig(json_object *request, unsigned int session_key)
2954{
2955 json_object *reply;
2956 NC_DATASTORE ds_type = -1;
2957 char *target, *url;
2958
2959 DEBUG("Request: delete-config (session %u)", session_key);
2960
2961 pthread_mutex_lock(&json_lock);
2962 target = get_param_string(request, "target");
2963 url = get_param_string(request, "url");
2964 pthread_mutex_unlock(&json_lock);
2965
2966 if (target != NULL) {
2967 ds_type = parse_datastore(target);
2968 }
2969 if ((int)ds_type == -1) {
2970 reply = create_error_reply("Invalid target repository type requested.");
2971 goto finalize;
2972 }
2973 if (ds_type == NC_DATASTORE_URL) {
2974 if (!url) {
2975 url = "";
2976 }
2977 }
2978
2979 reply = netconf_deleteconfig(session_key, ds_type, url);
2980
2981 CHECK_ERR_SET_REPLY
2982 if (reply == NULL) {
2983 reply = create_ok_reply();
2984 }
2985
2986finalize:
2987 CHECK_AND_FREE(target);
2988 CHECK_AND_FREE(url);
2989 return reply;
2990}
2991
2992json_object *
2993handle_op_lock(json_object *request, unsigned int session_key)
2994{
2995 json_object *reply;
2996 NC_DATASTORE ds_type = -1;
2997 char *target;
2998
2999 DEBUG("Request: lock (session %u)", session_key);
3000
3001 pthread_mutex_lock(&json_lock);
3002 target = get_param_string(request, "target");
3003 pthread_mutex_unlock(&json_lock);
3004
3005 if (target != NULL) {
3006 ds_type = parse_datastore(target);
3007 }
3008 if ((int)ds_type == -1) {
3009 reply = create_error_reply("Invalid target repository type requested.");
3010 goto finalize;
3011 }
3012
3013 reply = netconf_lock(session_key, ds_type);
3014
3015 CHECK_ERR_SET_REPLY
3016 if (reply == NULL) {
3017 reply = create_ok_reply();
3018 }
3019
3020finalize:
3021 CHECK_AND_FREE(target);
3022 return reply;
3023}
3024
3025json_object *
3026handle_op_unlock(json_object *request, unsigned int session_key)
3027{
3028 json_object *reply;
3029 NC_DATASTORE ds_type = -1;
3030 char *target;
3031
3032 DEBUG("Request: unlock (session %u)", session_key);
3033
3034 pthread_mutex_lock(&json_lock);
3035 target = get_param_string(request, "target");
3036 pthread_mutex_unlock(&json_lock);
3037
3038 if (target != NULL) {
3039 ds_type = parse_datastore(target);
3040 }
3041 if ((int)ds_type == -1) {
3042 reply = create_error_reply("Invalid target repository type requested.");
3043 goto finalize;
3044 }
3045
3046 reply = netconf_unlock(session_key, ds_type);
3047
3048 CHECK_ERR_SET_REPLY
3049 if (reply == NULL) {
3050 reply = create_ok_reply();
3051 }
3052
3053finalize:
3054 CHECK_AND_FREE(target);
3055 return reply;
3056}
3057
3058json_object *
3059handle_op_kill(json_object *request, unsigned int session_key)
3060{
3061 json_object *reply = NULL;
3062 char *sid = NULL;
3063
3064 DEBUG("Request: kill-session (session %u)", session_key);
3065
3066 pthread_mutex_lock(&json_lock);
3067 sid = get_param_string(request, "session-id");
3068 pthread_mutex_unlock(&json_lock);
3069
3070 if (sid == NULL) {
3071 reply = create_error_reply("Missing session-id parameter.");
3072 goto finalize;
3073 }
3074
3075 reply = netconf_killsession(session_key, sid);
3076
3077 CHECK_ERR_SET_REPLY
3078
3079finalize:
3080 CHECK_AND_FREE(sid);
3081 return reply;
3082}
3083
3084json_object *
3085handle_op_info(json_object *UNUSED(request), unsigned int session_key)
3086{
3087 json_object *reply = NULL;
3088 struct session_with_mutex *locked_session = NULL;
3089 DEBUG("Request: get info about session %u", session_key);
3090
3091 DEBUG("LOCK wrlock %s", __func__);
3092 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3093 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3094 }
3095
3096 for (locked_session = netconf_sessions_list;
3097 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003098 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003099 if (locked_session != NULL) {
3100 DEBUG("LOCK mutex %s", __func__);
3101 pthread_mutex_lock(&locked_session->lock);
3102 DEBUG("UNLOCK wrlock %s", __func__);
3103 if (pthread_rwlock_unlock(&session_lock) != 0) {
3104 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3105 }
3106 if (locked_session->hello_message != NULL) {
3107 reply = locked_session->hello_message;
3108 } else {
3109 reply = create_error_reply("Invalid session identifier.");
3110 }
3111 DEBUG("UNLOCK mutex %s", __func__);
3112 pthread_mutex_unlock(&locked_session->lock);
3113 } else {
3114 DEBUG("UNLOCK wrlock %s", __func__);
3115 if (pthread_rwlock_unlock(&session_lock) != 0) {
3116 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3117 }
3118 reply = create_error_reply("Invalid session identifier.");
3119 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02003120
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003121 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003122}
3123
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003124json_object *
3125handle_op_generic(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02003126{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003127 json_object *reply = NULL, *contents, *obj;
3128 char *config = NULL;
3129 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003130
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003131 DEBUG("Request: generic request (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02003132
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003133 pthread_mutex_lock(&json_lock);
3134 if (json_object_object_get_ex(request, "contents", &contents) == FALSE) {
3135 pthread_mutex_unlock(&json_lock);
3136 reply = create_error_reply("Missing contents parameter.");
3137 goto finalize;
3138 }
3139 obj = json_object_array_get_idx(contents, idx);
3140 if (!obj) {
3141 pthread_mutex_unlock(&json_lock);
3142 reply = create_error_reply("Contents array parameter shorter than sessions.");
3143 goto finalize;
3144 }
3145 config = strdup(json_object_get_string(obj));
3146 pthread_mutex_unlock(&json_lock);
3147
3148 reply = netconf_generic(session_key, config, &data);
3149 if (reply == NULL) {
3150 GETSPEC_ERR_REPLY
3151 if (err_reply != NULL) {
3152 /* use filled err_reply from libnetconf's callback */
3153 reply = err_reply;
3154 }
3155 } else {
3156 if (data == NULL) {
3157 pthread_mutex_lock(&json_lock);
3158 reply = json_object_new_object();
3159 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
3160 pthread_mutex_unlock(&json_lock);
3161 } else {
3162 reply = create_data_reply(data);
3163 free(data);
3164 }
3165 }
3166
3167finalize:
3168 CHECK_AND_FREE(config);
3169 return reply;
3170}
3171
3172json_object *
3173handle_op_getschema(json_object *request, unsigned int session_key)
3174{
3175 char *data = NULL;
3176 char *identifier = NULL;
3177 char *version = NULL;
3178 char *format = NULL;
3179 json_object *reply = NULL;
3180
3181 DEBUG("Request: get-schema (session %u)", session_key);
3182
3183 pthread_mutex_lock(&json_lock);
3184 identifier = get_param_string(request, "identifier");
3185 version = get_param_string(request, "version");
3186 format = get_param_string(request, "format");
3187 pthread_mutex_unlock(&json_lock);
3188
3189 if (identifier == NULL) {
3190 reply = create_error_reply("No identifier for get-schema supplied.");
3191 goto finalize;
3192 }
3193
3194 DEBUG("get-schema(version: %s, format: %s)", version, format);
3195 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
3196 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
3197 } else {
3198 reply = create_data_reply(data);
3199 free(data);
3200 }
3201
3202finalize:
3203 CHECK_AND_FREE(identifier);
3204 CHECK_AND_FREE(version);
3205 CHECK_AND_FREE(format);
3206 return reply;
3207}
3208
3209json_object *
3210handle_op_reloadhello(json_object *UNUSED(request), unsigned int session_key)
3211{
3212 struct nc_session *temp_session = NULL;
3213 struct session_with_mutex * locked_session = NULL;
3214 json_object *reply = NULL;
3215
3216 DEBUG("Request: reload hello (session %u)", session_key);
3217
3218 DEBUG("LOCK wrlock %s", __func__);
3219 if (pthread_rwlock_wrlock(&session_lock) != 0) {
3220 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3221 return NULL;
3222 }
3223
3224 for (locked_session = netconf_sessions_list;
3225 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003226 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003227 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
3228 DEBUG("LOCK mutex %s", __func__);
3229 pthread_mutex_lock(&locked_session->lock);
3230 DEBUG("creating temporary NC session.");
3231 temp_session = nc_session_connect_channel(locked_session->session, NULL);
3232 if (temp_session != NULL) {
3233 prepare_status_message(locked_session, temp_session);
3234 DEBUG("closing temporal NC session.");
3235 nc_session_free(temp_session);
3236 temp_session = NULL;
3237 } else {
3238 DEBUG("Reload hello failed due to channel establishment");
3239 reply = create_error_reply("Reload was unsuccessful, connection failed.");
3240 }
3241 DEBUG("UNLOCK mutex %s", __func__);
3242 pthread_mutex_unlock(&locked_session->lock);
3243 DEBUG("UNLOCK wrlock %s", __func__);
3244 if (pthread_rwlock_unlock(&session_lock) != 0) {
3245 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3246 }
3247 } else {
3248 DEBUG("UNLOCK wrlock %s", __func__);
3249 if (pthread_rwlock_unlock(&session_lock) != 0) {
3250 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3251 }
3252 reply = create_error_reply("Invalid session identifier.");
3253 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02003254
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003255 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
3256 reply = locked_session->hello_message;
3257 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02003258
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003259 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003260}
3261
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003262void
3263notification_history(time_t eventtime, const char *content)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003264{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003265 json_object *notif_history_array = (json_object *)pthread_getspecific(notif_history_key);
3266 if (notif_history_array == NULL) {
3267 ERROR("No list of notification history found.");
3268 return;
3269 }
3270 DEBUG("Got notification from history %lu.", (long unsigned)eventtime);
3271 pthread_mutex_lock(&json_lock);
3272 json_object *notif = json_object_new_object();
3273 if (notif == NULL) {
3274 ERROR("Could not allocate memory for notification (json).");
3275 goto failed;
3276 }
3277 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
3278 json_object_object_add(notif, "content", json_object_new_string(content));
3279 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003280failed:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003281 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003282}
3283
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003284json_object *
3285handle_op_ntfgethistory(json_object *request, unsigned int session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003286{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003287 json_object *reply = NULL;
3288 json_object *js_tmp = NULL;
3289 struct session_with_mutex *locked_session = NULL;
3290 struct nc_session *temp_session = NULL;
3291 nc_rpc *rpc = NULL;
3292 time_t start = 0;
3293 time_t stop = 0;
3294 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003295
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003296 DEBUG("Request: get notification history (session %u)", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003297
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003298 pthread_mutex_lock(&json_lock);
3299 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
3300 from = json_object_get_int64(js_tmp);
3301 }
3302 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
3303 to = json_object_get_int64(js_tmp);
3304 }
3305 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02003306
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003307 start = time(NULL) + from;
3308 stop = time(NULL) + to;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003309
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003310 DEBUG("notification history interval %li %li", (long int)from, (long int)to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003311
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003312 DEBUG("LOCK wrlock %s", __func__);
3313 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3314 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3315 reply = create_error_reply("Internal lock failed.");
3316 goto finalize;
3317 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003318
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003319 for (locked_session = netconf_sessions_list;
3320 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003321 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003322 if (locked_session != NULL) {
3323 DEBUG("LOCK mutex %s", __func__);
3324 pthread_mutex_lock(&locked_session->lock);
3325 DEBUG("UNLOCK wrlock %s", __func__);
3326 if (pthread_rwlock_unlock(&session_lock) != 0) {
3327 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3328 }
3329 DEBUG("creating temporal NC session.");
3330 temp_session = nc_session_connect_channel(locked_session->session, NULL);
3331 if (temp_session != NULL) {
3332 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
3333 if (rpc == NULL) {
3334 DEBUG("UNLOCK mutex %s", __func__);
3335 pthread_mutex_unlock(&locked_session->lock);
3336 DEBUG("notifications: creating an rpc request failed.");
3337 reply = create_error_reply("notifications: creating an rpc request failed.");
3338 goto finalize;
3339 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003340
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003341 DEBUG("Send NC subscribe.");
3342 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
3343 json_object *res = netconf_unlocked_op(temp_session, rpc);
3344 if (res != NULL) {
3345 DEBUG("UNLOCK mutex %s", __func__);
3346 pthread_mutex_unlock(&locked_session->lock);
3347 DEBUG("Subscription RPC failed.");
3348 reply = res;
3349 goto finalize;
3350 }
3351 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejka6b886e02013-07-05 09:53:17 +02003352
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003353 DEBUG("UNLOCK mutex %s", __func__);
3354 pthread_mutex_unlock(&locked_session->lock);
3355 DEBUG("LOCK mutex %s", __func__);
3356 pthread_mutex_lock(&ntf_history_lock);
3357 pthread_mutex_lock(&json_lock);
3358 json_object *notif_history_array = json_object_new_array();
3359 pthread_mutex_unlock(&json_lock);
3360 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
3361 ERROR("notif_history: cannot set thread-specific hash value.");
3362 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003363
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003364 ncntf_dispatch_receive(temp_session, notification_history);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003365
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003366 pthread_mutex_lock(&json_lock);
3367 reply = json_object_new_object();
3368 json_object_object_add(reply, "notifications", notif_history_array);
3369 //json_object_put(notif_history_array);
3370 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003371
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003372 DEBUG("UNLOCK mutex %s", __func__);
3373 pthread_mutex_unlock(&ntf_history_lock);
3374 DEBUG("closing temporal NC session.");
3375 nc_session_free(temp_session);
3376 temp_session = NULL;
3377 } else {
3378 DEBUG("UNLOCK mutex %s", __func__);
3379 pthread_mutex_unlock(&locked_session->lock);
3380 DEBUG("Get history of notification failed due to channel establishment");
3381 reply = create_error_reply("Get history of notification was unsuccessful, connection failed.");
3382 }
3383 } else {
3384 DEBUG("UNLOCK wrlock %s", __func__);
3385 if (pthread_rwlock_unlock(&session_lock) != 0) {
3386 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3387 }
3388 reply = create_error_reply("Invalid session identifier.");
3389 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003390
Tomas Cejka09629492014-07-10 15:58:06 +02003391finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003392 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02003393}
3394
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003395json_object *
3396handle_op_validate(json_object *request, unsigned int session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02003397{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003398 json_object *reply = NULL;
3399 char *target = NULL;
3400 char *url = NULL;
3401 nc_rpc *rpc = NULL;
3402 NC_DATASTORE target_ds;
Tomas Cejka4003a702013-10-01 00:02:45 +02003403
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003404 DEBUG("Request: validate datastore (session %u)", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02003405
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003406 pthread_mutex_lock(&json_lock);
3407 target = get_param_string(request, "target");
3408 url = get_param_string(request, "url");
3409 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02003410
3411
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003412 if (target == NULL) {
3413 reply = create_error_reply("Missing target parameter.");
3414 goto finalize;
3415 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003416
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003417 /* validation */
3418 target_ds = parse_datastore(target);
3419 if (target_ds == NC_DATASTORE_URL) {
3420 if (url != NULL) {
3421 rpc = nc_rpc_validate(target_ds, url);
3422 }
3423 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
3424 || (target_ds == NC_DATASTORE_CANDIDATE)) {
3425 rpc = nc_rpc_validate(target_ds);
3426 }
3427 if (rpc == NULL) {
3428 DEBUG("mod_netconf: creating rpc request failed");
3429 reply = create_error_reply("Creation of RPC request failed.");
3430 goto finalize;
3431 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003432
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003433 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
3434 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003435
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003436 if (reply == NULL) {
3437 DEBUG("Request: validation ok.");
3438 reply = create_ok_reply();
3439 }
3440 }
3441 nc_rpc_free (rpc);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003442
Tomas Cejka09629492014-07-10 15:58:06 +02003443finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003444 CHECK_AND_FREE(target);
3445 CHECK_AND_FREE(url);
3446 return reply;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003447}
3448
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003449json_object *
3450handle_op_query(json_object *request, unsigned int session_key, int idx)
David Kupka8e60a372012-09-04 09:15:20 +02003451{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003452 json_object *reply = NULL, *filters, *obj;
3453 char *filter = NULL;
3454 int load_children = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003455
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003456 DEBUG("Request: query (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003457
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003458 pthread_mutex_lock(&json_lock);
3459 if (json_object_object_get_ex(request, "filters", &filters) == FALSE) {
3460 pthread_mutex_unlock(&json_lock);
3461 reply = create_error_reply("Missing filters parameter.");
3462 goto finalize;
3463 }
3464 obj = json_object_array_get_idx(filters, idx);
3465 if (!obj) {
3466 pthread_mutex_unlock(&json_lock);
3467 reply = create_error_reply("Filters array parameter shorter than sessions.");
3468 goto finalize;
3469 }
3470 filter = strdup(json_object_get_string(obj));
3471 if (json_object_object_get_ex(request, "load_children", &obj) == TRUE) {
3472 load_children = json_object_get_boolean(obj);
3473 }
3474 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02003475
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003476 reply = libyang_query(session_key, filter, load_children);
David Kupka8e60a372012-09-04 09:15:20 +02003477
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003478 CHECK_ERR_SET_REPLY
3479 if (!reply) {
3480 reply = create_error_reply("Query failed.");
3481 }
David Kupka8e60a372012-09-04 09:15:20 +02003482
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003483finalize:
3484 CHECK_AND_FREE(filter);
3485 return reply;
3486}
David Kupka8e60a372012-09-04 09:15:20 +02003487
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003488json_object *
3489handle_op_merge(json_object *request, unsigned int session_key, int idx)
3490{
3491 json_object *reply = NULL, *configs, *obj;
3492 char *config = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003493
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003494 DEBUG("Request: merge (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003495
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003496 pthread_mutex_lock(&json_lock);
3497 if (json_object_object_get_ex(request, "configurations", &configs) == FALSE) {
3498 pthread_mutex_unlock(&json_lock);
3499 reply = create_error_reply("Missing configurations parameter.");
3500 goto finalize;
3501 }
3502 obj = json_object_array_get_idx(configs, idx);
3503 if (!obj) {
3504 pthread_mutex_unlock(&json_lock);
3505 reply = create_error_reply("Filters array parameter shorter than sessions.");
3506 goto finalize;
3507 }
3508 config = strdup(json_object_get_string(obj));
3509 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02003510
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003511 reply = libyang_merge(session_key, config);
David Kupka8e60a372012-09-04 09:15:20 +02003512
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003513 CHECK_ERR_SET_REPLY
3514 if (!reply) {
3515 reply = create_error_reply("Merge failed.");
3516 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003517
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003518finalize:
3519 CHECK_AND_FREE(config);
3520 return reply;
3521}
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003522
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003523void *
3524thread_routine(void *arg)
3525{
3526 void *retval = NULL;
3527 struct pollfd fds;
3528 json_object *request = NULL, *replies = NULL, *reply, *sessions = NULL;
3529 json_object *js_tmp = NULL;
3530 int operation = (-1), count, i;
3531 int status = 0;
3532 const char *msgtext;
3533 unsigned int session_key = 0;
3534 char *chunked_out_msg = NULL;
3535 int client = ((struct pass_to_thread *)arg)->client;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003536
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003537 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003538
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003539 /* init thread specific err_reply memory */
3540 create_err_reply_p();
David Kupka8e60a372012-09-04 09:15:20 +02003541
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003542 while (!isterminated) {
3543 fds.fd = client;
3544 fds.events = POLLIN;
3545 fds.revents = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003546
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003547 status = poll(&fds, 1, 1000);
Tomas Cejkad5b53772013-06-08 23:01:07 +02003548
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003549 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
3550 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
3551 continue;
3552 } else if (status < 0) {
3553 /* 0: poll time outed
3554 * close socket and ignore this request from the client, it can try it again
3555 * -1: poll failed
3556 * something wrong happend, close this socket and wait for another request
3557 */
3558 close(client);
3559 break;
3560 }
3561 /* status > 0 */
David Kupka8e60a372012-09-04 09:15:20 +02003562
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003563 /* check the status of the socket */
David Kupka8e60a372012-09-04 09:15:20 +02003564
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003565 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
3566 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
3567 /* close client's socket (it's probably already closed by client */
3568 close(client);
3569 break;
3570 }
Tomas Cejka09629492014-07-10 15:58:06 +02003571
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003572 DEBUG("Get framed message...");
3573 buffer = get_framed_message(client);
Tomas Cejka09629492014-07-10 15:58:06 +02003574
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003575 DEBUG("Check read buffer.");
3576 if (buffer != NULL) {
3577 enum json_tokener_error jerr;
3578 pthread_mutex_lock(&json_lock);
3579 request = json_tokener_parse_verbose(buffer, &jerr);
3580 if (jerr != json_tokener_success) {
3581 ERROR("JSON parsing error");
3582 pthread_mutex_unlock(&json_lock);
3583 continue;
3584 }
3585
3586 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
3587 operation = json_object_get_int(js_tmp);
3588 }
3589 pthread_mutex_unlock(&json_lock);
3590 if (operation == -1) {
3591 replies = create_replies();
3592 add_reply(replies, create_error_reply("Missing operation type from frontend."), 0);
3593 goto send_reply;
3594 }
3595
3596 if ((operation < 4) || ((operation > 19) && (operation < 100)) || (operation > 101)) {
3597 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
3598 replies = create_replies();
3599 add_reply(replies, create_error_reply("Operation not supported."), 0);
3600 goto send_reply;
3601 }
3602
3603 DEBUG("operation %d", operation);
3604
3605 /* null global JSON error-reply */
3606 clean_err_reply();
3607
3608 /* clean replies envelope */
3609 if (replies != NULL) {
3610 pthread_mutex_lock(&json_lock);
3611 json_object_put(replies);
3612 pthread_mutex_unlock(&json_lock);
3613 }
3614 replies = create_replies();
3615
3616 if (operation == MSG_CONNECT) {
3617 count = 1;
3618 } else {
3619 pthread_mutex_lock(&json_lock);
3620 if (json_object_object_get_ex(request, "sessions", &sessions) == FALSE) {
3621 add_reply(replies, create_error_reply("Operation missing \"sessions\" arg"), 0);
3622 goto send_reply;
3623 }
3624 count = json_object_array_length(sessions);
3625 pthread_mutex_unlock(&json_lock);
3626 }
3627
3628 for (i = 0; i < count; ++i) {
3629 if (operation != MSG_CONNECT) {
3630 js_tmp = json_object_array_get_idx(sessions, i);
3631 session_key = json_object_get_int(js_tmp);
3632 }
3633
3634 /* process required operation */
3635 switch (operation) {
3636 case MSG_CONNECT:
3637 reply = handle_op_connect(request);
3638 break;
3639 case MSG_DISCONNECT:
3640 reply = handle_op_disconnect(request, session_key);
3641 break;
3642 case MSG_GET:
3643 reply = handle_op_get(request, session_key);
3644 break;
3645 case MSG_GETCONFIG:
3646 reply = handle_op_getconfig(request, session_key);
3647 break;
3648 case MSG_EDITCONFIG:
3649 reply = handle_op_editconfig(request, session_key, i);
3650 break;
3651 case MSG_COPYCONFIG:
3652 reply = handle_op_copyconfig(request, session_key, i);
3653 break;
3654 case MSG_DELETECONFIG:
3655 reply = handle_op_deleteconfig(request, session_key);
3656 break;
3657 case MSG_LOCK:
3658 reply = handle_op_lock(request, session_key);
3659 break;
3660 case MSG_UNLOCK:
3661 reply = handle_op_unlock(request, session_key);
3662 break;
3663 case MSG_KILL:
3664 reply = handle_op_kill(request, session_key);
3665 break;
3666 case MSG_INFO:
3667 reply = handle_op_info(request, session_key);
3668 break;
3669 case MSG_GENERIC:
3670 reply = handle_op_generic(request, session_key, i);
3671 break;
3672 case MSG_GETSCHEMA:
3673 reply = handle_op_getschema(request, session_key);
3674 break;
3675 case MSG_RELOADHELLO:
3676 reply = handle_op_reloadhello(request, session_key);
3677 break;
3678 case MSG_NTF_GETHISTORY:
3679 reply = handle_op_ntfgethistory(request, session_key);
3680 break;
3681 case MSG_VALIDATE:
3682 reply = handle_op_validate(request, session_key);
3683 break;
3684 case SCH_QUERY:
3685 reply = handle_op_query(request, session_key, i);
3686 break;
3687 case SCH_MERGE:
3688 reply = handle_op_merge(request, session_key, i);
3689 break;
3690 }
3691
3692 add_reply(replies, reply, session_key);
3693 }
3694
3695 /* free parameters */
3696 operation = (-1);
3697
3698 DEBUG("Clean request json object.");
3699 if (request != NULL) {
3700 pthread_mutex_lock(&json_lock);
3701 json_object_put(request);
3702 pthread_mutex_unlock(&json_lock);
3703 }
3704 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02003705
3706send_reply:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003707 /* send reply to caller */
3708 if (replies) {
3709 pthread_mutex_lock(&json_lock);
3710 msgtext = json_object_to_json_string(replies);
3711 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
3712 if (buffer != NULL) {
3713 free(buffer);
3714 buffer = NULL;
3715 }
3716 pthread_mutex_unlock(&json_lock);
3717 break;
3718 }
3719 pthread_mutex_unlock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003720
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003721 DEBUG("Send framed reply json object.");
3722 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
3723 DEBUG("Clean reply json object.");
3724 pthread_mutex_lock(&json_lock);
3725 json_object_put(replies);
3726 replies = NULL;
3727 DEBUG("Clean message buffer.");
3728 CHECK_AND_FREE(chunked_out_msg);
3729 chunked_out_msg = NULL;
3730 if (buffer) {
3731 free(buffer);
3732 buffer = NULL;
3733 }
3734 pthread_mutex_unlock(&json_lock);
3735 clean_err_reply();
3736 } else {
3737 ERROR("Reply is NULL, shouldn't be...");
3738 continue;
3739 }
3740 }
3741 }
3742 free(arg);
3743 free_err_reply();
David Kupka8e60a372012-09-04 09:15:20 +02003744
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003745 return retval;
David Kupka8e60a372012-09-04 09:15:20 +02003746}
3747
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003748/**
3749 * \brief Close all open NETCONF sessions.
3750 *
3751 * During termination of mod_netconf, it is useful to close all remaining
3752 * sessions. This function iterates over the list of sessions and close them
3753 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003754 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003755static void
3756close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003757{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003758 struct session_with_mutex *locked_session, *next_session;
3759 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003760
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003761 /* get exclusive access to sessions_list (conns) */
3762 DEBUG("LOCK wrlock %s", __func__);
3763 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
3764 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3765 return;
3766 }
3767 for (next_session = netconf_sessions_list; next_session;) {
3768 locked_session = next_session;
3769 next_session = locked_session->next;
Tomas Cejka47387fd2013-06-10 20:37:46 +02003770
Michal Vaskoc3146782015-11-04 14:46:41 +01003771 /* close_and_free_session handles locking on its own */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003772 DEBUG("Closing NETCONF session %u (SID %s).", locked_session->session_key, nc_session_get_id(locked_session->session));
3773 close_and_free_session(locked_session);
3774 }
3775 netconf_sessions_list = NULL;
3776
3777 /* get exclusive access to sessions_list (conns) */
3778 DEBUG("UNLOCK wrlock %s", __func__);
3779 if (pthread_rwlock_unlock (&session_lock) != 0) {
3780 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3781 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003782}
3783
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003784static void
3785check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003786{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003787 struct nc_session *ns = NULL;
3788 struct session_with_mutex *locked_session = NULL;
3789 time_t current_time = time(NULL);
3790 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003791
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003792 /* get exclusive access to sessions_list (conns) */
3793 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
3794 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3795 return;
3796 }
3797 for (locked_session = netconf_sessions_list; locked_session; locked_session = locked_session->next) {
3798 ns = locked_session->session;
3799 if (ns == NULL) {
3800 continue;
3801 }
3802 pthread_mutex_lock(&locked_session->lock);
3803 if ((current_time - locked_session->last_activity) > ACTIVITY_TIMEOUT) {
3804 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 +02003805
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003806 /* close_and_free_session handles locking on its own */
3807 close_and_free_session(locked_session);
3808 } else {
3809 pthread_mutex_unlock(&locked_session->lock);
3810 }
3811 }
3812 /* get exclusive access to sessions_list (conns) */
3813 if (pthread_rwlock_unlock(&session_lock) != 0) {
3814 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3815 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003816}
3817
3818
3819/**
Radek Krejcif23850c2012-07-23 16:14:17 +02003820 * This is actually implementation of NETCONF client
3821 * - requests are received from UNIX socket in the predefined format
3822 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01003823 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02003824 *
3825 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003826static void
3827forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02003828{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003829 struct timeval tv;
3830 struct sockaddr_un local, remote;
3831 int lsock, client, ret, i, pthread_count = 0;
3832 unsigned int olds = 0, timediff = 0;
3833 socklen_t len;
3834 struct pass_to_thread *arg;
3835 pthread_t *ptids = calloc(1, sizeof(pthread_t));
3836 struct timespec maxtime;
3837 pthread_rwlockattr_t lock_attrs;
3838 #ifdef WITH_NOTIFICATIONS
3839 char use_notifications = 0;
3840 #endif
David Kupka8e60a372012-09-04 09:15:20 +02003841
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003842 /* wait at most 5 seconds for every thread to terminate */
3843 maxtime.tv_sec = 5;
3844 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02003845
Tomas Cejka04e08f42014-03-27 19:52:34 +01003846#ifdef HAVE_UNIXD_SETUP_CHILD
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003847 /* change uid and gid of process for security reasons */
3848 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01003849#else
3850# ifdef SU_GROUP
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003851 if (strlen(SU_GROUP) > 0) {
3852 struct group *g = getgrnam(SU_GROUP);
3853 if (g == NULL) {
3854 ERROR("GID (%s) was not found.", SU_GROUP);
3855 return;
3856 }
3857 if (setgid(g->gr_gid) != 0) {
3858 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
3859 return;
3860 }
3861 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003862# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003863 DEBUG("no SU_GROUP");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003864# endif
3865# ifdef SU_USER
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003866 if (strlen(SU_USER) > 0) {
3867 struct passwd *p = getpwnam(SU_USER);
3868 if (p == NULL) {
3869 ERROR("UID (%s) was not found.", SU_USER);
3870 return;
3871 }
3872 if (setuid(p->pw_uid) != 0) {
3873 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
3874 return;
3875 }
3876 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003877# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003878 DEBUG("no SU_USER");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003879# endif
3880#endif
Radek Krejci469aab82012-07-22 18:42:20 +02003881
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003882 /* try to remove if exists */
3883 unlink(sockname);
Tomas Cejka04e08f42014-03-27 19:52:34 +01003884
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003885 /* create listening UNIX socket to accept incoming connections */
3886 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
3887 ERROR("Creating socket failed (%s)", strerror(errno));
3888 goto error_exit;
3889 }
Radek Krejci469aab82012-07-22 18:42:20 +02003890
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003891 local.sun_family = AF_UNIX;
3892 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
3893 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
Radek Krejci469aab82012-07-22 18:42:20 +02003894
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003895 if (bind(lsock, (struct sockaddr *)&local, len) == -1) {
3896 if (errno == EADDRINUSE) {
3897 ERROR("mod_netconf socket address already in use");
3898 goto error_exit;
3899 }
3900 ERROR("Binding socket failed (%s)", strerror(errno));
3901 goto error_exit;
3902 }
Radek Krejci469aab82012-07-22 18:42:20 +02003903
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003904 if (listen(lsock, MAX_SOCKET_CL) == -1) {
3905 ERROR("Setting up listen socket failed (%s)", strerror(errno));
3906 goto error_exit;
3907 }
3908 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02003909
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003910 uid_t user = -1;
3911 if (strlen(CHOWN_USER) > 0) {
3912 struct passwd *p = getpwnam(CHOWN_USER);
3913 if (p != NULL) {
3914 user = p->pw_uid;
3915 }
3916 }
3917 gid_t group = -1;
3918 if (strlen(CHOWN_GROUP) > 0) {
3919 struct group *g = getgrnam(CHOWN_GROUP);
3920 if (g != NULL) {
3921 group = g->gr_gid;
3922 }
3923 }
3924 if (chown(sockname, user, group) == -1) {
3925 ERROR("Chown on socket file failed (%s).", strerror(errno));
3926 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003927
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003928 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02003929
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003930 #ifdef WITH_NOTIFICATIONS
3931 if (notification_init() == -1) {
3932 ERROR("libwebsockets initialization failed");
3933 use_notifications = 0;
3934 } else {
3935 use_notifications = 1;
3936 }
3937 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003938
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003939 /* setup libnetconf's callbacks */
3940 nc_verbosity(NC_VERB_DEBUG);
3941 nc_callback_print(clb_print);
3942 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
3943 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
3944 nc_callback_sshauth_password(netconf_callback_sshauth_password);
3945 nc_callback_sshauth_passphrase(netconf_callback_sshauth_passphrase);
3946 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02003947
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003948 /* disable publickey authentication */
3949 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
Radek Krejci469aab82012-07-22 18:42:20 +02003950
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003951 /* create mutex protecting session list */
3952 pthread_rwlockattr_init(&lock_attrs);
3953 /* rwlock is shared only with threads in this process */
3954 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
3955 /* create rw lock */
3956 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
3957 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
3958 goto error_exit;
3959 }
3960 pthread_mutex_init(&ntf_history_lock, NULL);
3961 pthread_mutex_init(&json_lock, NULL);
3962 DEBUG("Initialization of notification history.");
3963 if (pthread_key_create(&notif_history_key, NULL) != 0) {
3964 ERROR("Initialization of notification history failed.");
3965 }
3966 if (pthread_key_create(&err_reply_key, NULL) != 0) {
3967 ERROR("Initialization of reply key failed.");
3968 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02003969
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003970 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
3971 while (isterminated == 0) {
3972 gettimeofday(&tv, NULL);
3973 timediff = (unsigned int)tv.tv_sec - olds;
3974 #ifdef WITH_NOTIFICATIONS
3975 if (use_notifications == 1) {
3976 notification_handle();
3977 }
3978 #endif
3979 if (timediff > ACTIVITY_CHECK_INTERVAL) {
3980 check_timeout_and_close();
3981 }
Radek Krejci469aab82012-07-22 18:42:20 +02003982
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003983 /* open incoming connection if any */
3984 len = sizeof(remote);
3985 client = accept(lsock, (struct sockaddr *) &remote, &len);
3986 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
3987 usleep(SLEEP_TIME * 1000);
3988 continue;
3989 } else if (client == -1 && (errno == EINTR)) {
3990 continue;
3991 } else if (client == -1) {
3992 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
3993 continue;
3994 }
Radek Krejci469aab82012-07-22 18:42:20 +02003995
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003996 /* set client's socket as non-blocking */
3997 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02003998
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003999 arg = malloc(sizeof(struct pass_to_thread));
4000 arg->client = client;
4001 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02004002
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004003 /* start new thread. It will serve this particular request and then terminate */
4004 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void *)arg)) != 0) {
4005 ERROR("Creating POSIX thread failed: %d\n", ret);
4006 } else {
4007 DEBUG("Thread %lu created", ptids[pthread_count]);
4008 pthread_count++;
4009 ptids = realloc (ptids, sizeof(pthread_t) * (pthread_count+1));
4010 ptids[pthread_count] = 0;
4011 }
Radek Krejci469aab82012-07-22 18:42:20 +02004012
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004013 /* check if some thread already terminated, free some resources by joining it */
4014 for (i = 0; i < pthread_count; i++) {
4015 if (pthread_tryjoin_np(ptids[i], (void **)&arg) == 0) {
4016 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
4017 pthread_count--;
4018 if (pthread_count > 0) {
4019 /* place last Thread ID on the place of joined one */
4020 ptids[i] = ptids[pthread_count];
4021 }
4022 }
4023 }
4024 DEBUG("Running %d threads", pthread_count);
4025 }
Radek Krejci469aab82012-07-22 18:42:20 +02004026
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004027 DEBUG("mod_netconf terminating...");
4028 /* join all threads */
4029 for (i = 0; i < pthread_count; i++) {
4030 pthread_timedjoin_np(ptids[i], (void **)&arg, &maxtime);
4031 }
Radek Krejci469aab82012-07-22 18:42:20 +02004032
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004033 #ifdef WITH_NOTIFICATIONS
4034 notification_close();
4035 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01004036
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004037 /* close all NETCONF sessions */
4038 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02004039
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004040 /* destroy rwlock */
4041 pthread_rwlock_destroy(&session_lock);
4042 pthread_rwlockattr_destroy(&lock_attrs);
David Kupka8e60a372012-09-04 09:15:20 +02004043
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004044 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02004045
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004046 free(ptids);
4047 close(lsock);
4048 exit(0);
4049 return;
4050
Tomas Cejkaef531ee2013-11-12 16:07:00 +01004051error_exit:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004052 close(lsock);
4053 free(ptids);
4054 return;
Radek Krejci469aab82012-07-22 18:42:20 +02004055}
4056
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004057int
4058main(int argc, char **argv)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01004059{
Michal Vaskoc3146782015-11-04 14:46:41 +01004060 struct sigaction action;
4061 sigset_t block_mask;
Michal Vaskoa53ef882015-11-24 11:02:01 +01004062 int daemonize = 0, i;
Michal Vaskoc3146782015-11-04 14:46:41 +01004063
Michal Vaskoa53ef882015-11-24 11:02:01 +01004064 if (argc > 3) {
4065 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
4066 return 1;
4067 }
4068
4069 sockname = SOCKET_FILENAME;
4070 for (i = 1; i < argc; ++i) {
4071 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
4072 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
4073 return 0;
4074 } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--daemon")) {
4075 daemonize = 1;
4076 } else {
4077 sockname = argv[i];
4078 }
4079 }
4080
4081 if (daemonize && (daemon(0, 0) == -1)) {
4082 ERROR("daemon() failed (%s)", strerror(errno));
4083 return 1;
Michal Vaskoc3146782015-11-04 14:46:41 +01004084 }
4085
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004086 sigfillset(&block_mask);
Michal Vaskoc3146782015-11-04 14:46:41 +01004087 action.sa_handler = signal_handler;
4088 action.sa_mask = block_mask;
4089 action.sa_flags = 0;
4090 sigaction(SIGINT, &action, NULL);
4091 sigaction(SIGTERM, &action, NULL);
4092
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004093 forked_proc();
4094 DEBUG("Terminated");
4095 return 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01004096}