blob: e40481ecefd7a9aad468195fb247a80462d0dbbb [file] [log] [blame]
Radek Krejci469aab82012-07-22 18:42:20 +02001/*!
2 * \file mod_netconf.c
3 * \brief NETCONF Apache modul for Netopeer
4 * \author Tomas Cejka <cejkat@cesnet.cz>
5 * \author Radek Krejci <rkrejci@cesnet.cz>
6 * \date 2011
7 * \date 2012
Tomas Cejka94da2c52013-01-08 18:20:30 +01008 * \date 2013
Radek Krejci469aab82012-07-22 18:42:20 +02009 */
10/*
Tomas Cejkad340dbf2013-03-24 20:36:57 +010011 * Copyright (C) 2011-2013 CESNET
Radek Krejci469aab82012-07-22 18:42:20 +020012 *
13 * LICENSE TERMS
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in
22 * the documentation and/or other materials provided with the
23 * distribution.
24 * 3. Neither the name of the Company nor the names of its contributors
25 * may be used to endorse or promote products derived from this
26 * software without specific prior written permission.
27 *
28 * ALTERNATIVELY, provided that this notice is retained in full, this
29 * product may be distributed under the terms of the GNU General Public
30 * License (GPL) version 2 or later, in which case the provisions
31 * of the GPL apply INSTEAD OF those given above.
32 *
33 * This software is provided ``as is'', and any express or implied
34 * warranties, including, but not limited to, the implied warranties of
35 * merchantability and fitness for a particular purpose are disclaimed.
36 * In no event shall the company or contributors be liable for any
37 * direct, indirect, incidental, special, exemplary, or consequential
38 * damages (including, but not limited to, procurement of substitute
39 * goods or services; loss of use, data, or profits; or business
40 * interruption) however caused and on any theory of liability, whether
41 * in contract, strict liability, or tort (including negligence or
42 * otherwise) arising in any way out of the use of this software, even
43 * if advised of the possibility of such damage.
44 *
45 */
Michal Vaskoc3146782015-11-04 14:46:41 +010046#define _GNU_SOURCE
Radek Krejci469aab82012-07-22 18:42:20 +020047
Radek Krejci7b4ddd02012-07-30 08:09:58 +020048#include <unistd.h>
49#include <poll.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010050#include <time.h>
Radek Krejci469aab82012-07-22 18:42:20 +020051#include <sys/types.h>
52#include <sys/socket.h>
53#include <sys/un.h>
Tomas Cejkad340dbf2013-03-24 20:36:57 +010054#include <sys/fcntl.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010055#include <sys/stat.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010056#include <pwd.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010057#include <errno.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010058#include <limits.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010059#include <grp.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010060#include <signal.h>
David Kupka8e60a372012-09-04 09:15:20 +020061#include <pthread.h>
62#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020063
Radek Krejci469aab82012-07-22 18:42:20 +020064#include <libnetconf.h>
Tomas Cejkab3cc64f2013-05-03 19:44:54 +020065#include <libnetconf_ssh.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020066
Tomas Cejka04e08f42014-03-27 19:52:34 +010067#include "../config.h"
68
Tomas Cejkad340dbf2013-03-24 20:36:57 +010069#ifdef WITH_NOTIFICATIONS
70#include "notification_module.h"
71#endif
72
Tomas Cejka94da2c52013-01-08 18:20:30 +010073#include "message_type.h"
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020074#include "mod_netconf.h"
Radek Krejci469aab82012-07-22 18:42:20 +020075
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010076#define SCHEMA_DIR "/tmp/yang_models"
Radek Krejci469aab82012-07-22 18:42:20 +020077#define MAX_PROCS 5
Tomas Cejka86f0fc12014-09-17 15:09:38 +020078#define SOCKET_FILENAME "/var/run/mod_netconf.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020079#define MAX_SOCKET_CL 10
80#define BUFFER_SIZE 4096
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010081#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
82#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020083
Michal Vasko7732dee2015-11-05 10:22:15 +010084/* sleep in master process for non-blocking socket reading, in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020085#define SLEEP_TIME 200
86
87#ifndef offsetof
88#define offsetof(type, member) ((size_t) ((type *) 0)->member)
89#endif
90
Tomas Cejka027f3bc2012-11-10 20:28:36 +010091/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020092struct timeval timeout = { 1, 0 };
93
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010094#define NCWITHDEFAULTS NCWD_MODE_NOTSET
Tomas Cejka5064c232013-01-17 09:30:58 +010095
Radek Krejci469aab82012-07-22 18:42:20 +020096#define MSG_OK 0
97#define MSG_OPEN 1
98#define MSG_DATA 2
99#define MSG_CLOSE 3
100#define MSG_ERROR 4
101#define MSG_UNKNOWN 5
102
Tomas Cejka47387fd2013-06-10 20:37:46 +0200103pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200104pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100105pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100106pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
107
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100108unsigned int session_key_generator = 1;
Michal Vaskoc3146782015-11-04 14:46:41 +0100109struct session_with_mutex *netconf_sessions_list = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +0100110static const char *sockname;
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200111static pthread_key_t notif_history_key;
Tomas Cejka442258e2014-04-01 18:17:18 +0200112pthread_key_t err_reply_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200113volatile int isterminated = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200114static char* password;
115
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100116json_object *create_ok_reply(void);
117json_object *create_data_reply(const char *data);
118static char *netconf_getschema(unsigned int session_key, const char *identifier, const char *version,
119 const char *format, json_object **err);
120static void node_add_metadata_recursive(struct lyd_node *data_tree, struct lys_module *module,
121 json_object *data_json_parent);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100122static void node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100123
124static void
125signal_handler(int sign)
Radek Krejci469aab82012-07-22 18:42:20 +0200126{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100127 switch (sign) {
128 case SIGINT:
129 case SIGTERM:
130 isterminated = 1;
131 break;
132 }
Radek Krejci469aab82012-07-22 18:42:20 +0200133}
134
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100135int
136netconf_callback_ssh_hostkey_check(const char* UNUSED(hostname), ssh_session UNUSED(session))
Radek Krejci469aab82012-07-22 18:42:20 +0200137{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100138 /* always approve */
139 return (EXIT_SUCCESS);
Radek Krejci469aab82012-07-22 18:42:20 +0200140}
141
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100142char *
143netconf_callback_sshauth_passphrase(const char *UNUSED(username), const char *UNUSED(hostname), const char *UNUSED(priv_key_file))
Radek Krejci469aab82012-07-22 18:42:20 +0200144{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100145 char *buf;
146 buf = strdup(password);
147 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200148}
149
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100150char *
151netconf_callback_sshauth_password(const char *UNUSED(username), const char *UNUSED(hostname))
Radek Krejci469aab82012-07-22 18:42:20 +0200152{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100153 char *buf;
154 buf = strdup(password);
155 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200156}
157
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100158char *
159netconf_callback_sshauth_interactive(const char *UNUSED(name), const char *UNUSED(instruction),
160 const char *UNUSED(prompt), int UNUSED(echo))
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200161{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100162 char *buf;
163 buf = strdup(password);
164 return (buf);
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200165}
166
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100167void
168netconf_callback_error_process(const char *UNUSED(tag),
169 const char *UNUSED(type),
170 const char *UNUSED(severity),
171 const char *UNUSED(apptag),
172 const char *UNUSED(path),
173 const char *message,
174 const char *UNUSED(attribute),
175 const char *UNUSED(element),
176 const char *UNUSED(ns),
177 const char *UNUSED(sid))
Radek Krejcic11fd862012-07-26 12:41:21 +0200178{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100179 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
180 if (err_reply_p == NULL) {
181 ERROR("Error message was not allocated. %s", __func__);
182 return;
183 }
184 json_object *err_reply = *err_reply_p;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100185
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100186 json_object *array = NULL;
187 if (err_reply == NULL) {
188 ERROR("error calback: empty error list");
189 pthread_mutex_lock(&json_lock);
190 err_reply = json_object_new_object();
191 array = json_object_new_array();
192 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
193 json_object_object_add(err_reply, "errors", array);
194 if (message != NULL) {
195 json_object_array_add(array, json_object_new_string(message));
196 }
197 pthread_mutex_unlock(&json_lock);
198 (*err_reply_p) = err_reply;
199 } else {
200 ERROR("error calback: nonempty error list");
201 pthread_mutex_lock(&json_lock);
202 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
203 if (message != NULL) {
204 json_object_array_add(array, json_object_new_string(message));
205 }
206 }
207 pthread_mutex_unlock(&json_lock);
208 }
209 pthread_setspecific(err_reply_key, err_reply_p);
210 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200211}
212
Tomas Cejka47387fd2013-06-10 20:37:46 +0200213/**
214 * should be used in locked area
215 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100216void
217prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200218{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100219 json_object *json_obj = NULL;
220 json_object *js_tmp = NULL;
221 char *old_sid = NULL;
222 const char *j_old_sid = NULL;
223 const char *cpbltstr;
224 struct nc_cpblts* cpblts = NULL;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200225
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100226 if (s == NULL) {
227 ERROR("No session given.");
228 return;
229 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200230
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100231 pthread_mutex_lock(&json_lock);
232 if (s->hello_message != NULL) {
233 ERROR("clean previous hello message");
234 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
235 j_old_sid = json_object_get_string(js_tmp);
236 if (j_old_sid != NULL) {
237 old_sid = strdup(j_old_sid);
238 }
239 }
240 json_object_put(s->hello_message);
241 s->hello_message = NULL;
242 }
243 s->hello_message = json_object_new_object();
244 if (session != NULL) {
245 if (old_sid != NULL) {
246 /* use previous sid */
247 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
248 free(old_sid);
249 old_sid = NULL;
250 } else {
251 /* we don't have old sid */
252 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
253 }
254 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
255 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
256 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
257 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
258 cpblts = nc_session_get_cpblts (session);
259 if (cpblts != NULL) {
260 json_obj = json_object_new_array();
261 nc_cpblts_iter_start (cpblts);
262 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
263 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
264 }
265 json_object_object_add(s->hello_message, "capabilities", json_obj);
266 }
267 DEBUG("%s", json_object_to_json_string(s->hello_message));
268 } else {
269 ERROR("Session was not given.");
270 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
271 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
272 }
273 DEBUG("Status info from hello message prepared");
274 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200275
276}
277
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100278void
279create_err_reply_p()
Tomas Cejka442258e2014-04-01 18:17:18 +0200280{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100281 json_object **err_reply = calloc(1, sizeof(json_object **));
282 if (err_reply == NULL) {
283 ERROR("Allocation of err_reply storage failed!");
284 return;
285 }
286 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
287 ERROR("cannot set thread-specific value.");
288 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200289}
290
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100291void
292clean_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200293{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100294 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
295 if (err_reply != NULL) {
296 if (*err_reply != NULL) {
297 pthread_mutex_lock(&json_lock);
298 json_object_put(*err_reply);
299 pthread_mutex_unlock(&json_lock);
300 }
301 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
302 ERROR("Cannot set thread-specific hash value.");
303 }
304 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200305}
306
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100307void
308free_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200309{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100310 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
311 if (err_reply != NULL) {
312 if (*err_reply != NULL) {
313 pthread_mutex_lock(&json_lock);
314 json_object_put(*err_reply);
315 pthread_mutex_unlock(&json_lock);
316 }
317 free(err_reply);
318 err_reply = NULL;
319 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
320 ERROR("Cannot set thread-specific hash value.");
321 }
322 }
323}
324
325static struct session_with_mutex *
326session_get_locked(unsigned int session_key, json_object **err)
327{
328 struct session_with_mutex *locked_session;
329
330 /* get non-exclusive (read) access to sessions_list (conns) */
331 DEBUG("LOCK wrlock %s", __func__);
332 if (pthread_rwlock_rdlock(&session_lock) != 0) {
333 if (*err) {
334 *err = create_error_reply("Locking failed.");
335 }
336 return NULL;
337 }
338 /* get session where send the RPC */
339 for (locked_session = netconf_sessions_list;
340 locked_session && (locked_session->session_key != session_key);
341 locked_session = locked_session->next);
342 if (!locked_session) {
343 if (*err) {
344 *err = create_error_reply("Session not found.");
345 }
346 return NULL;
347 }
348
349 /* get exclusive access to session */
350 DEBUG("LOCK mutex %s", __func__);
351 if (pthread_mutex_lock(&locked_session->lock) != 0) {
352 if (*err) {
353 *err = create_error_reply("Locking failed.");
354 }
355 goto wrlock_fail;
356 }
357 return locked_session;
358
359wrlock_fail:
360 DEBUG("UNLOCK wrlock %s", __func__);
361 pthread_rwlock_unlock(&session_lock);
362 return NULL;
363}
364
365static void
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100366session_user_activity(const char *username)
367{
368 struct session_with_mutex *sess;
369
370 for (sess = netconf_sessions_list; sess; sess = sess->next) {
371 if (!strcmp(nc_session_get_user(sess->session), username)) {
372 sess->last_activity = time(NULL);
373 }
374 }
375}
376
377static void
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100378session_unlock(struct session_with_mutex *locked_session)
379{
380 DEBUG("UNLOCK mutex %s", __func__);
381 pthread_mutex_unlock(&locked_session->lock);
382 DEBUG("UNLOCK wrlock %s", __func__);
383 pthread_rwlock_unlock(&session_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +0200384}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200385
Michal Vasko3fda9a92015-11-23 10:10:57 +0100386static void
387node_metadata_text(const char *text, const char *name, json_object *parent)
388{
389 json_object *obj;
390
391 if (!text) {
392 return;
393 }
394
395 obj = json_object_new_string(text);
396 json_object_object_add(parent, name, obj);
397}
398
399static void
400node_metadata_restr(struct lys_restr *restr, const char *name, json_object *parent)
401{
402 json_object *obj;
403
404 if (!restr) {
405 return;
406 }
407
408 obj = json_object_new_string(restr->expr);
409 json_object_object_add(parent, name, obj);
410}
411
412static void
413node_metadata_must(uint8_t must_size, struct lys_restr *must, json_object *parent)
414{
415 uint8_t i;
416 json_object *array, *obj;
417
418 if (!must_size || !must) {
419 return;
420 }
421
422 array = json_object_new_array();
423
424 for (i = 0; i < must_size; ++i) {
425 obj = json_object_new_string(must[i].expr);
426 json_object_array_add(array, obj);
427 }
428
429 json_object_object_add(parent, "must", array);
430}
431
432static void
433node_metadata_basic(struct lys_node *node, json_object *parent)
434{
435 json_object *obj;
436
437 /* description */
438 node_metadata_text(node->dsc, "description", parent);
439
440 /* reference */
441 node_metadata_text(node->ref, "reference", parent);
442
443 /* config */
444 if (node->flags & LYS_CONFIG_R) {
445 obj = json_object_new_boolean(0);
446 } else {
447 obj = json_object_new_boolean(1);
448 }
449 json_object_object_add(parent, "config", obj);
450
451 /* status */
452 if (node->flags & LYS_STATUS_DEPRC) {
453 obj = json_object_new_string("deprecated");
454 } else if (node->flags & LYS_STATUS_OBSLT) {
455 obj = json_object_new_string("obsolete");
456 } else {
457 obj = json_object_new_string("current");
458 }
459 json_object_object_add(parent, "status", obj);
460
461 /* mandatory */
462 if (node->flags & LYS_MAND_TRUE) {
463 obj = json_object_new_boolean(1);
464 } else {
465 obj = json_object_new_boolean(0);
466 }
467 json_object_object_add(parent, "mandatory", obj);
468
469 /* NACM extensions */
470 if (node->nacm) {
471 if (node->nacm & LYS_NACM_DENYW) {
472 obj = json_object_new_string("default-deny-write");
473 } else {
474 obj = json_object_new_string("default-deny-all");
475 }
476 json_object_object_add(parent, "ext", obj);
477 }
478}
479
480static void
481node_metadata_when(struct lys_when *when, json_object *parent)
482{
483 json_object *obj;
484
485 if (!when) {
486 return;
487 }
488
489 obj = json_object_new_string(when->cond);
490 json_object_object_add(parent, "when", obj);
491}
492
493static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100494node_metadata_children_recursive(struct lys_node *node, json_object **child_array, json_object **choice_array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100495{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100496 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100497 struct lys_node *child;
498
499 if (!node->child) {
500 return;
501 }
502
503 LY_TREE_FOR(node->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100504 if (child->nodetype == LYS_USES) {
505 node_metadata_children_recursive(child, child_array, choice_array);
506 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100507 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100508 if (!*child_array) {
509 *child_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100510 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100511 json_object_array_add(*child_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100512 } else if (child->nodetype == LYS_CHOICE) {
513 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100514 if (!*choice_array) {
515 *choice_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100516 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100517 json_object_array_add(*choice_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100518 }
519 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100520}
521
522static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100523node_metadata_cases_recursive(struct lys_node_choice *choice, json_object *array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100524{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100525 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100526 struct lys_node *child;
527
528 if (!choice->child) {
529 return;
530 }
531
Michal Vasko3fda9a92015-11-23 10:10:57 +0100532 LY_TREE_FOR(choice->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100533 if (child->nodetype == LYS_USES) {
534 node_metadata_cases_recursive((struct lys_node_choice *)child, array);
535 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_CASE)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100536 obj = json_object_new_string(child->name);
537 json_object_array_add(array, obj);
538 }
539 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100540}
541
542static void
543node_metadata_min_max(uint32_t min, uint32_t max, json_object *parent)
544{
545 json_object *obj;
546
547 if (min) {
548 obj = json_object_new_int(min);
549 json_object_object_add(parent, "min-elements", obj);
550 }
551
552 if (max) {
553 obj = json_object_new_int(max);
554 json_object_object_add(parent, "max-elements", obj);
555 }
556}
557
558static void
559node_metadata_ident_recursive(struct lys_ident *ident, json_object *array)
560{
561 struct lys_ident_der *cur;
562 json_object *obj;
563
564 if (!ident) {
565 return;
566 }
567
568 obj = json_object_new_string(ident->name);
569 json_object_array_add(array, obj);
570
571 for (cur = ident->der; cur; cur = cur->next) {
572 node_metadata_ident_recursive(cur->ident, array);
573 }
574}
575
576static void
577node_metadata_type(struct lys_type *type, struct lys_module *module, json_object *parent)
578{
579 json_object *obj, *array, *item;
580 char *str;
581 int i;
582
583 /* built-in YANG type */
584 if (!type->der->module) {
585 switch (type->base) {
586 case LY_TYPE_BINARY:
587 node_metadata_text("binary", "type", parent);
588 node_metadata_restr(type->info.binary.length, "length", parent);
589 break;
590 case LY_TYPE_BITS:
591 node_metadata_text("bits", "type", parent);
592
593 array = json_object_new_array();
594 for (i = 0; i < type->info.bits.count; ++i) {
595 item = json_object_new_object();
596 obj = json_object_new_string(type->info.bits.bit[i].name);
597 json_object_object_add(item, "name", obj);
598 obj = json_object_new_int(type->info.bits.bit[i].pos);
599 json_object_object_add(item, "position", obj);
600 json_object_array_add(array, item);
601 }
602 json_object_object_add(parent, "bits", array);
603 break;
604 case LY_TYPE_BOOL:
605 node_metadata_text("bool", "type", parent);
606 break;
607 case LY_TYPE_DEC64:
608 node_metadata_text("decimal64", "type", parent);
609 node_metadata_restr(type->info.dec64.range, "range", parent);
610 obj = json_object_new_int(type->info.dec64.dig);
611 json_object_object_add(parent, "fraction-digits", obj);
612 break;
613 case LY_TYPE_EMPTY:
614 node_metadata_text("empty", "type", parent);
615 break;
616 case LY_TYPE_ENUM:
617 node_metadata_text("enumeration", "type", parent);
618
619 array = json_object_new_array();
620 for (i = 0; i < type->info.enums.count; ++i) {
621 obj = json_object_new_string(type->info.enums.enm[i].name);
622 json_object_array_add(array, obj);
623 }
624 json_object_object_add(parent, "enumval", array);
625 break;
626 case LY_TYPE_IDENT:
627 node_metadata_text("identityref", "type", parent);
628
629 array = json_object_new_array();
630 node_metadata_ident_recursive(type->info.ident.ref, array);
631 json_object_object_add(parent, "identityval", array);
632 break;
633 case LY_TYPE_INST:
634 node_metadata_text("instance-identifier", "type", parent);
635 if (type->info.inst.req == -1) {
636 obj = json_object_new_boolean(0);
637 } else {
638 obj = json_object_new_boolean(1);
639 }
640 json_object_object_add(parent, "require-instance", obj);
641 break;
642 case LY_TYPE_LEAFREF:
643 node_metadata_text("leafref", "type", parent);
644 node_metadata_text(type->info.lref.path, "path", parent);
645 break;
646 case LY_TYPE_STRING:
647 node_metadata_text("string", "type", parent);
648 node_metadata_restr(type->info.str.length, "length", parent);
649 if (type->info.str.pat_count) {
650 array = json_object_new_array();
651 for (i = 0; i < type->info.str.pat_count; ++i) {
652 obj = json_object_new_string(type->info.str.patterns[i].expr);
653 json_object_array_add(array, obj);
654 }
655 json_object_object_add(parent, "pattern", array);
656 }
657 break;
658 case LY_TYPE_UNION:
659 node_metadata_text("union", "type", parent);
660 array = json_object_new_array();
661 for (i = 0; i < type->info.uni.count; ++i) {
662 obj = json_object_new_object();
663 node_metadata_type(&type->info.uni.types[i], module, obj);
664 json_object_array_add(array, obj);
665 }
666 json_object_object_add(parent, "types", array);
667 break;
668 case LY_TYPE_INT8:
669 node_metadata_text("int8", "type", parent);
670 node_metadata_restr(type->info.num.range, "range", parent);
671 break;
672 case LY_TYPE_UINT8:
673 node_metadata_text("uint8", "type", parent);
674 node_metadata_restr(type->info.num.range, "range", parent);
675 break;
676 case LY_TYPE_INT16:
677 node_metadata_text("int16", "type", parent);
678 node_metadata_restr(type->info.num.range, "range", parent);
679 break;
680 case LY_TYPE_UINT16:
681 node_metadata_text("uint16", "type", parent);
682 node_metadata_restr(type->info.num.range, "range", parent);
683 break;
684 case LY_TYPE_INT32:
685 node_metadata_text("int32", "type", parent);
686 node_metadata_restr(type->info.num.range, "range", parent);
687 break;
688 case LY_TYPE_UINT32:
689 node_metadata_text("uint32", "type", parent);
690 node_metadata_restr(type->info.num.range, "range", parent);
691 break;
692 case LY_TYPE_INT64:
693 node_metadata_text("int64", "type", parent);
694 node_metadata_restr(type->info.num.range, "range", parent);
695 break;
696 case LY_TYPE_UINT64:
697 node_metadata_text("uint64", "type", parent);
698 node_metadata_restr(type->info.num.range, "range", parent);
699 break;
700 default:
701 ERROR("Internal: unknown type (%s:%d)", __FILE__, __LINE__);
702 break;
703 }
704
705 /* typedef */
706 } else {
707 if (!module || !type->module_name || !strcmp(type->module_name, module->name)) {
708 node_metadata_text(type->der->name, "type", parent);
709 } else {
710 asprintf(&str, "%s:%s", type->module_name, type->der->name);
711 node_metadata_text(str, "type", parent);
712 free(str);
713 }
714 obj = json_object_new_object();
715 node_metadata_typedef(type->der, obj);
716 json_object_object_add(parent, "typedef", obj);
717 }
718}
719
720static void
721node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent)
722{
723 json_object *obj;
724
725 /* description */
726 node_metadata_text(tpdf->dsc, "description", parent);
727
728 /* reference */
729 node_metadata_text(tpdf->ref, "reference", parent);
730
731 /* status */
732 if (tpdf->flags & LYS_STATUS_DEPRC) {
733 obj = json_object_new_string("deprecated");
734 } else if (tpdf->flags & LYS_STATUS_OBSLT) {
735 obj = json_object_new_string("obsolete");
736 } else {
737 obj = json_object_new_string("current");
738 }
739 json_object_object_add(parent, "status", obj);
740
741 /* type */
742 node_metadata_type(&tpdf->type, tpdf->module, parent);
743
744 /* units */
745 node_metadata_text(tpdf->units, "units", parent);
746
747 /* default */
748 node_metadata_text(tpdf->dflt, "default", parent);
749}
750
751static void
752node_metadata_container(struct lys_node_container *cont, json_object *parent)
753{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100754 json_object *obj, *child_array = NULL, *choice_array = NULL;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100755
756 /* element type */
757 obj = json_object_new_string("container");
758 json_object_object_add(parent, "eltype", obj);
759
760 /* shared info */
761 node_metadata_basic((struct lys_node *)cont, parent);
762
763 /* must */
764 node_metadata_must(cont->must_size, cont->must, parent);
765
766 /* presence */
767 node_metadata_text(cont->presence, "presence", parent);
768
769 /* when */
770 node_metadata_when(cont->when, parent);
771
772 /* children & choice */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100773 node_metadata_children_recursive((struct lys_node *)cont, &child_array, &choice_array);
774 if (child_array) {
775 json_object_object_add(parent, "children", child_array);
776 }
777 if (choice_array) {
778 json_object_object_add(parent, "choice", choice_array);
779 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100780}
781
782static void
783node_metadata_choice(struct lys_node_choice *choice, json_object *parent)
784{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100785 json_object *obj, *array;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100786
787 /* element type */
788 obj = json_object_new_string("choice");
789 json_object_object_add(parent, "eltype", obj);
790
791 /* shared info */
792 node_metadata_basic((struct lys_node *)choice, parent);
793
794 /* default */
795 node_metadata_text(choice->dflt->name, "default", parent);
796
797 /* when */
798 node_metadata_when(choice->when, parent);
799
800 /* cases */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100801 if (choice->child) {
802 array = json_object_new_array();
803 node_metadata_cases_recursive(choice, array);
804 json_object_object_add(parent, "cases", array);
805 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100806}
807
808static void
809node_metadata_leaf(struct lys_node_leaf *leaf, json_object *parent)
810{
811 json_object *obj;
812 struct lys_node_list *list;
813 int is_key, i;
814
815 /* element type */
816 obj = json_object_new_string("leaf");
817 json_object_object_add(parent, "eltype", obj);
818
819 /* shared info */
820 node_metadata_basic((struct lys_node *)leaf, parent);
821
822 /* type */
823 node_metadata_type(&leaf->type, leaf->module, parent);
824
825 /* units */
826 node_metadata_text(leaf->units, "units", parent);
827
828 /* default */
829 node_metadata_text(leaf->dflt, "default", parent);
830
831 /* must */
832 node_metadata_must(leaf->must_size, leaf->must, parent);
833
834 /* when */
835 node_metadata_when(leaf->when, parent);
836
837 /* iskey */
838 is_key = 0;
839 list = (struct lys_node_list *)lys_parent((struct lys_node *)leaf);
840 if (list && (list->nodetype == LYS_LIST)) {
841 for (i = 0; i < list->keys_size; ++i) {
842 if (list->keys[i] == leaf) {
843 is_key = 1;
844 break;
845 }
846 }
847 }
848 obj = json_object_new_boolean(is_key);
849 json_object_object_add(parent, "iskey", obj);
850}
851
852static void
853node_metadata_leaflist(struct lys_node_leaflist *llist, json_object *parent)
854{
855 json_object *obj;
856
857 /* element type */
858 obj = json_object_new_string("leaf-list");
859 json_object_object_add(parent, "eltype", obj);
860
861 /* shared info */
862 node_metadata_basic((struct lys_node *)llist, parent);
863
864 /* type */
865 node_metadata_type(&llist->type, llist->module, parent);
866
867 /* units */
868 node_metadata_text(llist->units, "units", parent);
869
870 /* must */
871 node_metadata_must(llist->must_size, llist->must, parent);
872
873 /* when */
874 node_metadata_when(llist->when, parent);
875
876 /* min/max-elements */
877 node_metadata_min_max(llist->min, llist->max, parent);
878}
879
880static void
881node_metadata_list(struct lys_node_list *list, json_object *parent)
882{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100883 json_object *obj, *array, *child_array = NULL, *choice_array = NULL;;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100884 int i;
885 unsigned int j;
886
887 /* element type */
888 obj = json_object_new_string("list");
889 json_object_object_add(parent, "eltype", obj);
890
891 /* shared info */
892 node_metadata_basic((struct lys_node *)list, parent);
893
894 /* must */
895 node_metadata_must(list->must_size, list->must, parent);
896
897 /* when */
898 node_metadata_when(list->when, parent);
899
900 /* min/max-elements */
901 node_metadata_min_max(list->min, list->max, parent);
902
903 /* keys */
904 if (list->keys_size) {
905 array = json_object_new_array();
906 for (i = 0; i < list->keys_size; ++i) {
907 obj = json_object_new_string(list->keys[i]->name);
908 json_object_array_add(array, obj);
909 }
910 json_object_object_add(parent, "keys", array);
911 }
912
913 /* unique */
914 if (list->unique_size) {
915 array = json_object_new_array();
916 for (i = 0; i < list->unique_size; ++i) {
917 for (j = 0; j < list->unique[i].expr_size; ++j) {
918 obj = json_object_new_string(list->unique[i].expr[j]);
919 json_object_array_add(array, obj);
920 }
921 }
922 json_object_object_add(parent, "unique", array);
923 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100924
925 /* children & choice */
926 node_metadata_children_recursive((struct lys_node *)list, &child_array, &choice_array);
927 if (child_array) {
928 json_object_object_add(parent, "children", child_array);
929 }
930 if (choice_array) {
931 json_object_object_add(parent, "choice", choice_array);
932 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100933}
934
935static void
936node_metadata_anyxml(struct lys_node_anyxml *anyxml, json_object *parent)
937{
938 json_object *obj;
939
940 /* element type */
941 obj = json_object_new_string("anyxml");
942 json_object_object_add(parent, "eltype", obj);
943
944 /* shared info */
945 node_metadata_basic((struct lys_node *)anyxml, parent);
946
947 /* must */
948 node_metadata_must(anyxml->must_size, anyxml->must, parent);
949
950 /* when */
951 node_metadata_when(anyxml->when, parent);
952
953}
954
955static void
956node_metadata_case(struct lys_node_case *cas, json_object *parent)
957{
958 json_object *obj;
959
960 /* element type */
961 obj = json_object_new_string("case");
962 json_object_object_add(parent, "eltype", obj);
963
964 /* shared info */
965 node_metadata_basic((struct lys_node *)cas, parent);
966
967 /* when */
968 node_metadata_when(cas->when, parent);
969}
970
Michal Vaskoa45770b2015-11-23 15:49:41 +0100971static void
972node_metadata_rpc(struct lys_node_rpc *rpc, json_object *parent)
973{
974 json_object *obj;
975
976 /* element type */
977 obj = json_object_new_string("rpc");
978 json_object_object_add(parent, "eltype", obj);
979
980 /* description */
981 node_metadata_text(rpc->dsc, "description", parent);
982
983 /* reference */
984 node_metadata_text(rpc->ref, "reference", parent);
985
986 /* status */
987 if (rpc->flags & LYS_STATUS_DEPRC) {
988 obj = json_object_new_string("deprecated");
989 } else if (rpc->flags & LYS_STATUS_OBSLT) {
990 obj = json_object_new_string("obsolete");
991 } else {
992 obj = json_object_new_string("current");
993 }
994 json_object_object_add(parent, "status", obj);
995}
996
997static void
998node_metadata_model(struct lys_module *module, json_object *parent)
999{
1000 json_object *obj, *array, *item;
1001 int i;
1002
1003 /* yang-version */
1004 if (module->version == 2) {
1005 obj = json_object_new_string("1.1");
1006 } else {
1007 obj = json_object_new_string("1.0");
1008 }
1009 json_object_object_add(parent, "yang-version", obj);
1010
1011 /* namespace */
1012 node_metadata_text(module->ns, "namespace", parent);
1013
1014 /* prefix */
1015 node_metadata_text(module->prefix, "prefix", parent);
1016
1017 /* contact */
1018 node_metadata_text(module->contact, "contact", parent);
1019
1020 /* organization */
1021 node_metadata_text(module->org, "organization", parent);
1022
1023 /* revision */
1024 if (module->rev_size) {
1025 node_metadata_text(module->rev[0].date, "revision", parent);
1026 }
1027
1028 /* description */
1029 node_metadata_text(module->dsc, "description", parent);
1030
1031 /* import */
1032 if (module->imp_size) {
1033 array = json_object_new_array();
1034 for (i = 0; i < module->imp_size; ++i) {
1035 item = json_object_new_object();
1036
1037 node_metadata_text(module->imp[i].module->name, "name", item);
1038 node_metadata_text(module->imp[i].prefix, "prefix", item);
1039 if (module->imp[i].rev && module->imp[i].rev[0]) {
1040 node_metadata_text(module->imp[i].rev, "revision", item);
1041 }
1042
1043 json_object_array_add(array, item);
1044 }
1045 json_object_object_add(parent, "imports", array);
1046 }
1047
1048 /* include */
1049 if (module->inc_size) {
1050 array = json_object_new_array();
1051 for (i = 0; i < module->inc_size; ++i) {
1052 item = json_object_new_object();
1053
1054 node_metadata_text(module->inc[i].submodule->name, "name", item);
1055 if (module->inc[i].rev && module->inc[i].rev[0]) {
1056 node_metadata_text(module->inc[i].rev, "revision", item);
1057 }
1058
1059 json_object_array_add(array, item);
1060 }
1061 json_object_object_add(parent, "includes", array);
1062 }
1063}
1064
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001065/**
1066 * \defgroup netconf_operations NETCONF operations
1067 * The list of NETCONF operations that mod_netconf supports.
1068 * @{
1069 */
1070
1071/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001072 * \brief Send RPC and wait for reply with timeout.
1073 *
1074 * \param[in] session libnetconf session
1075 * \param[in] rpc prepared RPC message
1076 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
1077 * \param[out] reply reply from the server
1078 * \return Value from nc_session_recv_reply() or NC_MSG_UNKNOWN when send_rpc() fails.
1079 * On success, it returns NC_MSG_REPLY.
1080 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001081NC_MSG_TYPE
1082netconf_send_recv_timed(struct nc_session *session, nc_rpc *rpc, int timeout, nc_reply **reply)
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001083{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001084 const nc_msgid msgid = NULL;
1085 NC_MSG_TYPE ret = NC_MSG_UNKNOWN;
1086 msgid = nc_session_send_rpc(session, rpc);
1087 if (msgid == NULL) {
1088 return ret;
1089 }
1090 do {
1091 ret = nc_session_recv_reply(session, timeout, reply);
1092 if (ret == NC_MSG_HELLO) {
1093 ERROR("<hello> received instead reply, it will be lost.");
1094 nc_reply_free(*reply);
1095 }
1096 if (ret == NC_MSG_WOULDBLOCK) {
1097 ERROR("Timeout for receiving RPC reply expired.");
1098 break;
1099 }
1100 } while (ret == NC_MSG_HELLO || ret == NC_MSG_NOTIFICATION);
1101 return ret;
1102}
1103
1104static int
1105ctx_download_module(struct session_with_mutex *session, const char *model_name, const char *revision, const char *schema_dir)
1106{
1107 json_object *err = NULL;
1108 char *model_data = NULL, *model_path;
1109 size_t length;
1110 FILE *file;
1111
1112 DEBUG("UNLOCK rwlock %s", __func__);
1113 if (pthread_rwlock_unlock(&session_lock) != 0) {
1114 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1115 return 1;
1116 }
1117
1118 model_data = netconf_getschema(session->session_key, model_name, revision, "yin", &err);
1119
1120 DEBUG("LOCK rwlock %s", __func__);
1121 if (pthread_rwlock_wrlock(&session_lock) != 0) {
1122 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1123 return 1;
1124 }
1125
1126 if (!model_data) {
1127 if (err) {
1128 json_object_put(err);
1129 }
1130 ERROR("Failed to get-schema of \"%s\".", model_name);
1131 return 1;
1132 }
1133
1134 if (revision) {
1135 asprintf(&model_path, "%s/%s@%s.yin", schema_dir, model_name, revision);
1136 } else {
1137 asprintf(&model_path, "%s/%s.yin", schema_dir, model_name);
1138 }
1139
1140 file = fopen(model_path, "w");
1141 if (!file) {
1142 ERROR("Failed to open \"%s\" for writing (%s).", model_path, strerror(errno));
1143 free(model_data);
1144 free(model_path);
1145 return 1;
1146 }
1147 free(model_path);
1148
1149 length = strlen(model_data);
1150 if (fwrite(model_data, 1, length, file) < length) {
1151 ERROR("Failed to store the model \"%s\".", model_name);
1152 free(model_data);
1153 fclose(file);
1154 return 1;
1155 }
1156
1157 free(model_data);
1158 fclose(file);
1159 return 0;
1160}
1161
1162static void
1163ctx_enable_features(struct lys_module *module, const char *cpblt)
1164{
1165 char *ptr, *ptr2, *features = NULL;
1166
1167 /* parse features */
1168 ptr = strstr(cpblt, "features=");
1169 if (ptr) {
1170 ptr += 9;
1171 ptr2 = strchr(ptr, '&');
1172 if (!ptr2) {
1173 ptr2 = ptr + strlen(ptr);
1174 }
1175 features = strndup(ptr, ptr2 - ptr);
1176 }
1177
1178 /* enable features */
1179 if (features) {
1180 /* basically manual strtok_r (to avoid macro) */
1181 ptr2 = features;
1182 for (ptr = features; *ptr; ++ptr) {
1183 if (*ptr == ',') {
1184 *ptr = '\0';
1185 /* remember last feature */
1186 ptr2 = ptr + 1;
1187 }
1188 }
1189
1190 ptr = features;
1191 lys_features_enable(module, ptr);
1192 while (ptr != ptr2) {
1193 ptr += strlen(ptr) + 1;
1194 lys_features_enable(module, ptr);
1195 }
1196
1197 free(features);
1198 }
1199}
1200
1201static void
1202ctx_enable_capabs(struct lys_module *ietfnc, json_object *cpb_array)
1203{
1204 json_object *item;
1205 int i;
1206 const char *capab;
1207
1208 /* set supported capabilities from ietf-netconf */
1209 for (i = 0; i < json_object_array_length(cpb_array); ++i) {
1210 item = json_object_array_get_idx(cpb_array, i);
1211 capab = json_object_get_string(item);
1212
1213 if (!strncmp(capab, "urn:ietf:params:netconf:capability:", 35)) {
1214 if (!strncmp(capab, "writable-running", 16)) {
1215 lys_features_enable(ietfnc, "writable-running");
1216 } else if (!strncmp(capab, "candidate", 9)) {
1217 lys_features_enable(ietfnc, "candidate");
1218 } else if (!strcmp(capab, "confirmed-commit:1.1")) {
1219 lys_features_enable(ietfnc, "confirmed-commit");
1220 } else if (!strncmp(capab, "rollback-on-error", 17)) {
1221 lys_features_enable(ietfnc, "rollback-on-error");
1222 } else if (!strcmp(capab, "validate:1.1")) {
1223 lys_features_enable(ietfnc, "validate");
1224 } else if (!strncmp(capab, "startup", 7)) {
1225 lys_features_enable(ietfnc, "startup");
1226 } else if (!strncmp(capab, "url", 3)) {
1227 lys_features_enable(ietfnc, "url");
1228 } else if (!strncmp(capab, "xpath", 5)) {
1229 lys_features_enable(ietfnc, "xpath");
1230 }
1231 }
1232 }
1233}
1234
1235static int
1236prepare_context(struct session_with_mutex *session)
1237{
1238 struct lys_module *module;
1239 json_object *array, *item;
1240 char *ptr, *ptr2;
1241 char *model_name = NULL, *revision = NULL;
1242 const char *capab;
1243 int i, get_schema_support;
1244
1245 if (json_object_object_get_ex(session->hello_message, "capabilities", &array) == FALSE) {
1246 return 1;
1247 }
1248
1249 get_schema_support = 0;
1250 for (i = 0; i < json_object_array_length(array); ++i) {
1251 item = json_object_array_get_idx(array, i);
1252 capab = json_object_get_string(item);
1253
1254 if (!strncmp(capab, "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", 51)) {
1255 get_schema_support = 1;
1256 break;
1257 }
1258 }
1259
1260 if (get_schema_support) {
1261 errno = 0;
1262 if (eaccess(SCHEMA_DIR, W_OK)) {
1263 if (errno == ENOENT) {
1264 if (mkdir(SCHEMA_DIR, 00755)) {
1265 ERROR("Failed to create temp model dir \"%s\" (%s).", SCHEMA_DIR, strerror(errno));
1266 return 1;
1267 }
1268 } else {
1269 ERROR("Unable to write to temp model dir \"%s\" (%s).", SCHEMA_DIR, strerror(errno));
1270 return 1;
1271 }
1272 }
1273
1274 session->ctx = ly_ctx_new(SCHEMA_DIR);
1275 } else {
1276 /* TODO */
1277 session->ctx = ly_ctx_new(NULL);
1278 }
1279
1280loop:
1281 /* download all the models first or load them directly */
1282 for (i = 0; i < json_object_array_length(array); ++i) {
1283 item = json_object_array_get_idx(array, i);
1284 capab = json_object_get_string(item);
1285 if (!strncmp(capab, "urn:ietf:params:netconf:capability", 34)
1286 || !strncmp(capab, "urn:ietf:params:netconf:base", 28)) {
1287 continue;
1288 }
1289
1290 /* get module */
1291 ptr = strstr(capab, "module=");
1292 if (!ptr) {
1293 ERROR("Unknown capability \"%s\" could not be parsed.", capab);
1294 continue;
1295 }
1296 ptr += 7;
1297 ptr2 = strchr(ptr, '&');
1298 if (!ptr2) {
1299 ptr2 = ptr + strlen(ptr);
1300 }
1301 model_name = strndup(ptr, ptr2 - ptr);
1302
1303 /* get revision */
1304 ptr = strstr(capab, "revision=");
1305 if (ptr) {
1306 ptr += 9;
1307 ptr2 = strchr(ptr, '&');
1308 if (!ptr2) {
1309 ptr2 = ptr + strlen(ptr);
1310 }
1311 revision = strndup(ptr, ptr2 - ptr);
1312 }
1313
1314 if (get_schema_support) {
1315 ctx_download_module(session, model_name, revision, SCHEMA_DIR);
1316 } else {
1317 module = ly_ctx_get_module(session->ctx, model_name, revision);
1318 if (!module) {
1319 module = ly_ctx_load_module(session->ctx, NULL, model_name, revision);
1320 if (module) {
1321 if (!strcmp(module->name, "ietf-netconf")) {
1322 ctx_enable_capabs(module, array);
1323 } else {
1324 ctx_enable_features(module, capab);
1325 }
1326 }
1327 }
1328 }
1329
1330 free(model_name);
1331 free(revision);
1332 revision = NULL;
1333 }
1334
1335 if (get_schema_support) {
1336 /* we have downloaded all the models, load them now */
1337 get_schema_support = 0;
1338 goto loop;
1339 }
1340
1341 return 0;
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001342}
1343
1344/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001345 * \brief Connect to NETCONF server
1346 *
1347 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
1348 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001349static unsigned int
1350netconf_connect(const char *host, const char *port, const char *user, const char *pass, struct nc_cpblts *cpblts)
Radek Krejci469aab82012-07-22 18:42:20 +02001351{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001352 struct nc_session* session = NULL;
1353 struct session_with_mutex *locked_session, *last_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001354
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001355 /* connect to the requested NETCONF server */
1356 password = (char*)pass;
1357 DEBUG("prepare to connect %s@%s:%s", user, host, port);
1358 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
1359 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +02001360
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001361 /* if connected successful, add session to the list */
1362 if (session != NULL) {
1363 if ((locked_session = calloc(1, sizeof(struct session_with_mutex))) == NULL || pthread_mutex_init (&locked_session->lock, NULL) != 0) {
1364 nc_session_free(session);
1365 session = NULL;
1366 free(locked_session);
1367 locked_session = NULL;
1368 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
1369 return 0;
1370 }
1371 locked_session->session = session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001372 locked_session->hello_message = NULL;
1373 locked_session->closed = 0;
1374 pthread_mutex_init(&locked_session->lock, NULL);
1375 DEBUG("Before session_lock");
1376 /* get exclusive access to sessions_list (conns) */
1377 DEBUG("LOCK wrlock %s", __func__);
1378 if (pthread_rwlock_wrlock(&session_lock) != 0) {
1379 nc_session_free(session);
1380 free(locked_session);
1381 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1382 return 0;
1383 }
1384 locked_session->ntfc_subscribed = 0;
1385 DEBUG("Add connection to the list");
1386 if (!netconf_sessions_list) {
Michal Vaskoc3146782015-11-04 14:46:41 +01001387 netconf_sessions_list = locked_session;
1388 } else {
1389 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
1390 last_session->next = locked_session;
1391 locked_session->prev = last_session;
1392 }
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001393 session_user_activity(nc_session_get_user(locked_session->session));
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001394
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001395 /* no need to lock session, noone can read it while we have wrlock */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001396
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001397 /* store information about session from hello message for future usage */
1398 prepare_status_message(locked_session, session);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001399
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001400 /* create context from the hello message cpabilities */
1401 if (prepare_context(locked_session)) {
1402 nc_session_free(session);
1403 free(locked_session);
1404 DEBUG("UNLOCK wrlock %s", __func__);
1405 pthread_rwlock_unlock(&session_lock);
1406 ERROR("Failed to prepare context");
1407 return 0;
1408 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001409
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001410 DEBUG("NETCONF session established");
1411 locked_session->session_key = session_key_generator;
1412 ++session_key_generator;
1413 if (session_key_generator == UINT_MAX) {
1414 session_key_generator = 1;
1415 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001416
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001417 DEBUG("Before session_unlock");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001418 /* unlock session list */
1419 DEBUG("UNLOCK wrlock %s", __func__);
1420 if (pthread_rwlock_unlock(&session_lock) != 0) {
1421 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1422 }
Radek Krejci469aab82012-07-22 18:42:20 +02001423
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001424 return locked_session->session_key;
1425 }
1426
1427 ERROR("Connection could not be established");
1428 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +02001429}
1430
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001431static int
1432close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +02001433{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001434 DEBUG("lock private lock.");
1435 DEBUG("LOCK mutex %s", __func__);
1436 if (pthread_mutex_lock(&locked_session->lock) != 0) {
1437 ERROR("Error while locking rwlock");
1438 }
1439 locked_session->ntfc_subscribed = 0;
1440 locked_session->closed = 1;
1441 if (locked_session->session != NULL) {
1442 nc_session_free(locked_session->session);
1443 locked_session->session = NULL;
1444 }
1445 DEBUG("session closed.");
1446 DEBUG("unlock private lock.");
1447 DEBUG("UNLOCK mutex %s", __func__);
1448 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
1449 ERROR("Error while locking rwlock");
1450 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001451
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001452 DEBUG("unlock session lock.");
1453 DEBUG("closed session, disabled notif(?), wait 0.5s");
1454 usleep(500000); /* let notification thread stop */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001455
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001456 /* session shouldn't be used by now */
1457 /** \todo free all notifications from queue */
1458 free(locked_session->notifications);
1459 pthread_mutex_destroy(&locked_session->lock);
1460 if (locked_session->hello_message != NULL) {
1461 json_object_put(locked_session->hello_message);
1462 locked_session->hello_message = NULL;
1463 }
1464 locked_session->session = NULL;
1465 ly_ctx_destroy(locked_session->ctx);
1466 free(locked_session);
1467 locked_session = NULL;
1468 DEBUG("NETCONF session closed, everything cleared.");
1469 return (EXIT_SUCCESS);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001470}
1471
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001472static int
1473netconf_close(unsigned int session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +02001474{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001475 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001476
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001477 DEBUG("Session to close: %u", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001478
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001479 /* get exclusive (write) access to sessions_list (conns) */
1480 DEBUG("lock session lock.");
1481 DEBUG("LOCK wrlock %s", __func__);
1482 if (pthread_rwlock_wrlock (&session_lock) != 0) {
1483 ERROR("Error while locking rwlock");
1484 (*reply) = create_error_reply("Internal: Error while locking.");
1485 return EXIT_FAILURE;
1486 }
1487 /* remove session from the active sessions list -> nobody new can now work with session */
1488 for (locked_session = netconf_sessions_list;
1489 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01001490 locked_session = locked_session->next);
1491
1492 if (!locked_session) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001493 ERROR("Could not find the session %u to close.", session_key);
1494 (*reply) = create_error_reply("Internal: Error while finding a session.");
Michal Vaskoc3146782015-11-04 14:46:41 +01001495 return EXIT_FAILURE;
1496 }
1497
1498 if (!locked_session->prev) {
1499 netconf_sessions_list = netconf_sessions_list->next;
1500 netconf_sessions_list->prev = NULL;
1501 } else {
1502 locked_session->prev->next = locked_session->next;
1503 if (locked_session->next) {
1504 locked_session->next->prev = locked_session->prev;
1505 }
1506 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001507
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001508 DEBUG("UNLOCK wrlock %s", __func__);
1509 if (pthread_rwlock_unlock (&session_lock) != 0) {
1510 ERROR("Error while unlocking rwlock");
1511 (*reply) = create_error_reply("Internal: Error while unlocking.");
1512 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001513
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001514 if ((locked_session != NULL) && (locked_session->session != NULL)) {
1515 return close_and_free_session(locked_session);
1516 } else {
1517 ERROR("Unknown session to close");
1518 (*reply) = create_error_reply("Internal: Unkown session to close.");
1519 return (EXIT_FAILURE);
1520 }
1521 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +02001522}
1523
Tomas Cejkac7929632013-10-24 19:25:15 +02001524/**
1525 * Test reply message type and return error message.
1526 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001527 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +01001528 * \param[in] session_key session ID, 0 to disable disconnect on error
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001529 * \param[in] msgt RPC-REPLY message type
Tomas Cejkac7929632013-10-24 19:25:15 +02001530 * \param[out] data
1531 * \return NULL on success
1532 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001533json_object *
1534netconf_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 +02001535{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001536 NC_REPLY_TYPE replyt;
1537 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001538
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001539 /* process the result of the operation */
1540 switch (msgt) {
1541 case NC_MSG_UNKNOWN:
1542 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
1543 ERROR("mod_netconf: receiving rpc-reply failed");
1544 if (session_key) {
1545 netconf_close(session_key, &err);
1546 }
1547 if (err != NULL) {
1548 return err;
1549 }
1550 return create_error_reply("Internal: Receiving RPC-REPLY failed.");
1551 }
1552 case NC_MSG_NONE:
1553 /* there is error handled by callback */
1554 if (data != NULL) {
1555 free(*data);
1556 (*data) = NULL;
1557 }
1558 return NULL;
1559 case NC_MSG_REPLY:
1560 switch (replyt = nc_reply_get_type(reply)) {
1561 case NC_REPLY_OK:
1562 if ((data != NULL) && (*data != NULL)) {
1563 free(*data);
1564 (*data) = NULL;
1565 }
1566 return create_ok_reply();
1567 case NC_REPLY_DATA:
1568 if (((*data) = nc_reply_get_data(reply)) == NULL) {
1569 ERROR("mod_netconf: no data from reply");
1570 return create_error_reply("Internal: No data from reply received.");
1571 } else {
1572 return NULL;
1573 }
1574 break;
1575 case NC_REPLY_ERROR:
1576 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
1577 if (data != NULL) {
1578 free(*data);
1579 (*data) = NULL;
1580 }
1581 return create_error_reply(nc_reply_get_errormsg(reply));
1582 default:
1583 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
1584 if (data != NULL) {
1585 free(*data);
1586 (*data) = NULL;
1587 }
1588 return create_error_reply("Unknown type of NETCONF reply.");
1589 }
1590 break;
1591 default:
1592 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
1593 if (data != NULL) {
1594 free(*data);
1595 (*data) = NULL;
1596 }
1597 return create_error_reply("Internal: Unexpected RPC-REPLY message type.");
1598 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001599}
1600
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001601json_object *
1602netconf_unlocked_op(struct nc_session *session, nc_rpc *rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001603{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001604 nc_reply* reply = NULL;
1605 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001606
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001607 /* check requests */
1608 if (rpc == NULL) {
1609 ERROR("mod_netconf: rpc is not created");
1610 return create_error_reply("Internal error: RPC is not created");
1611 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001612
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001613 if (session != NULL) {
1614 /* send the request and get the reply */
1615 msgt = netconf_send_recv_timed(session, rpc, 50000, &reply);
1616 /* process the result of the operation */
1617 return netconf_test_reply(session, 0, msgt, reply, NULL);
1618 } else {
1619 ERROR("Unknown session to process.");
1620 return create_error_reply("Internal error: Unknown session to process.");
1621 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001622}
1623
Tomas Cejkac7929632013-10-24 19:25:15 +02001624/**
1625 * Perform RPC method that returns data.
1626 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001627 * \param[in] session_id session identifier
1628 * \param[in] rpc RPC message to perform
1629 * \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 +02001630 * \return NULL on success, json object with error otherwise
1631 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001632static json_object *
1633netconf_op(unsigned int session_key, nc_rpc *rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +02001634{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001635 struct session_with_mutex * locked_session;
1636 nc_reply* reply = NULL;
1637 json_object *res = NULL;
1638 char *data = NULL;
1639 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +02001640
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001641 /* check requests */
1642 if (rpc == NULL) {
1643 ERROR("mod_netconf: rpc is not created");
1644 res = create_error_reply("Internal: RPC could not be created.");
1645 data = NULL;
1646 goto finished;
1647 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001648
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001649 locked_session = session_get_locked(session_key, &res);
1650 if (!locked_session) {
1651 ERROR("Unknown session or locking failed.");
1652 goto finished;
1653 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001654
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001655 session_user_activity(nc_session_get_user(locked_session->session));
Tomas Cejkac7929632013-10-24 19:25:15 +02001656
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001657 /* send the request and get the reply */
1658 msgt = netconf_send_recv_timed(locked_session->session, rpc, 2000000, &reply);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001659
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001660 session_unlock(locked_session);
Tomas Cejkac7929632013-10-24 19:25:15 +02001661
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001662 res = netconf_test_reply(locked_session->session, session_key, msgt, reply, &data);
Radek Krejcia332b692012-11-12 16:15:54 +01001663
Tomas Cejkac7929632013-10-24 19:25:15 +02001664finished:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001665 nc_reply_free(reply);
1666 if (received_data != NULL) {
1667 (*received_data) = data;
1668 } else {
1669 if (data != NULL) {
1670 free(data);
1671 data = NULL;
1672 }
1673 }
1674 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001675}
1676
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001677static char *
1678netconf_getconfig(unsigned int session_key, NC_DATASTORE source, const char *filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001679{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001680 nc_rpc* rpc;
1681 struct nc_filter *f = NULL;
1682 struct session_with_mutex *locked_session;
1683 char* data = NULL, *data_xml;
1684 json_object *res = NULL, *data_cjson;
1685 enum json_tokener_error tok_err;
1686 struct lyd_node *node, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001687
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001688 /* create filter if set */
1689 if (filter != NULL) {
1690 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
1691 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001692
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001693 /* create requests */
1694 rpc = nc_rpc_getconfig(source, f);
1695 nc_filter_free(f);
1696 if (rpc == NULL) {
1697 ERROR("mod_netconf: creating rpc request failed");
1698 return (NULL);
1699 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001700
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001701 /* tell server to show all elements even if they have default values */
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001702#ifdef HAVE_WITHDEFAULTS_TAGGED
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001703 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001704#else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001705 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET))
1706 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001707#endif
Michal Vaskoc3146782015-11-04 14:46:41 +01001708 {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001709 ERROR("mod_netconf: setting withdefaults failed");
1710 }
Tomas Cejka94674662013-09-13 15:55:24 +02001711
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001712 res = netconf_op(session_key, rpc, &data);
1713 nc_rpc_free(rpc);
1714 if (res != NULL) {
1715 (*err) = res;
1716 } else {
1717 (*err) = NULL;
1718 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001719
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001720 if (data) {
1721 for (locked_session = netconf_sessions_list;
1722 locked_session && (locked_session->session_key != session_key);
1723 locked_session = locked_session->next);
1724 /* won't fail */
1725
1726 asprintf(&data_xml, "<get-config>%s</get-config>", data);
1727 node = lyd_parse(locked_session->ctx, data_xml, LYD_XML, LYD_OPT_GETCONFIG | (strict ? LYD_OPT_STRICT : 0));
1728 free(data_xml);
1729 free(data);
1730 if (!node) {
1731 ERROR("Parsing <get-config> data failed.");
1732 return NULL;
1733 }
1734
1735 /* replace XML data with JSON data */
1736 if (lyd_print_mem(&data, node, LYD_JSON)) {
1737 ERROR("Printing JSON <get-config> data failed.");
1738 LY_TREE_FOR(node, sibling) {
1739 lyd_free(sibling);
1740 }
1741 return NULL;
1742 }
1743
1744 /* parse JSON data into cjson */
1745 pthread_mutex_lock(&json_lock);
1746 data_cjson = json_tokener_parse_verbose(data, &tok_err);
1747 if (!data_cjson) {
1748 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1749 pthread_mutex_unlock(&json_lock);
1750 LY_TREE_FOR(node, sibling) {
1751 lyd_free(sibling);
1752 }
1753 free(data);
1754 return NULL;
1755 }
1756 free(data);
1757
1758 /* go simultaneously through both trees and add metadata */
1759 LY_TREE_FOR_SAFE(node, next, sibling) {
1760 node_add_metadata_recursive(sibling, NULL, data_cjson);
1761 lyd_free(sibling);
1762 }
1763
1764 data = strdup(json_object_to_json_string_ext(data_cjson, 0));
1765 json_object_put(data_cjson);
1766 pthread_mutex_unlock(&json_lock);
1767 }
1768
1769 return (data);
Radek Krejci8e4632a2012-07-26 13:40:34 +02001770}
1771
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001772static char *
1773netconf_getschema(unsigned int session_key, const char *identifier, const char *version, const char *format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001774{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001775 nc_rpc* rpc;
1776 char* data = NULL;
1777 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001778
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001779 /* create requests */
1780 rpc = nc_rpc_getschema(identifier, version, format);
1781 if (rpc == NULL) {
1782 ERROR("mod_netconf: creating rpc request failed");
1783 return (NULL);
1784 }
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001785
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001786 res = netconf_op(session_key, rpc, &data);
1787 nc_rpc_free (rpc);
1788 if (res != NULL) {
1789 (*err) = res;
1790 } else {
1791 (*err) = NULL;
1792 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001793
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001794 return (data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001795}
1796
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001797static char *
1798netconf_get(unsigned int session_key, const char* filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001799{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001800 nc_rpc* rpc;
1801 struct nc_filter *f = NULL;
1802 char* data = NULL, *data_xml;
1803 json_object *res = NULL, *data_cjson;
1804 enum json_tokener_error tok_err;
1805 struct session_with_mutex *locked_session;
1806 struct lyd_node *node, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001807
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001808 /* create filter if set */
1809 if (filter != NULL) {
1810 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
1811 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001812
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001813 /* create requests */
1814 rpc = nc_rpc_get(f);
1815 nc_filter_free(f);
1816 if (rpc == NULL) {
1817 ERROR("mod_netconf: creating rpc request failed");
1818 return (NULL);
1819 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001820
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001821 /* tell server to show all elements even if they have default values */
1822 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
1823 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
1824 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
1825 ERROR("mod_netconf: setting withdefaults failed");
1826 }
Tomas Cejka94674662013-09-13 15:55:24 +02001827
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001828 res = netconf_op(session_key, rpc, &data);
1829 nc_rpc_free(rpc);
1830 if (res != NULL) {
1831 (*err) = res;
1832 } else {
1833 (*err) = NULL;
1834 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001835
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001836 if (data) {
1837 for (locked_session = netconf_sessions_list;
1838 locked_session && (locked_session->session_key != session_key);
1839 locked_session = locked_session->next);
1840 /* won't fail */
1841
1842 asprintf(&data_xml, "<get>%s</get>", data);
1843 node = lyd_parse(locked_session->ctx, data_xml, LYD_XML, LYD_OPT_GET | (strict ? LYD_OPT_STRICT : 0));
1844 free(data_xml);
1845 free(data);
1846 if (!node) {
1847 ERROR("Parsing <get> data failed.");
1848 return NULL;
1849 }
1850
1851 /* replace XML data with JSON data */
1852 if (lyd_print_mem(&data, node, LYD_JSON)) {
1853 ERROR("Printing JSON <get> data failed.");
1854 LY_TREE_FOR(node, sibling) {
1855 lyd_free(sibling);
1856 }
1857 return NULL;
1858 }
1859
1860 /* parse JSON data into cjson */
1861 pthread_mutex_lock(&json_lock);
1862 data_cjson = json_tokener_parse_verbose(data, &tok_err);
1863 if (!data_cjson) {
1864 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1865 pthread_mutex_unlock(&json_lock);
1866 LY_TREE_FOR(node, sibling) {
1867 lyd_free(sibling);
1868 }
1869 free(data);
1870 return NULL;
1871 }
1872 free(data);
1873
1874 /* go simultaneously through both trees and add metadata */
1875 LY_TREE_FOR_SAFE(node, next, sibling) {
1876 node_add_metadata_recursive(sibling, NULL, data_cjson);
1877 lyd_free(sibling);
1878 }
1879
1880 data = strdup(json_object_to_json_string_ext(data_cjson, 0));
1881 json_object_put(data_cjson);
1882 pthread_mutex_unlock(&json_lock);
1883 }
1884
1885 return data;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001886}
1887
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001888static json_object *
1889netconf_copyconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, const char *config,
1890 const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001891{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001892 nc_rpc* rpc;
1893 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001894
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001895 /* create requests */
1896 if (source == NC_DATASTORE_CONFIG) {
1897 if (target == NC_DATASTORE_URL) {
1898 /* config, url */
1899 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
1900 } else {
1901 /* config, datastore */
1902 rpc = nc_rpc_copyconfig(source, target, config);
1903 }
1904 } else if (source == NC_DATASTORE_URL) {
1905 if (target == NC_DATASTORE_URL) {
1906 /* url, url */
1907 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
1908 } else {
1909 /* url, datastore */
1910 rpc = nc_rpc_copyconfig(source, target, uri_src);
1911 }
1912 } else {
1913 if (target == NC_DATASTORE_URL) {
1914 /* datastore, url */
1915 rpc = nc_rpc_copyconfig(source, target, uri_trg);
1916 } else {
1917 /* datastore, datastore */
1918 rpc = nc_rpc_copyconfig(source, target);
1919 }
1920 }
1921 if (rpc == NULL) {
1922 ERROR("mod_netconf: creating rpc request failed");
1923 return create_error_reply("Internal: Creating rpc request failed");
1924 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001925
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001926 res = netconf_op(session_key, rpc, NULL);
1927 nc_rpc_free(rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001928
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001929 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001930}
Radek Krejci035bf4e2012-07-25 10:59:09 +02001931
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001932static json_object *
1933netconf_editconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, NC_EDIT_DEFOP_TYPE defop,
1934 NC_EDIT_ERROPT_TYPE erropt, NC_EDIT_TESTOPT_TYPE testopt, const char *config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +02001935{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001936 nc_rpc* rpc;
1937 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001938
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001939 /* create requests */
1940 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
1941 if (rpc == NULL) {
1942 ERROR("mod_netconf: creating rpc request failed");
1943 return create_error_reply("Internal: Creating rpc request failed");
1944 }
Radek Krejci62ab34b2012-07-26 13:42:05 +02001945
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001946 res = netconf_op(session_key, rpc, NULL);
1947 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001948
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001949 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001950}
1951
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001952static json_object *
1953netconf_killsession(unsigned int session_key, const char *sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001954{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001955 nc_rpc *rpc;
1956 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001957
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001958 /* create requests */
1959 rpc = nc_rpc_killsession(sid);
1960 if (rpc == NULL) {
1961 ERROR("mod_netconf: creating rpc request failed");
1962 return create_error_reply("Internal: Creating rpc request failed");
1963 }
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001964
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001965 res = netconf_op(session_key, rpc, NULL);
1966 nc_rpc_free(rpc);
1967 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001968}
1969
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001970static json_object *
1971netconf_onlytargetop(unsigned int session_key, NC_DATASTORE target, nc_rpc *(*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +02001972{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001973 nc_rpc* rpc;
1974 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +02001975
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001976 /* create requests */
1977 rpc = op_func(target);
1978 if (rpc == NULL) {
1979 ERROR("mod_netconf: creating rpc request failed");
1980 return create_error_reply("Internal: Creating rpc request failed");
1981 }
Radek Krejci2f318372012-07-26 14:22:35 +02001982
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001983 res = netconf_op(session_key, rpc, NULL);
1984 nc_rpc_free (rpc);
1985 return res;
Radek Krejci2f318372012-07-26 14:22:35 +02001986}
1987
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001988static json_object *
1989netconf_deleteconfig(unsigned int session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001990{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001991 nc_rpc *rpc = NULL;
1992 json_object *res = NULL;
1993 if (target != NC_DATASTORE_URL) {
1994 rpc = nc_rpc_deleteconfig(target);
1995 } else {
1996 rpc = nc_rpc_deleteconfig(target, url);
1997 }
1998 if (rpc == NULL) {
1999 ERROR("mod_netconf: creating rpc request failed");
2000 return create_error_reply("Internal: Creating rpc request failed");
2001 }
Tomas Cejka404d37e2013-04-13 02:31:35 +02002002
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002003 res = netconf_op(session_key, rpc, NULL);
2004 nc_rpc_free (rpc);
2005 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +02002006}
2007
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002008static json_object *
2009netconf_lock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02002010{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002011 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02002012}
2013
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002014static json_object *
2015netconf_unlock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02002016{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002017 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02002018}
2019
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002020static json_object *
2021netconf_generic(unsigned int session_key, const char *content, char **data)
Radek Krejci80c10d92012-07-30 08:38:50 +02002022{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002023 nc_rpc* rpc = NULL;
2024 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +02002025
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002026 /* create requests */
2027 rpc = nc_rpc_generic(content);
2028 if (rpc == NULL) {
2029 ERROR("mod_netconf: creating rpc request failed");
2030 return create_error_reply("Internal: Creating rpc request failed");
2031 }
Radek Krejci80c10d92012-07-30 08:38:50 +02002032
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002033 if (data != NULL) {
2034 // TODO ?free(*data);
2035 (*data) = NULL;
2036 }
Radek Krejci80c10d92012-07-30 08:38:50 +02002037
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002038 /* get session where send the RPC */
2039 res = netconf_op(session_key, rpc, data);
2040 nc_rpc_free (rpc);
2041 return res;
2042}
2043
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002044static int
2045node_add_metadata(struct lys_node *node, struct lys_module *module, json_object *parent)
2046{
2047 struct lys_module *cur_module;
2048 json_object *meta_obj;
2049 char *obj_name;
2050
Michal Vaskoa45770b2015-11-23 15:49:41 +01002051 if (node->nodetype == LYS_INPUT) {
2052 /* silently skipped */
2053 return 0;
2054 }
2055
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002056 cur_module = node->module;
2057 if (cur_module->type) {
2058 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
2059 }
2060 if (cur_module == module) {
2061 asprintf(&obj_name, "$@%s", node->name);
2062 } else {
2063 asprintf(&obj_name, "$@%s:%s", cur_module->name, node->name);
2064 }
2065
2066 /* in (leaf-)lists the metadata could have already been added */
Michal Vaskoa45770b2015-11-23 15:49:41 +01002067 if ((node->nodetype & (LYS_LEAFLIST | LYS_LIST)) && (json_object_object_get_ex(parent, obj_name, NULL) == TRUE)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002068 free(obj_name);
2069 return 1;
2070 }
2071
2072 meta_obj = json_object_new_object();
2073
2074 switch (node->nodetype) {
2075 case LYS_CONTAINER:
2076 node_metadata_container((struct lys_node_container *)node, meta_obj);
2077 break;
Michal Vasko3fda9a92015-11-23 10:10:57 +01002078 case LYS_CHOICE:
2079 node_metadata_choice((struct lys_node_choice *)node, meta_obj);
2080 break;
2081 case LYS_LEAF:
2082 node_metadata_leaf((struct lys_node_leaf *)node, meta_obj);
2083 break;
2084 case LYS_LEAFLIST:
2085 node_metadata_leaflist((struct lys_node_leaflist *)node, meta_obj);
2086 break;
2087 case LYS_LIST:
2088 node_metadata_list((struct lys_node_list *)node, meta_obj);
2089 break;
2090 case LYS_ANYXML:
2091 node_metadata_anyxml((struct lys_node_anyxml *)node, meta_obj);
2092 break;
2093 case LYS_CASE:
2094 node_metadata_case((struct lys_node_case *)node, meta_obj);
2095 break;
Michal Vaskoa45770b2015-11-23 15:49:41 +01002096 case LYS_RPC:
2097 node_metadata_rpc((struct lys_node_rpc *)node, meta_obj);
2098 break;
2099 default: /* LYS_OUTPUT */
Michal Vasko3fda9a92015-11-23 10:10:57 +01002100 ERROR("Internal: unuxpected nodetype (%s:%d)", __FILE__, __LINE__);
2101 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002102 }
2103
2104 /* just a precaution */
2105 if (json_object_get_type(parent) != json_type_object) {
2106 ERROR("Internal: wrong JSON type (%s:%d)", __FILE__, __LINE__);
2107 free(obj_name);
2108 return 1;
2109 }
2110
2111 json_object_object_add(parent, obj_name, meta_obj);
2112 free(obj_name);
2113 return 0;
2114}
2115
2116static void
2117node_add_metadata_recursive(struct lyd_node *data_tree, struct lys_module *module, json_object *data_json_parent)
2118{
2119 struct lys_module *cur_module;
2120 struct lys_node *list_schema;
2121 struct lyd_node *child, *list_item;
2122 json_object *child_json, *list_child_json;
2123 char *child_name;
2124 int list_idx;
2125
Michal Vaskoa45770b2015-11-23 15:49:41 +01002126 if (data_tree->schema->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
2127 return;
2128 }
2129
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002130 /* add data_tree metadata */
2131 if (node_add_metadata(data_tree->schema, module, data_json_parent)) {
2132 return;
2133 }
2134
2135 /* get data_tree module */
2136 cur_module = data_tree->schema->module;
2137 if (cur_module->type) {
2138 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
2139 }
2140
2141 if (!(data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
2142 /* print correct data_tree JSON name */
2143 if (cur_module == module) {
2144 asprintf(&child_name, "%s", data_tree->schema->name);
2145 } else {
2146 asprintf(&child_name, "%s:%s", cur_module->name, data_tree->schema->name);
2147 }
2148
2149 /* go down in JSON object */
2150 if (json_object_object_get_ex(data_json_parent, child_name, &child_json) == FALSE) {
2151 ERROR("Internal: failed to get JSON object \"%s\".", child_name);
2152 free(child_name);
2153 return;
2154 }
2155 free(child_name);
2156
2157 if (data_tree->schema->nodetype == LYS_LIST) {
2158 if (json_object_get_type(child_json) != json_type_array) {
2159 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
2160 return;
2161 }
2162 /* go down in data tree for every item, we process them all now, skip later
2163 * (metadata duplicate will be detected at the beginning of this function) */
2164 list_idx = 0;
2165 list_schema = data_tree->schema;
2166
2167 LY_TREE_FOR(data_tree, list_item) {
2168 /* another list member */
2169 if (list_item->schema == list_schema) {
2170 list_child_json = json_object_array_get_idx(child_json, list_idx);
2171 if (!list_child_json) {
2172 ERROR("Internal: list \"%s\" idx out-of-bounds", list_schema->name);
2173 return;
2174 }
2175 LY_TREE_FOR(list_item->child, child) {
2176 node_add_metadata_recursive(child, cur_module, list_child_json);
2177 }
2178
2179 ++list_idx;
2180 }
2181 }
2182 } else {
2183 if (json_object_get_type(child_json) != json_type_object) {
2184 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
2185 return;
2186 }
2187 /* go down in data tree */
2188 LY_TREE_FOR(data_tree->child, child) {
2189 node_add_metadata_recursive(child, cur_module, child_json);
2190 }
2191 }
2192 }
2193}
2194
2195static void
Michal Vaskoa45770b2015-11-23 15:49:41 +01002196node_add_model_metadata(struct lys_module *module, json_object *parent)
2197{
2198 json_object *obj;
2199 char *str;
2200
2201 obj = json_object_new_object();
2202 node_metadata_model(module, obj);
2203 asprintf(&str, "$@@%s", module->name);
2204 json_object_object_add(parent, str, obj);
2205 free(str);
2206}
2207
2208static void
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002209node_add_children_with_metadata_recursive(struct lys_node *node, struct lys_module *module, json_object *parent)
2210{
2211 struct lys_module *cur_module;
2212 struct lys_node *child;
2213 json_object *node_json;
2214 char *json_name;
2215
Michal Vaskoa45770b2015-11-23 15:49:41 +01002216 if (node->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
2217 return;
2218 }
2219
2220 if (node->nodetype & LYS_USES) {
2221 cur_module = module;
2222 node_json = parent;
2223 goto children;
2224 }
2225
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002226 /* add node metadata */
2227 if (node_add_metadata(node, module, parent)) {
2228 ERROR("Internal: metadata duplicate for \"%s\".", node->name);
2229 return;
2230 }
2231
Michal Vaskoa45770b2015-11-23 15:49:41 +01002232 /* no other metadata */
2233 if (!node->child) {
2234 return;
2235 }
2236
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002237 /* get node module */
2238 cur_module = node->module;
2239 if (cur_module->type) {
2240 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
2241 }
2242
2243 /* create JSON object for child metadata */
2244 node_json = json_object_new_object();
2245 if (cur_module == module) {
2246 json_object_object_add(parent, node->name, node_json);
2247 } else {
2248 asprintf(&json_name, "%s:%s", cur_module->name, node->name);
2249 json_object_object_add(parent, json_name, node_json);
2250 free(json_name);
2251 }
2252
Michal Vaskoa45770b2015-11-23 15:49:41 +01002253children:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002254 LY_TREE_FOR(node->child, child) {
2255 node_add_children_with_metadata_recursive(child, cur_module, node_json);
2256 }
2257}
2258
2259static json_object *
2260libyang_query(unsigned int session_key, const char *filter, int load_children)
2261{
2262 struct lys_node *node;
Michal Vaskoa45770b2015-11-23 15:49:41 +01002263 struct lys_module *module = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002264 struct session_with_mutex *locked_session;
2265 json_object *ret = NULL, *data;
2266
2267 locked_session = session_get_locked(session_key, &ret);
2268 if (!locked_session) {
2269 ERROR("Locking failed or session not found.");
2270 goto finish;
2271 }
2272
Michal Vaskoe32bcba2015-11-24 09:05:51 +01002273 session_user_activity(nc_session_get_user(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002274
Michal Vaskoa45770b2015-11-23 15:49:41 +01002275 if (filter[0] == '/') {
2276 node = ly_ctx_get_node(locked_session->ctx, filter);
2277 if (!node) {
2278 ret = create_error_reply("Failed to resolve XPath filter node.");
2279 goto finish;
2280 }
2281 } else {
2282 module = ly_ctx_get_module(locked_session->ctx, filter, NULL);
2283 if (!module) {
2284 ret = create_error_reply("Failed to find model.");
2285 goto finish;
2286 }
2287 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002288
Michal Vaskoa45770b2015-11-23 15:49:41 +01002289 pthread_mutex_lock(&json_lock);
2290 data = json_object_new_object();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002291
Michal Vaskoa45770b2015-11-23 15:49:41 +01002292 if (module) {
2293 node_add_model_metadata(module, data);
2294 if (load_children) {
2295 LY_TREE_FOR(module->data, node) {
2296 node_add_children_with_metadata_recursive(node, NULL, data);
2297 }
2298 }
2299 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002300 if (load_children) {
2301 node_add_children_with_metadata_recursive(node, NULL, data);
2302 } else {
2303 node_add_metadata(node, NULL, data);
2304 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002305 }
2306
Michal Vaskoa45770b2015-11-23 15:49:41 +01002307 pthread_mutex_unlock(&json_lock);
2308 ret = create_data_reply(json_object_to_json_string(data));
2309 json_object_put(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002310
2311finish:
Michal Vaskoa45770b2015-11-23 15:49:41 +01002312 session_unlock(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002313 return ret;
2314}
2315
2316static json_object *
2317libyang_merge(unsigned int session_key, const char *config)
2318{
2319 struct lyd_node *data_tree = NULL, *sibling;
2320 struct session_with_mutex *locked_session;
2321 json_object *ret = NULL, *data_json = NULL;
2322 enum json_tokener_error err = 0;
2323
2324 locked_session = session_get_locked(session_key, &ret);
2325 if (!locked_session) {
2326 ERROR("Locking failed or session not found.");
2327 goto finish;
2328 }
2329
Michal Vaskoe32bcba2015-11-24 09:05:51 +01002330 session_user_activity(nc_session_get_user(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002331
2332 data_tree = lyd_parse(locked_session->ctx, config, LYD_JSON, LYD_OPT_STRICT);
2333 if (!data_tree) {
2334 ERROR("Creating data tree failed.");
2335 ret = create_error_reply("Failed to create data tree from JSON config.");
2336 session_unlock(locked_session);
2337 goto finish;
2338 }
2339
2340 session_unlock(locked_session);
2341
2342 pthread_mutex_lock(&json_lock);
2343 data_json = json_tokener_parse_verbose(config, &err);
2344 if (!data_json) {
2345 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(err));
2346 pthread_mutex_unlock(&json_lock);
2347 ret = create_error_reply(json_tokener_error_desc(err));
2348 goto finish;
2349 }
2350
2351 /* go simultaneously through both trees and add metadata */
2352 LY_TREE_FOR(data_tree, sibling) {
2353 node_add_metadata_recursive(sibling, NULL, data_json);
2354 }
2355 pthread_mutex_unlock(&json_lock);
2356 ret = create_data_reply(json_object_to_json_string(data_json));
2357
2358finish:
2359 LY_TREE_FOR(data_tree, sibling) {
2360 lyd_free(sibling);
2361 }
2362 json_object_put(data_json);
2363 return ret;
Radek Krejci80c10d92012-07-30 08:38:50 +02002364}
2365
Tomas Cejka0a4bba82013-04-19 11:51:28 +02002366/**
2367 * @}
2368 *//* netconf_operations */
2369
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002370void
2371clb_print(NC_VERB_LEVEL level, const char *msg)
Radek Krejci469aab82012-07-22 18:42:20 +02002372{
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002373#define FOREACH(I) \
Tomas Cejkacf44e522015-04-24 17:29:21 +02002374 I(NC_VERB_ERROR) I(NC_VERB_WARNING)
2375
2376#define CASE(VAL) case VAL: ERROR("%s: %s", #VAL, msg); \
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002377 break;
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002378
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002379 switch (level) {
2380 FOREACH(CASE);
2381 case NC_VERB_VERBOSE:
2382 case NC_VERB_DEBUG:
2383 DEBUG("DEBUG: %s", msg);
2384 break;
2385 }
2386 if (level == NC_VERB_ERROR) {
2387 /* return global error */
2388 netconf_callback_error_process(NULL /* tag */, NULL /* type */,
2389 NULL /* severity */, NULL /* apptag */,
2390 NULL /* path */, msg, NULL /* attribute */,
2391 NULL /* element */, NULL /* ns */, NULL /* sid */);
2392 }
Radek Krejci469aab82012-07-22 18:42:20 +02002393}
2394
Tomas Cejka64b87482013-06-03 16:30:53 +02002395/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02002396 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02002397 * Caller should free message memory.
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002398 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02002399 * \return pointer to message
2400 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002401char *
2402get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02002403{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002404 /* read json in chunked framing */
2405 unsigned int buffer_size = 0;
2406 ssize_t buffer_len = 0;
2407 char *buffer = NULL;
2408 char c;
2409 ssize_t ret;
2410 int i, chunk_len;
2411 char chunk_len_str[12];
Tomas Cejka64b87482013-06-03 16:30:53 +02002412
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002413 while (1) {
2414 /* read chunk length */
2415 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
2416 if (buffer != NULL) {
2417 free (buffer);
2418 buffer = NULL;
2419 }
2420 break;
2421 }
2422 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
2423 if (buffer != NULL) {
2424 free (buffer);
2425 buffer = NULL;
2426 }
2427 break;
2428 }
2429 i=0;
2430 memset (chunk_len_str, 0, 12);
2431 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
2432 if (i==0 && c == '#') {
2433 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
2434 /* end but invalid */
2435 if (buffer != NULL) {
2436 free (buffer);
2437 buffer = NULL;
2438 }
2439 }
2440 /* end of message, double-loop break */
2441 goto msg_complete;
2442 }
2443 chunk_len_str[i++] = c;
2444 if (i==11) {
2445 ERROR("Message is too long, buffer for length is not big enought!!!!");
2446 break;
2447 }
2448 }
2449 if (c != '\n') {
2450 if (buffer != NULL) {
2451 free (buffer);
2452 buffer = NULL;
2453 }
2454 break;
2455 }
2456 chunk_len_str[i] = 0;
2457 if ((chunk_len = atoi (chunk_len_str)) == 0) {
2458 if (buffer != NULL) {
2459 free (buffer);
2460 buffer = NULL;
2461 }
2462 break;
2463 }
2464 buffer_size += chunk_len+1;
2465 buffer = realloc (buffer, sizeof(char)*buffer_size);
2466 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
2467 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
2468 if (buffer != NULL) {
2469 free (buffer);
2470 buffer = NULL;
2471 }
2472 break;
2473 }
2474 buffer_len += ret;
2475 }
Tomas Cejka64b87482013-06-03 16:30:53 +02002476msg_complete:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002477 return buffer;
Tomas Cejka64b87482013-06-03 16:30:53 +02002478}
2479
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002480NC_DATASTORE
2481parse_datastore(const char *ds)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002482{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002483 if (strcmp(ds, "running") == 0) {
2484 return NC_DATASTORE_RUNNING;
2485 } else if (strcmp(ds, "startup") == 0) {
2486 return NC_DATASTORE_STARTUP;
2487 } else if (strcmp(ds, "candidate") == 0) {
2488 return NC_DATASTORE_CANDIDATE;
2489 } else if (strcmp(ds, "url") == 0) {
2490 return NC_DATASTORE_URL;
2491 } else if (strcmp(ds, "config") == 0) {
2492 return NC_DATASTORE_CONFIG;
2493 }
2494 return -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002495}
2496
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002497NC_EDIT_TESTOPT_TYPE
2498parse_testopt(const char *t)
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002499{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002500 if (strcmp(t, "notset") == 0) {
2501 return NC_EDIT_TESTOPT_NOTSET;
2502 } else if (strcmp(t, "testset") == 0) {
2503 return NC_EDIT_TESTOPT_TESTSET;
2504 } else if (strcmp(t, "set") == 0) {
2505 return NC_EDIT_TESTOPT_SET;
2506 } else if (strcmp(t, "test") == 0) {
2507 return NC_EDIT_TESTOPT_TEST;
2508 }
2509 return NC_EDIT_TESTOPT_ERROR;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002510}
2511
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002512json_object *
2513create_error_reply(const char *errmess)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002514{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002515 json_object *reply, *array;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002516
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002517 pthread_mutex_lock(&json_lock);
2518 reply = json_object_new_object();
2519 array = json_object_new_array();
2520 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2521 json_object_array_add(array, json_object_new_string(errmess));
2522 json_object_object_add(reply, "errors", array);
2523 pthread_mutex_unlock(&json_lock);
2524
2525 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002526}
2527
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002528json_object *
2529create_data_reply(const char *data)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002530{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002531 pthread_mutex_lock(&json_lock);
2532 json_object *reply = json_object_new_object();
2533 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
2534 json_object_object_add(reply, "data", json_object_new_string(data));
2535 pthread_mutex_unlock(&json_lock);
2536 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002537}
2538
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002539json_object *
2540create_ok_reply(void)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002541{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002542 pthread_mutex_lock(&json_lock);
2543 json_object *reply = json_object_new_object();
2544 reply = json_object_new_object();
2545 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2546 pthread_mutex_unlock(&json_lock);
2547 return reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002548}
2549
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002550json_object *
2551create_replies(void)
Tomas Cejka09629492014-07-10 15:58:06 +02002552{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002553 json_object *replies;
2554
2555 pthread_mutex_lock(&json_lock);
2556 replies = json_object_new_object();
2557 pthread_mutex_unlock(&json_lock);
2558
2559 return replies;
Tomas Cejka09629492014-07-10 15:58:06 +02002560}
2561
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002562void
2563add_reply(json_object *replies, json_object *reply, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002564{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002565 char *str;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002566
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002567 asprintf(&str, "%u", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002568
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002569 pthread_mutex_lock(&json_lock);
2570 json_object_object_add(replies, str, reply);
2571 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002572
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002573 free(str);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002574}
2575
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002576char *
2577get_param_string(json_object *data, const char *name)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002578{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002579 json_object *js_tmp = NULL;
2580 char *res = NULL;
2581 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
2582 res = strdup(json_object_get_string(js_tmp));
2583 }
2584 return res;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002585}
2586
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002587json_object *
2588handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002589{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002590 char *host = NULL;
2591 char *port = NULL;
2592 char *user = NULL;
2593 char *pass = NULL;
2594 json_object *reply = NULL;
2595 unsigned int session_key = 0;
2596 struct nc_cpblts* cpblts = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002597
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002598 DEBUG("Request: connect");
2599 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002600
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002601 host = get_param_string(request, "host");
2602 port = get_param_string(request, "port");
2603 user = get_param_string(request, "user");
2604 pass = get_param_string(request, "pass");
Tomas Cejkad5b53772013-06-08 23:01:07 +02002605
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002606 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002607
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002608 DEBUG("host: %s, port: %s, user: %s", host, port, user);
2609 if ((host == NULL) || (user == NULL)) {
2610 ERROR("Cannot connect - insufficient input.");
2611 session_key = 0;
2612 } else {
2613 session_key = netconf_connect(host, port, user, pass, cpblts);
2614 DEBUG("Session key: %u", session_key);
2615 }
2616 if (cpblts != NULL) {
2617 nc_cpblts_free(cpblts);
2618 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002619
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002620 GETSPEC_ERR_REPLY
2621
2622 pthread_mutex_lock(&json_lock);
2623 if (session_key == 0) {
2624 /* negative reply */
2625 if (err_reply == NULL) {
2626 reply = json_object_new_object();
2627 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2628 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
2629 ERROR("Connection failed.");
2630 } else {
2631 /* use filled err_reply from libnetconf's callback */
2632 reply = err_reply;
2633 ERROR("Connect - error from libnetconf's callback.");
2634 }
2635 } else {
2636 /* positive reply */
2637 reply = json_object_new_object();
2638 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2639 json_object_object_add(reply, "session", json_object_new_int(session_key));
2640 }
2641 memset(pass, 0, strlen(pass));
2642 pthread_mutex_unlock(&json_lock);
2643 CHECK_AND_FREE(host);
2644 CHECK_AND_FREE(user);
2645 CHECK_AND_FREE(port);
2646 CHECK_AND_FREE(pass);
2647 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002648}
2649
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002650json_object *
2651handle_op_disconnect(json_object *UNUSED(request), unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002652{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002653 json_object *reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002654
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002655 DEBUG("Request: disconnect (session %u)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002656
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002657 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
2658 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
2659 } else {
2660 reply = create_ok_reply();
2661 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002662
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002663 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002664}
2665
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002666json_object *
2667handle_op_get(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002668{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002669 char *filter = NULL;
2670 char *data = NULL;
2671 json_object *reply = NULL, *obj;
2672 int strict;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002673
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002674 DEBUG("Request: get (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002675
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002676 pthread_mutex_lock(&json_lock);
2677 filter = get_param_string(request, "filter");
2678 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2679 pthread_mutex_unlock(&json_lock);
2680 reply = create_error_reply("Missing strict parameter.");
2681 return reply;
2682 }
2683 strict = json_object_get_boolean(obj);
2684 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002685
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002686 if ((data = netconf_get(session_key, filter, strict, &reply)) == NULL) {
2687 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
2688 } else {
2689 reply = create_data_reply(data);
2690 free(data);
2691 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002692
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002693 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002694}
2695
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002696json_object *
2697handle_op_getconfig(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002698{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002699 NC_DATASTORE ds_type_s = -1;
2700 char *filter = NULL;
2701 char *data = NULL;
2702 char *source = NULL;
2703 json_object *reply = NULL, *obj;
2704 int strict;
Tomas Cejkab4d05872014-02-14 22:44:38 +01002705
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002706 DEBUG("Request: get-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002707
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002708 pthread_mutex_lock(&json_lock);
2709 filter = get_param_string(request, "filter");
2710 source = get_param_string(request, "source");
2711 if (source != NULL) {
2712 ds_type_s = parse_datastore(source);
2713 }
2714 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2715 pthread_mutex_unlock(&json_lock);
2716 reply = create_error_reply("Missing strict parameter.");
2717 return reply;
2718 }
2719 strict = json_object_get_boolean(obj);
2720 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002721
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002722 if ((int)ds_type_s == -1) {
2723 reply = create_error_reply("Invalid source repository type requested.");
2724 goto finalize;
2725 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002726
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002727 if ((data = netconf_getconfig(session_key, ds_type_s, filter, strict, &reply)) == NULL) {
2728 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
2729 } else {
2730 reply = create_data_reply(data);
2731 free(data);
2732 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002733
Tomas Cejka09629492014-07-10 15:58:06 +02002734finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002735 CHECK_AND_FREE(filter);
2736 CHECK_AND_FREE(source);
2737 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002738}
2739
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002740json_object *
2741handle_op_editconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002742{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002743 NC_DATASTORE ds_type_s = -1;
2744 NC_DATASTORE ds_type_t = -1;
2745 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
2746 NC_EDIT_ERROPT_TYPE erropt_type = 0;
2747 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
2748 char *defop = NULL;
2749 char *erropt = NULL;
2750 char *config = NULL;
2751 char *source = NULL;
2752 char *target = NULL;
2753 char *testopt = NULL;
2754 char *urisource = NULL;
2755 json_object *reply = NULL, *configs, *obj;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002756
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002757 DEBUG("Request: edit-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002758
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002759 pthread_mutex_lock(&json_lock);
2760 /* get parameters */
2761 target = get_param_string(request, "target");
2762 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2763 pthread_mutex_unlock(&json_lock);
2764 reply = create_error_reply("Missing configs parameter.");
2765 goto finalize;
2766 }
2767 obj = json_object_array_get_idx(configs, idx);
2768 config = strdup(json_object_get_string(obj));
Tomas Cejkad5b53772013-06-08 23:01:07 +02002769
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002770 source = get_param_string(request, "source");
2771 defop = get_param_string(request, "default-operation");
2772 erropt = get_param_string(request, "error-option");
2773 urisource = get_param_string(request, "uri-source");
2774 testopt = get_param_string(request, "test-option");
2775 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002776
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002777 if (target != NULL) {
2778 ds_type_t = parse_datastore(target);
2779 }
2780 if (source != NULL) {
2781 ds_type_s = parse_datastore(source);
2782 } else {
2783 /* source is optional, default value is config */
2784 ds_type_s = NC_DATASTORE_CONFIG;
2785 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002786
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002787 if (defop != NULL) {
2788 if (strcmp(defop, "merge") == 0) {
2789 defop_type = NC_EDIT_DEFOP_MERGE;
2790 } else if (strcmp(defop, "replace") == 0) {
2791 defop_type = NC_EDIT_DEFOP_REPLACE;
2792 } else if (strcmp(defop, "none") == 0) {
2793 defop_type = NC_EDIT_DEFOP_NONE;
2794 } else {
2795 reply = create_error_reply("Invalid default-operation parameter.");
2796 goto finalize;
2797 }
2798 } else {
2799 defop_type = NC_EDIT_DEFOP_NOTSET;
2800 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002801
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002802 if (erropt != NULL) {
2803 if (strcmp(erropt, "continue-on-error") == 0) {
2804 erropt_type = NC_EDIT_ERROPT_CONT;
2805 } else if (strcmp(erropt, "stop-on-error") == 0) {
2806 erropt_type = NC_EDIT_ERROPT_STOP;
2807 } else if (strcmp(erropt, "rollback-on-error") == 0) {
2808 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
2809 } else {
2810 reply = create_error_reply("Invalid error-option parameter.");
2811 goto finalize;
2812 }
2813 } else {
2814 erropt_type = 0;
2815 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002816
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002817 if ((int)ds_type_t == -1) {
2818 reply = create_error_reply("Invalid target repository type requested.");
2819 goto finalize;
2820 }
2821 if (ds_type_s == NC_DATASTORE_CONFIG) {
2822 if (config == NULL) {
2823 reply = create_error_reply("Invalid config data parameter.");
2824 goto finalize;
2825 }
2826 } else if (ds_type_s == NC_DATASTORE_URL){
2827 if (urisource == NULL) {
2828 reply = create_error_reply("Invalid uri-source parameter.");
2829 goto finalize;
2830 }
2831 config = urisource;
2832 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002833
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002834 if (testopt != NULL) {
2835 testopt_type = parse_testopt(testopt);
2836 } else {
2837 testopt_type = NC_EDIT_TESTOPT_TESTSET;
2838 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002839
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002840 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 +02002841
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002842 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002843
Tomas Cejka09629492014-07-10 15:58:06 +02002844finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002845 CHECK_AND_FREE(defop);
2846 CHECK_AND_FREE(erropt);
2847 CHECK_AND_FREE(config);
2848 CHECK_AND_FREE(source);
2849 CHECK_AND_FREE(urisource);
2850 CHECK_AND_FREE(target);
2851 CHECK_AND_FREE(testopt);
2852
2853 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002854}
2855
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002856json_object *
2857handle_op_copyconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002858{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002859 NC_DATASTORE ds_type_s = -1;
2860 NC_DATASTORE ds_type_t = -1;
2861 char *config = NULL;
2862 char *target = NULL;
2863 char *source = NULL;
2864 char *uri_src = NULL;
2865 char *uri_trg = NULL;
2866 json_object *reply = NULL, *configs, *obj;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002867
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002868 DEBUG("Request: copy-config (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002869
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002870 /* get parameters */
2871 pthread_mutex_lock(&json_lock);
2872 target = get_param_string(request, "target");
2873 source = get_param_string(request, "source");
2874 uri_src = get_param_string(request, "uri-source");
2875 uri_trg = get_param_string(request, "uri-target");
2876 if (!strcmp(source, "config")) {
2877 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2878 pthread_mutex_unlock(&json_lock);
2879 reply = create_error_reply("Missing configs parameter.");
2880 goto finalize;
2881 }
2882 obj = json_object_array_get_idx(configs, idx);
2883 if (!obj) {
2884 pthread_mutex_unlock(&json_lock);
2885 reply = create_error_reply("Configs array parameter shorter than sessions.");
2886 goto finalize;
2887 }
2888 config = strdup(json_object_get_string(obj));
2889 }
2890 pthread_mutex_unlock(&json_lock);
2891
2892 if (target != NULL) {
2893 ds_type_t = parse_datastore(target);
2894 }
2895 if (source != NULL) {
2896 ds_type_s = parse_datastore(source);
2897 }
2898
2899 if ((int)ds_type_s == -1) {
2900 /* invalid source datastore specified */
2901 reply = create_error_reply("Invalid source repository type requested.");
2902 goto finalize;
2903 }
2904
2905 if ((int)ds_type_t == -1) {
2906 /* invalid target datastore specified */
2907 reply = create_error_reply("Invalid target repository type requested.");
2908 goto finalize;
2909 }
2910
2911 if (ds_type_s == NC_DATASTORE_URL) {
2912 if (uri_src == NULL) {
2913 uri_src = "";
2914 }
2915 }
2916 if (ds_type_t == NC_DATASTORE_URL) {
2917 if (uri_trg == NULL) {
2918 uri_trg = "";
2919 }
2920 }
2921 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
2922
2923 CHECK_ERR_SET_REPLY
2924
2925finalize:
2926 CHECK_AND_FREE(config);
2927 CHECK_AND_FREE(target);
2928 CHECK_AND_FREE(source);
2929 CHECK_AND_FREE(uri_src);
2930 CHECK_AND_FREE(uri_trg);
2931
2932 return reply;
2933}
2934
2935json_object *
2936handle_op_deleteconfig(json_object *request, unsigned int session_key)
2937{
2938 json_object *reply;
2939 NC_DATASTORE ds_type = -1;
2940 char *target, *url;
2941
2942 DEBUG("Request: delete-config (session %u)", session_key);
2943
2944 pthread_mutex_lock(&json_lock);
2945 target = get_param_string(request, "target");
2946 url = get_param_string(request, "url");
2947 pthread_mutex_unlock(&json_lock);
2948
2949 if (target != NULL) {
2950 ds_type = parse_datastore(target);
2951 }
2952 if ((int)ds_type == -1) {
2953 reply = create_error_reply("Invalid target repository type requested.");
2954 goto finalize;
2955 }
2956 if (ds_type == NC_DATASTORE_URL) {
2957 if (!url) {
2958 url = "";
2959 }
2960 }
2961
2962 reply = netconf_deleteconfig(session_key, ds_type, url);
2963
2964 CHECK_ERR_SET_REPLY
2965 if (reply == NULL) {
2966 reply = create_ok_reply();
2967 }
2968
2969finalize:
2970 CHECK_AND_FREE(target);
2971 CHECK_AND_FREE(url);
2972 return reply;
2973}
2974
2975json_object *
2976handle_op_lock(json_object *request, unsigned int session_key)
2977{
2978 json_object *reply;
2979 NC_DATASTORE ds_type = -1;
2980 char *target;
2981
2982 DEBUG("Request: lock (session %u)", session_key);
2983
2984 pthread_mutex_lock(&json_lock);
2985 target = get_param_string(request, "target");
2986 pthread_mutex_unlock(&json_lock);
2987
2988 if (target != NULL) {
2989 ds_type = parse_datastore(target);
2990 }
2991 if ((int)ds_type == -1) {
2992 reply = create_error_reply("Invalid target repository type requested.");
2993 goto finalize;
2994 }
2995
2996 reply = netconf_lock(session_key, ds_type);
2997
2998 CHECK_ERR_SET_REPLY
2999 if (reply == NULL) {
3000 reply = create_ok_reply();
3001 }
3002
3003finalize:
3004 CHECK_AND_FREE(target);
3005 return reply;
3006}
3007
3008json_object *
3009handle_op_unlock(json_object *request, unsigned int session_key)
3010{
3011 json_object *reply;
3012 NC_DATASTORE ds_type = -1;
3013 char *target;
3014
3015 DEBUG("Request: unlock (session %u)", session_key);
3016
3017 pthread_mutex_lock(&json_lock);
3018 target = get_param_string(request, "target");
3019 pthread_mutex_unlock(&json_lock);
3020
3021 if (target != NULL) {
3022 ds_type = parse_datastore(target);
3023 }
3024 if ((int)ds_type == -1) {
3025 reply = create_error_reply("Invalid target repository type requested.");
3026 goto finalize;
3027 }
3028
3029 reply = netconf_unlock(session_key, ds_type);
3030
3031 CHECK_ERR_SET_REPLY
3032 if (reply == NULL) {
3033 reply = create_ok_reply();
3034 }
3035
3036finalize:
3037 CHECK_AND_FREE(target);
3038 return reply;
3039}
3040
3041json_object *
3042handle_op_kill(json_object *request, unsigned int session_key)
3043{
3044 json_object *reply = NULL;
3045 char *sid = NULL;
3046
3047 DEBUG("Request: kill-session (session %u)", session_key);
3048
3049 pthread_mutex_lock(&json_lock);
3050 sid = get_param_string(request, "session-id");
3051 pthread_mutex_unlock(&json_lock);
3052
3053 if (sid == NULL) {
3054 reply = create_error_reply("Missing session-id parameter.");
3055 goto finalize;
3056 }
3057
3058 reply = netconf_killsession(session_key, sid);
3059
3060 CHECK_ERR_SET_REPLY
3061
3062finalize:
3063 CHECK_AND_FREE(sid);
3064 return reply;
3065}
3066
3067json_object *
3068handle_op_info(json_object *UNUSED(request), unsigned int session_key)
3069{
3070 json_object *reply = NULL;
3071 struct session_with_mutex *locked_session = NULL;
3072 DEBUG("Request: get info about session %u", session_key);
3073
3074 DEBUG("LOCK wrlock %s", __func__);
3075 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3076 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3077 }
3078
3079 for (locked_session = netconf_sessions_list;
3080 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003081 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003082 if (locked_session != NULL) {
3083 DEBUG("LOCK mutex %s", __func__);
3084 pthread_mutex_lock(&locked_session->lock);
3085 DEBUG("UNLOCK wrlock %s", __func__);
3086 if (pthread_rwlock_unlock(&session_lock) != 0) {
3087 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3088 }
3089 if (locked_session->hello_message != NULL) {
3090 reply = locked_session->hello_message;
3091 } else {
3092 reply = create_error_reply("Invalid session identifier.");
3093 }
3094 DEBUG("UNLOCK mutex %s", __func__);
3095 pthread_mutex_unlock(&locked_session->lock);
3096 } else {
3097 DEBUG("UNLOCK wrlock %s", __func__);
3098 if (pthread_rwlock_unlock(&session_lock) != 0) {
3099 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3100 }
3101 reply = create_error_reply("Invalid session identifier.");
3102 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02003103
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003104 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003105}
3106
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003107json_object *
3108handle_op_generic(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02003109{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003110 json_object *reply = NULL, *contents, *obj;
3111 char *config = NULL;
3112 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003113
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003114 DEBUG("Request: generic request (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02003115
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003116 pthread_mutex_lock(&json_lock);
3117 if (json_object_object_get_ex(request, "contents", &contents) == FALSE) {
3118 pthread_mutex_unlock(&json_lock);
3119 reply = create_error_reply("Missing contents parameter.");
3120 goto finalize;
3121 }
3122 obj = json_object_array_get_idx(contents, idx);
3123 if (!obj) {
3124 pthread_mutex_unlock(&json_lock);
3125 reply = create_error_reply("Contents array parameter shorter than sessions.");
3126 goto finalize;
3127 }
3128 config = strdup(json_object_get_string(obj));
3129 pthread_mutex_unlock(&json_lock);
3130
3131 reply = netconf_generic(session_key, config, &data);
3132 if (reply == NULL) {
3133 GETSPEC_ERR_REPLY
3134 if (err_reply != NULL) {
3135 /* use filled err_reply from libnetconf's callback */
3136 reply = err_reply;
3137 }
3138 } else {
3139 if (data == NULL) {
3140 pthread_mutex_lock(&json_lock);
3141 reply = json_object_new_object();
3142 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
3143 pthread_mutex_unlock(&json_lock);
3144 } else {
3145 reply = create_data_reply(data);
3146 free(data);
3147 }
3148 }
3149
3150finalize:
3151 CHECK_AND_FREE(config);
3152 return reply;
3153}
3154
3155json_object *
3156handle_op_getschema(json_object *request, unsigned int session_key)
3157{
3158 char *data = NULL;
3159 char *identifier = NULL;
3160 char *version = NULL;
3161 char *format = NULL;
3162 json_object *reply = NULL;
3163
3164 DEBUG("Request: get-schema (session %u)", session_key);
3165
3166 pthread_mutex_lock(&json_lock);
3167 identifier = get_param_string(request, "identifier");
3168 version = get_param_string(request, "version");
3169 format = get_param_string(request, "format");
3170 pthread_mutex_unlock(&json_lock);
3171
3172 if (identifier == NULL) {
3173 reply = create_error_reply("No identifier for get-schema supplied.");
3174 goto finalize;
3175 }
3176
3177 DEBUG("get-schema(version: %s, format: %s)", version, format);
3178 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
3179 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
3180 } else {
3181 reply = create_data_reply(data);
3182 free(data);
3183 }
3184
3185finalize:
3186 CHECK_AND_FREE(identifier);
3187 CHECK_AND_FREE(version);
3188 CHECK_AND_FREE(format);
3189 return reply;
3190}
3191
3192json_object *
3193handle_op_reloadhello(json_object *UNUSED(request), unsigned int session_key)
3194{
3195 struct nc_session *temp_session = NULL;
3196 struct session_with_mutex * locked_session = NULL;
3197 json_object *reply = NULL;
3198
3199 DEBUG("Request: reload hello (session %u)", session_key);
3200
3201 DEBUG("LOCK wrlock %s", __func__);
3202 if (pthread_rwlock_wrlock(&session_lock) != 0) {
3203 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3204 return NULL;
3205 }
3206
3207 for (locked_session = netconf_sessions_list;
3208 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003209 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003210 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
3211 DEBUG("LOCK mutex %s", __func__);
3212 pthread_mutex_lock(&locked_session->lock);
3213 DEBUG("creating temporary NC session.");
3214 temp_session = nc_session_connect_channel(locked_session->session, NULL);
3215 if (temp_session != NULL) {
3216 prepare_status_message(locked_session, temp_session);
3217 DEBUG("closing temporal NC session.");
3218 nc_session_free(temp_session);
3219 temp_session = NULL;
3220 } else {
3221 DEBUG("Reload hello failed due to channel establishment");
3222 reply = create_error_reply("Reload was unsuccessful, connection failed.");
3223 }
3224 DEBUG("UNLOCK mutex %s", __func__);
3225 pthread_mutex_unlock(&locked_session->lock);
3226 DEBUG("UNLOCK wrlock %s", __func__);
3227 if (pthread_rwlock_unlock(&session_lock) != 0) {
3228 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3229 }
3230 } else {
3231 DEBUG("UNLOCK wrlock %s", __func__);
3232 if (pthread_rwlock_unlock(&session_lock) != 0) {
3233 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3234 }
3235 reply = create_error_reply("Invalid session identifier.");
3236 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02003237
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003238 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
3239 reply = locked_session->hello_message;
3240 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02003241
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003242 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003243}
3244
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003245void
3246notification_history(time_t eventtime, const char *content)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003247{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003248 json_object *notif_history_array = (json_object *)pthread_getspecific(notif_history_key);
3249 if (notif_history_array == NULL) {
3250 ERROR("No list of notification history found.");
3251 return;
3252 }
3253 DEBUG("Got notification from history %lu.", (long unsigned)eventtime);
3254 pthread_mutex_lock(&json_lock);
3255 json_object *notif = json_object_new_object();
3256 if (notif == NULL) {
3257 ERROR("Could not allocate memory for notification (json).");
3258 goto failed;
3259 }
3260 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
3261 json_object_object_add(notif, "content", json_object_new_string(content));
3262 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003263failed:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003264 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003265}
3266
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003267json_object *
3268handle_op_ntfgethistory(json_object *request, unsigned int session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003269{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003270 json_object *reply = NULL;
3271 json_object *js_tmp = NULL;
3272 struct session_with_mutex *locked_session = NULL;
3273 struct nc_session *temp_session = NULL;
3274 nc_rpc *rpc = NULL;
3275 time_t start = 0;
3276 time_t stop = 0;
3277 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003278
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003279 DEBUG("Request: get notification history (session %u)", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003280
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003281 pthread_mutex_lock(&json_lock);
3282 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
3283 from = json_object_get_int64(js_tmp);
3284 }
3285 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
3286 to = json_object_get_int64(js_tmp);
3287 }
3288 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02003289
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003290 start = time(NULL) + from;
3291 stop = time(NULL) + to;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003292
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003293 DEBUG("notification history interval %li %li", (long int)from, (long int)to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003294
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003295 DEBUG("LOCK wrlock %s", __func__);
3296 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3297 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3298 reply = create_error_reply("Internal lock failed.");
3299 goto finalize;
3300 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003301
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003302 for (locked_session = netconf_sessions_list;
3303 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003304 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003305 if (locked_session != NULL) {
3306 DEBUG("LOCK mutex %s", __func__);
3307 pthread_mutex_lock(&locked_session->lock);
3308 DEBUG("UNLOCK wrlock %s", __func__);
3309 if (pthread_rwlock_unlock(&session_lock) != 0) {
3310 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3311 }
3312 DEBUG("creating temporal NC session.");
3313 temp_session = nc_session_connect_channel(locked_session->session, NULL);
3314 if (temp_session != NULL) {
3315 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
3316 if (rpc == NULL) {
3317 DEBUG("UNLOCK mutex %s", __func__);
3318 pthread_mutex_unlock(&locked_session->lock);
3319 DEBUG("notifications: creating an rpc request failed.");
3320 reply = create_error_reply("notifications: creating an rpc request failed.");
3321 goto finalize;
3322 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003323
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003324 DEBUG("Send NC subscribe.");
3325 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
3326 json_object *res = netconf_unlocked_op(temp_session, rpc);
3327 if (res != NULL) {
3328 DEBUG("UNLOCK mutex %s", __func__);
3329 pthread_mutex_unlock(&locked_session->lock);
3330 DEBUG("Subscription RPC failed.");
3331 reply = res;
3332 goto finalize;
3333 }
3334 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejka6b886e02013-07-05 09:53:17 +02003335
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003336 DEBUG("UNLOCK mutex %s", __func__);
3337 pthread_mutex_unlock(&locked_session->lock);
3338 DEBUG("LOCK mutex %s", __func__);
3339 pthread_mutex_lock(&ntf_history_lock);
3340 pthread_mutex_lock(&json_lock);
3341 json_object *notif_history_array = json_object_new_array();
3342 pthread_mutex_unlock(&json_lock);
3343 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
3344 ERROR("notif_history: cannot set thread-specific hash value.");
3345 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003346
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003347 ncntf_dispatch_receive(temp_session, notification_history);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003348
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003349 pthread_mutex_lock(&json_lock);
3350 reply = json_object_new_object();
3351 json_object_object_add(reply, "notifications", notif_history_array);
3352 //json_object_put(notif_history_array);
3353 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003354
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003355 DEBUG("UNLOCK mutex %s", __func__);
3356 pthread_mutex_unlock(&ntf_history_lock);
3357 DEBUG("closing temporal NC session.");
3358 nc_session_free(temp_session);
3359 temp_session = NULL;
3360 } else {
3361 DEBUG("UNLOCK mutex %s", __func__);
3362 pthread_mutex_unlock(&locked_session->lock);
3363 DEBUG("Get history of notification failed due to channel establishment");
3364 reply = create_error_reply("Get history of notification was unsuccessful, connection failed.");
3365 }
3366 } else {
3367 DEBUG("UNLOCK wrlock %s", __func__);
3368 if (pthread_rwlock_unlock(&session_lock) != 0) {
3369 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3370 }
3371 reply = create_error_reply("Invalid session identifier.");
3372 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003373
Tomas Cejka09629492014-07-10 15:58:06 +02003374finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003375 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02003376}
3377
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003378json_object *
3379handle_op_validate(json_object *request, unsigned int session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02003380{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003381 json_object *reply = NULL;
3382 char *target = NULL;
3383 char *url = NULL;
3384 nc_rpc *rpc = NULL;
3385 NC_DATASTORE target_ds;
Tomas Cejka4003a702013-10-01 00:02:45 +02003386
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003387 DEBUG("Request: validate datastore (session %u)", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02003388
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003389 pthread_mutex_lock(&json_lock);
3390 target = get_param_string(request, "target");
3391 url = get_param_string(request, "url");
3392 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02003393
3394
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003395 if (target == NULL) {
3396 reply = create_error_reply("Missing target parameter.");
3397 goto finalize;
3398 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003399
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003400 /* validation */
3401 target_ds = parse_datastore(target);
3402 if (target_ds == NC_DATASTORE_URL) {
3403 if (url != NULL) {
3404 rpc = nc_rpc_validate(target_ds, url);
3405 }
3406 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
3407 || (target_ds == NC_DATASTORE_CANDIDATE)) {
3408 rpc = nc_rpc_validate(target_ds);
3409 }
3410 if (rpc == NULL) {
3411 DEBUG("mod_netconf: creating rpc request failed");
3412 reply = create_error_reply("Creation of RPC request failed.");
3413 goto finalize;
3414 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003415
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003416 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
3417 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003418
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003419 if (reply == NULL) {
3420 DEBUG("Request: validation ok.");
3421 reply = create_ok_reply();
3422 }
3423 }
3424 nc_rpc_free (rpc);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003425
Tomas Cejka09629492014-07-10 15:58:06 +02003426finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003427 CHECK_AND_FREE(target);
3428 CHECK_AND_FREE(url);
3429 return reply;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003430}
3431
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003432json_object *
3433handle_op_query(json_object *request, unsigned int session_key, int idx)
David Kupka8e60a372012-09-04 09:15:20 +02003434{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003435 json_object *reply = NULL, *filters, *obj;
3436 char *filter = NULL;
3437 int load_children = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003438
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003439 DEBUG("Request: query (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003440
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003441 pthread_mutex_lock(&json_lock);
3442 if (json_object_object_get_ex(request, "filters", &filters) == FALSE) {
3443 pthread_mutex_unlock(&json_lock);
3444 reply = create_error_reply("Missing filters parameter.");
3445 goto finalize;
3446 }
3447 obj = json_object_array_get_idx(filters, idx);
3448 if (!obj) {
3449 pthread_mutex_unlock(&json_lock);
3450 reply = create_error_reply("Filters array parameter shorter than sessions.");
3451 goto finalize;
3452 }
3453 filter = strdup(json_object_get_string(obj));
3454 if (json_object_object_get_ex(request, "load_children", &obj) == TRUE) {
3455 load_children = json_object_get_boolean(obj);
3456 }
3457 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02003458
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003459 reply = libyang_query(session_key, filter, load_children);
David Kupka8e60a372012-09-04 09:15:20 +02003460
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003461 CHECK_ERR_SET_REPLY
3462 if (!reply) {
3463 reply = create_error_reply("Query failed.");
3464 }
David Kupka8e60a372012-09-04 09:15:20 +02003465
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003466finalize:
3467 CHECK_AND_FREE(filter);
3468 return reply;
3469}
David Kupka8e60a372012-09-04 09:15:20 +02003470
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003471json_object *
3472handle_op_merge(json_object *request, unsigned int session_key, int idx)
3473{
3474 json_object *reply = NULL, *configs, *obj;
3475 char *config = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003476
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003477 DEBUG("Request: merge (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003478
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003479 pthread_mutex_lock(&json_lock);
3480 if (json_object_object_get_ex(request, "configurations", &configs) == FALSE) {
3481 pthread_mutex_unlock(&json_lock);
3482 reply = create_error_reply("Missing configurations parameter.");
3483 goto finalize;
3484 }
3485 obj = json_object_array_get_idx(configs, idx);
3486 if (!obj) {
3487 pthread_mutex_unlock(&json_lock);
3488 reply = create_error_reply("Filters array parameter shorter than sessions.");
3489 goto finalize;
3490 }
3491 config = strdup(json_object_get_string(obj));
3492 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02003493
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003494 reply = libyang_merge(session_key, config);
David Kupka8e60a372012-09-04 09:15:20 +02003495
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003496 CHECK_ERR_SET_REPLY
3497 if (!reply) {
3498 reply = create_error_reply("Merge failed.");
3499 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003500
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003501finalize:
3502 CHECK_AND_FREE(config);
3503 return reply;
3504}
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003505
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003506void *
3507thread_routine(void *arg)
3508{
3509 void *retval = NULL;
3510 struct pollfd fds;
3511 json_object *request = NULL, *replies = NULL, *reply, *sessions = NULL;
3512 json_object *js_tmp = NULL;
3513 int operation = (-1), count, i;
3514 int status = 0;
3515 const char *msgtext;
3516 unsigned int session_key = 0;
3517 char *chunked_out_msg = NULL;
3518 int client = ((struct pass_to_thread *)arg)->client;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003519
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003520 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003521
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003522 /* init thread specific err_reply memory */
3523 create_err_reply_p();
David Kupka8e60a372012-09-04 09:15:20 +02003524
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003525 while (!isterminated) {
3526 fds.fd = client;
3527 fds.events = POLLIN;
3528 fds.revents = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003529
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003530 status = poll(&fds, 1, 1000);
Tomas Cejkad5b53772013-06-08 23:01:07 +02003531
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003532 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
3533 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
3534 continue;
3535 } else if (status < 0) {
3536 /* 0: poll time outed
3537 * close socket and ignore this request from the client, it can try it again
3538 * -1: poll failed
3539 * something wrong happend, close this socket and wait for another request
3540 */
3541 close(client);
3542 break;
3543 }
3544 /* status > 0 */
David Kupka8e60a372012-09-04 09:15:20 +02003545
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003546 /* check the status of the socket */
David Kupka8e60a372012-09-04 09:15:20 +02003547
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003548 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
3549 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
3550 /* close client's socket (it's probably already closed by client */
3551 close(client);
3552 break;
3553 }
Tomas Cejka09629492014-07-10 15:58:06 +02003554
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003555 DEBUG("Get framed message...");
3556 buffer = get_framed_message(client);
Tomas Cejka09629492014-07-10 15:58:06 +02003557
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003558 DEBUG("Check read buffer.");
3559 if (buffer != NULL) {
3560 enum json_tokener_error jerr;
3561 pthread_mutex_lock(&json_lock);
3562 request = json_tokener_parse_verbose(buffer, &jerr);
3563 if (jerr != json_tokener_success) {
3564 ERROR("JSON parsing error");
3565 pthread_mutex_unlock(&json_lock);
3566 continue;
3567 }
3568
3569 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
3570 operation = json_object_get_int(js_tmp);
3571 }
3572 pthread_mutex_unlock(&json_lock);
3573 if (operation == -1) {
3574 replies = create_replies();
3575 add_reply(replies, create_error_reply("Missing operation type from frontend."), 0);
3576 goto send_reply;
3577 }
3578
3579 if ((operation < 4) || ((operation > 19) && (operation < 100)) || (operation > 101)) {
3580 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
3581 replies = create_replies();
3582 add_reply(replies, create_error_reply("Operation not supported."), 0);
3583 goto send_reply;
3584 }
3585
3586 DEBUG("operation %d", operation);
3587
3588 /* null global JSON error-reply */
3589 clean_err_reply();
3590
3591 /* clean replies envelope */
3592 if (replies != NULL) {
3593 pthread_mutex_lock(&json_lock);
3594 json_object_put(replies);
3595 pthread_mutex_unlock(&json_lock);
3596 }
3597 replies = create_replies();
3598
3599 if (operation == MSG_CONNECT) {
3600 count = 1;
3601 } else {
3602 pthread_mutex_lock(&json_lock);
3603 if (json_object_object_get_ex(request, "sessions", &sessions) == FALSE) {
3604 add_reply(replies, create_error_reply("Operation missing \"sessions\" arg"), 0);
3605 goto send_reply;
3606 }
3607 count = json_object_array_length(sessions);
3608 pthread_mutex_unlock(&json_lock);
3609 }
3610
3611 for (i = 0; i < count; ++i) {
3612 if (operation != MSG_CONNECT) {
3613 js_tmp = json_object_array_get_idx(sessions, i);
3614 session_key = json_object_get_int(js_tmp);
3615 }
3616
3617 /* process required operation */
3618 switch (operation) {
3619 case MSG_CONNECT:
3620 reply = handle_op_connect(request);
3621 break;
3622 case MSG_DISCONNECT:
3623 reply = handle_op_disconnect(request, session_key);
3624 break;
3625 case MSG_GET:
3626 reply = handle_op_get(request, session_key);
3627 break;
3628 case MSG_GETCONFIG:
3629 reply = handle_op_getconfig(request, session_key);
3630 break;
3631 case MSG_EDITCONFIG:
3632 reply = handle_op_editconfig(request, session_key, i);
3633 break;
3634 case MSG_COPYCONFIG:
3635 reply = handle_op_copyconfig(request, session_key, i);
3636 break;
3637 case MSG_DELETECONFIG:
3638 reply = handle_op_deleteconfig(request, session_key);
3639 break;
3640 case MSG_LOCK:
3641 reply = handle_op_lock(request, session_key);
3642 break;
3643 case MSG_UNLOCK:
3644 reply = handle_op_unlock(request, session_key);
3645 break;
3646 case MSG_KILL:
3647 reply = handle_op_kill(request, session_key);
3648 break;
3649 case MSG_INFO:
3650 reply = handle_op_info(request, session_key);
3651 break;
3652 case MSG_GENERIC:
3653 reply = handle_op_generic(request, session_key, i);
3654 break;
3655 case MSG_GETSCHEMA:
3656 reply = handle_op_getschema(request, session_key);
3657 break;
3658 case MSG_RELOADHELLO:
3659 reply = handle_op_reloadhello(request, session_key);
3660 break;
3661 case MSG_NTF_GETHISTORY:
3662 reply = handle_op_ntfgethistory(request, session_key);
3663 break;
3664 case MSG_VALIDATE:
3665 reply = handle_op_validate(request, session_key);
3666 break;
3667 case SCH_QUERY:
3668 reply = handle_op_query(request, session_key, i);
3669 break;
3670 case SCH_MERGE:
3671 reply = handle_op_merge(request, session_key, i);
3672 break;
3673 }
3674
3675 add_reply(replies, reply, session_key);
3676 }
3677
3678 /* free parameters */
3679 operation = (-1);
3680
3681 DEBUG("Clean request json object.");
3682 if (request != NULL) {
3683 pthread_mutex_lock(&json_lock);
3684 json_object_put(request);
3685 pthread_mutex_unlock(&json_lock);
3686 }
3687 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02003688
3689send_reply:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003690 /* send reply to caller */
3691 if (replies) {
3692 pthread_mutex_lock(&json_lock);
3693 msgtext = json_object_to_json_string(replies);
3694 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
3695 if (buffer != NULL) {
3696 free(buffer);
3697 buffer = NULL;
3698 }
3699 pthread_mutex_unlock(&json_lock);
3700 break;
3701 }
3702 pthread_mutex_unlock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003703
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003704 DEBUG("Send framed reply json object.");
3705 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
3706 DEBUG("Clean reply json object.");
3707 pthread_mutex_lock(&json_lock);
3708 json_object_put(replies);
3709 replies = NULL;
3710 DEBUG("Clean message buffer.");
3711 CHECK_AND_FREE(chunked_out_msg);
3712 chunked_out_msg = NULL;
3713 if (buffer) {
3714 free(buffer);
3715 buffer = NULL;
3716 }
3717 pthread_mutex_unlock(&json_lock);
3718 clean_err_reply();
3719 } else {
3720 ERROR("Reply is NULL, shouldn't be...");
3721 continue;
3722 }
3723 }
3724 }
3725 free(arg);
3726 free_err_reply();
David Kupka8e60a372012-09-04 09:15:20 +02003727
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003728 return retval;
David Kupka8e60a372012-09-04 09:15:20 +02003729}
3730
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003731/**
3732 * \brief Close all open NETCONF sessions.
3733 *
3734 * During termination of mod_netconf, it is useful to close all remaining
3735 * sessions. This function iterates over the list of sessions and close them
3736 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003737 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003738static void
3739close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003740{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003741 struct session_with_mutex *locked_session, *next_session;
3742 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003743
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003744 /* get exclusive access to sessions_list (conns) */
3745 DEBUG("LOCK wrlock %s", __func__);
3746 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
3747 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3748 return;
3749 }
3750 for (next_session = netconf_sessions_list; next_session;) {
3751 locked_session = next_session;
3752 next_session = locked_session->next;
Tomas Cejka47387fd2013-06-10 20:37:46 +02003753
Michal Vaskoc3146782015-11-04 14:46:41 +01003754 /* close_and_free_session handles locking on its own */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003755 DEBUG("Closing NETCONF session %u (SID %s).", locked_session->session_key, nc_session_get_id(locked_session->session));
3756 close_and_free_session(locked_session);
3757 }
3758 netconf_sessions_list = NULL;
3759
3760 /* get exclusive access to sessions_list (conns) */
3761 DEBUG("UNLOCK wrlock %s", __func__);
3762 if (pthread_rwlock_unlock (&session_lock) != 0) {
3763 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3764 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003765}
3766
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003767static void
3768check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003769{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003770 struct nc_session *ns = NULL;
3771 struct session_with_mutex *locked_session = NULL;
3772 time_t current_time = time(NULL);
3773 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003774
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003775 /* get exclusive access to sessions_list (conns) */
3776 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
3777 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3778 return;
3779 }
3780 for (locked_session = netconf_sessions_list; locked_session; locked_session = locked_session->next) {
3781 ns = locked_session->session;
3782 if (ns == NULL) {
3783 continue;
3784 }
3785 pthread_mutex_lock(&locked_session->lock);
3786 if ((current_time - locked_session->last_activity) > ACTIVITY_TIMEOUT) {
3787 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 +02003788
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003789 /* close_and_free_session handles locking on its own */
3790 close_and_free_session(locked_session);
3791 } else {
3792 pthread_mutex_unlock(&locked_session->lock);
3793 }
3794 }
3795 /* get exclusive access to sessions_list (conns) */
3796 if (pthread_rwlock_unlock(&session_lock) != 0) {
3797 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3798 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003799}
3800
3801
3802/**
Radek Krejcif23850c2012-07-23 16:14:17 +02003803 * This is actually implementation of NETCONF client
3804 * - requests are received from UNIX socket in the predefined format
3805 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01003806 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02003807 *
3808 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003809static void
3810forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02003811{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003812 struct timeval tv;
3813 struct sockaddr_un local, remote;
3814 int lsock, client, ret, i, pthread_count = 0;
3815 unsigned int olds = 0, timediff = 0;
3816 socklen_t len;
3817 struct pass_to_thread *arg;
3818 pthread_t *ptids = calloc(1, sizeof(pthread_t));
3819 struct timespec maxtime;
3820 pthread_rwlockattr_t lock_attrs;
3821 #ifdef WITH_NOTIFICATIONS
3822 char use_notifications = 0;
3823 #endif
David Kupka8e60a372012-09-04 09:15:20 +02003824
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003825 /* wait at most 5 seconds for every thread to terminate */
3826 maxtime.tv_sec = 5;
3827 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02003828
Tomas Cejka04e08f42014-03-27 19:52:34 +01003829#ifdef HAVE_UNIXD_SETUP_CHILD
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003830 /* change uid and gid of process for security reasons */
3831 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01003832#else
3833# ifdef SU_GROUP
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003834 if (strlen(SU_GROUP) > 0) {
3835 struct group *g = getgrnam(SU_GROUP);
3836 if (g == NULL) {
3837 ERROR("GID (%s) was not found.", SU_GROUP);
3838 return;
3839 }
3840 if (setgid(g->gr_gid) != 0) {
3841 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
3842 return;
3843 }
3844 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003845# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003846 DEBUG("no SU_GROUP");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003847# endif
3848# ifdef SU_USER
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003849 if (strlen(SU_USER) > 0) {
3850 struct passwd *p = getpwnam(SU_USER);
3851 if (p == NULL) {
3852 ERROR("UID (%s) was not found.", SU_USER);
3853 return;
3854 }
3855 if (setuid(p->pw_uid) != 0) {
3856 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
3857 return;
3858 }
3859 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003860# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003861 DEBUG("no SU_USER");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003862# endif
3863#endif
Radek Krejci469aab82012-07-22 18:42:20 +02003864
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003865 /* try to remove if exists */
3866 unlink(sockname);
Tomas Cejka04e08f42014-03-27 19:52:34 +01003867
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003868 /* create listening UNIX socket to accept incoming connections */
3869 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
3870 ERROR("Creating socket failed (%s)", strerror(errno));
3871 goto error_exit;
3872 }
Radek Krejci469aab82012-07-22 18:42:20 +02003873
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003874 local.sun_family = AF_UNIX;
3875 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
3876 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
Radek Krejci469aab82012-07-22 18:42:20 +02003877
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003878 if (bind(lsock, (struct sockaddr *)&local, len) == -1) {
3879 if (errno == EADDRINUSE) {
3880 ERROR("mod_netconf socket address already in use");
3881 goto error_exit;
3882 }
3883 ERROR("Binding socket failed (%s)", strerror(errno));
3884 goto error_exit;
3885 }
Radek Krejci469aab82012-07-22 18:42:20 +02003886
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003887 if (listen(lsock, MAX_SOCKET_CL) == -1) {
3888 ERROR("Setting up listen socket failed (%s)", strerror(errno));
3889 goto error_exit;
3890 }
3891 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02003892
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003893 uid_t user = -1;
3894 if (strlen(CHOWN_USER) > 0) {
3895 struct passwd *p = getpwnam(CHOWN_USER);
3896 if (p != NULL) {
3897 user = p->pw_uid;
3898 }
3899 }
3900 gid_t group = -1;
3901 if (strlen(CHOWN_GROUP) > 0) {
3902 struct group *g = getgrnam(CHOWN_GROUP);
3903 if (g != NULL) {
3904 group = g->gr_gid;
3905 }
3906 }
3907 if (chown(sockname, user, group) == -1) {
3908 ERROR("Chown on socket file failed (%s).", strerror(errno));
3909 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003910
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003911 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02003912
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003913 #ifdef WITH_NOTIFICATIONS
3914 if (notification_init() == -1) {
3915 ERROR("libwebsockets initialization failed");
3916 use_notifications = 0;
3917 } else {
3918 use_notifications = 1;
3919 }
3920 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003921
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003922 /* setup libnetconf's callbacks */
3923 nc_verbosity(NC_VERB_DEBUG);
3924 nc_callback_print(clb_print);
3925 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
3926 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
3927 nc_callback_sshauth_password(netconf_callback_sshauth_password);
3928 nc_callback_sshauth_passphrase(netconf_callback_sshauth_passphrase);
3929 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02003930
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003931 /* disable publickey authentication */
3932 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
Radek Krejci469aab82012-07-22 18:42:20 +02003933
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003934 /* create mutex protecting session list */
3935 pthread_rwlockattr_init(&lock_attrs);
3936 /* rwlock is shared only with threads in this process */
3937 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
3938 /* create rw lock */
3939 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
3940 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
3941 goto error_exit;
3942 }
3943 pthread_mutex_init(&ntf_history_lock, NULL);
3944 pthread_mutex_init(&json_lock, NULL);
3945 DEBUG("Initialization of notification history.");
3946 if (pthread_key_create(&notif_history_key, NULL) != 0) {
3947 ERROR("Initialization of notification history failed.");
3948 }
3949 if (pthread_key_create(&err_reply_key, NULL) != 0) {
3950 ERROR("Initialization of reply key failed.");
3951 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02003952
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003953 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
3954 while (isterminated == 0) {
3955 gettimeofday(&tv, NULL);
3956 timediff = (unsigned int)tv.tv_sec - olds;
3957 #ifdef WITH_NOTIFICATIONS
3958 if (use_notifications == 1) {
3959 notification_handle();
3960 }
3961 #endif
3962 if (timediff > ACTIVITY_CHECK_INTERVAL) {
3963 check_timeout_and_close();
3964 }
Radek Krejci469aab82012-07-22 18:42:20 +02003965
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003966 /* open incoming connection if any */
3967 len = sizeof(remote);
3968 client = accept(lsock, (struct sockaddr *) &remote, &len);
3969 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
3970 usleep(SLEEP_TIME * 1000);
3971 continue;
3972 } else if (client == -1 && (errno == EINTR)) {
3973 continue;
3974 } else if (client == -1) {
3975 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
3976 continue;
3977 }
Radek Krejci469aab82012-07-22 18:42:20 +02003978
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003979 /* set client's socket as non-blocking */
3980 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02003981
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003982 arg = malloc(sizeof(struct pass_to_thread));
3983 arg->client = client;
3984 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02003985
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003986 /* start new thread. It will serve this particular request and then terminate */
3987 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void *)arg)) != 0) {
3988 ERROR("Creating POSIX thread failed: %d\n", ret);
3989 } else {
3990 DEBUG("Thread %lu created", ptids[pthread_count]);
3991 pthread_count++;
3992 ptids = realloc (ptids, sizeof(pthread_t) * (pthread_count+1));
3993 ptids[pthread_count] = 0;
3994 }
Radek Krejci469aab82012-07-22 18:42:20 +02003995
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003996 /* check if some thread already terminated, free some resources by joining it */
3997 for (i = 0; i < pthread_count; i++) {
3998 if (pthread_tryjoin_np(ptids[i], (void **)&arg) == 0) {
3999 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
4000 pthread_count--;
4001 if (pthread_count > 0) {
4002 /* place last Thread ID on the place of joined one */
4003 ptids[i] = ptids[pthread_count];
4004 }
4005 }
4006 }
4007 DEBUG("Running %d threads", pthread_count);
4008 }
Radek Krejci469aab82012-07-22 18:42:20 +02004009
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004010 DEBUG("mod_netconf terminating...");
4011 /* join all threads */
4012 for (i = 0; i < pthread_count; i++) {
4013 pthread_timedjoin_np(ptids[i], (void **)&arg, &maxtime);
4014 }
Radek Krejci469aab82012-07-22 18:42:20 +02004015
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004016 #ifdef WITH_NOTIFICATIONS
4017 notification_close();
4018 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01004019
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004020 /* close all NETCONF sessions */
4021 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02004022
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004023 /* destroy rwlock */
4024 pthread_rwlock_destroy(&session_lock);
4025 pthread_rwlockattr_destroy(&lock_attrs);
David Kupka8e60a372012-09-04 09:15:20 +02004026
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004027 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02004028
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004029 free(ptids);
4030 close(lsock);
4031 exit(0);
4032 return;
4033
Tomas Cejkaef531ee2013-11-12 16:07:00 +01004034error_exit:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004035 close(lsock);
4036 free(ptids);
4037 return;
Radek Krejci469aab82012-07-22 18:42:20 +02004038}
4039
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004040int
4041main(int argc, char **argv)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01004042{
Michal Vaskoc3146782015-11-04 14:46:41 +01004043 struct sigaction action;
4044 sigset_t block_mask;
4045
4046 if (argc > 1) {
4047 sockname = argv[1];
4048 } else {
4049 sockname = SOCKET_FILENAME;
4050 }
4051
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004052 sigfillset(&block_mask);
Michal Vaskoc3146782015-11-04 14:46:41 +01004053 action.sa_handler = signal_handler;
4054 action.sa_mask = block_mask;
4055 action.sa_flags = 0;
4056 sigaction(SIGINT, &action, NULL);
4057 sigaction(SIGTERM, &action, NULL);
4058
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01004059 forked_proc();
4060 DEBUG("Terminated");
4061 return 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01004062}