blob: 12f1023f2ae3eb608a518814279b394fe1ce48c4 [file] [log] [blame]
Radek Krejci469aab82012-07-22 18:42:20 +02001/*!
2 * \file mod_netconf.c
3 * \brief NETCONF Apache modul for Netopeer
4 * \author Tomas Cejka <cejkat@cesnet.cz>
5 * \author Radek Krejci <rkrejci@cesnet.cz>
6 * \date 2011
7 * \date 2012
Tomas Cejka94da2c52013-01-08 18:20:30 +01008 * \date 2013
Radek Krejci469aab82012-07-22 18:42:20 +02009 */
10/*
Tomas Cejkad340dbf2013-03-24 20:36:57 +010011 * Copyright (C) 2011-2013 CESNET
Radek Krejci469aab82012-07-22 18:42:20 +020012 *
13 * LICENSE TERMS
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in
22 * the documentation and/or other materials provided with the
23 * distribution.
24 * 3. Neither the name of the Company nor the names of its contributors
25 * may be used to endorse or promote products derived from this
26 * software without specific prior written permission.
27 *
28 * ALTERNATIVELY, provided that this notice is retained in full, this
29 * product may be distributed under the terms of the GNU General Public
30 * License (GPL) version 2 or later, in which case the provisions
31 * of the GPL apply INSTEAD OF those given above.
32 *
33 * This software is provided ``as is'', and any express or implied
34 * warranties, including, but not limited to, the implied warranties of
35 * merchantability and fitness for a particular purpose are disclaimed.
36 * In no event shall the company or contributors be liable for any
37 * direct, indirect, incidental, special, exemplary, or consequential
38 * damages (including, but not limited to, procurement of substitute
39 * goods or services; loss of use, data, or profits; or business
40 * interruption) however caused and on any theory of liability, whether
41 * in contract, strict liability, or tort (including negligence or
42 * otherwise) arising in any way out of the use of this software, even
43 * if advised of the possibility of such damage.
44 *
45 */
Michal Vaskoc3146782015-11-04 14:46:41 +010046#define _GNU_SOURCE
Radek Krejci469aab82012-07-22 18:42:20 +020047
Radek Krejci7b4ddd02012-07-30 08:09:58 +020048#include <unistd.h>
49#include <poll.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010050#include <time.h>
Radek Krejci469aab82012-07-22 18:42:20 +020051#include <sys/types.h>
52#include <sys/socket.h>
53#include <sys/un.h>
Tomas Cejkad340dbf2013-03-24 20:36:57 +010054#include <sys/fcntl.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010055#include <sys/stat.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010056#include <pwd.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010057#include <errno.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010058#include <limits.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010059#include <grp.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010060#include <signal.h>
David Kupka8e60a372012-09-04 09:15:20 +020061#include <pthread.h>
62#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020063
Radek Krejci469aab82012-07-22 18:42:20 +020064#include <libnetconf.h>
Tomas Cejkab3cc64f2013-05-03 19:44:54 +020065#include <libnetconf_ssh.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020066
Tomas Cejka04e08f42014-03-27 19:52:34 +010067#include "../config.h"
68
Tomas Cejkad340dbf2013-03-24 20:36:57 +010069#ifdef WITH_NOTIFICATIONS
70#include "notification_module.h"
71#endif
72
Tomas Cejka94da2c52013-01-08 18:20:30 +010073#include "message_type.h"
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020074#include "mod_netconf.h"
Radek Krejci469aab82012-07-22 18:42:20 +020075
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010076#define SCHEMA_DIR "/tmp/yang_models"
Radek Krejci469aab82012-07-22 18:42:20 +020077#define MAX_PROCS 5
Tomas Cejka86f0fc12014-09-17 15:09:38 +020078#define SOCKET_FILENAME "/var/run/mod_netconf.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020079#define MAX_SOCKET_CL 10
80#define BUFFER_SIZE 4096
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010081#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
82#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020083
Michal Vasko7732dee2015-11-05 10:22:15 +010084/* sleep in master process for non-blocking socket reading, in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020085#define SLEEP_TIME 200
86
87#ifndef offsetof
88#define offsetof(type, member) ((size_t) ((type *) 0)->member)
89#endif
90
Tomas Cejka027f3bc2012-11-10 20:28:36 +010091/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020092struct timeval timeout = { 1, 0 };
93
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010094#define NCWITHDEFAULTS NCWD_MODE_NOTSET
Tomas Cejka5064c232013-01-17 09:30:58 +010095
Radek Krejci469aab82012-07-22 18:42:20 +020096#define MSG_OK 0
97#define MSG_OPEN 1
98#define MSG_DATA 2
99#define MSG_CLOSE 3
100#define MSG_ERROR 4
101#define MSG_UNKNOWN 5
102
Tomas Cejka47387fd2013-06-10 20:37:46 +0200103pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200104pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100105pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100106pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
107
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100108unsigned int session_key_generator = 1;
Michal Vaskoc3146782015-11-04 14:46:41 +0100109struct session_with_mutex *netconf_sessions_list = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +0100110static const char *sockname;
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200111static pthread_key_t notif_history_key;
Tomas Cejka442258e2014-04-01 18:17:18 +0200112pthread_key_t err_reply_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200113volatile int isterminated = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200114static char* password;
115
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100116json_object *create_ok_reply(void);
117json_object *create_data_reply(const char *data);
118static char *netconf_getschema(unsigned int session_key, const char *identifier, const char *version,
119 const char *format, json_object **err);
120static void node_add_metadata_recursive(struct lyd_node *data_tree, struct lys_module *module,
121 json_object *data_json_parent);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100122static void node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100123
124static void
125signal_handler(int sign)
Radek Krejci469aab82012-07-22 18:42:20 +0200126{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100127 switch (sign) {
128 case SIGINT:
129 case SIGTERM:
130 isterminated = 1;
131 break;
132 }
Radek Krejci469aab82012-07-22 18:42:20 +0200133}
134
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100135int
136netconf_callback_ssh_hostkey_check(const char* UNUSED(hostname), ssh_session UNUSED(session))
Radek Krejci469aab82012-07-22 18:42:20 +0200137{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100138 /* always approve */
139 return (EXIT_SUCCESS);
Radek Krejci469aab82012-07-22 18:42:20 +0200140}
141
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100142char *
143netconf_callback_sshauth_passphrase(const char *UNUSED(username), const char *UNUSED(hostname), const char *UNUSED(priv_key_file))
Radek Krejci469aab82012-07-22 18:42:20 +0200144{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100145 char *buf;
146 buf = strdup(password);
147 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200148}
149
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100150char *
151netconf_callback_sshauth_password(const char *UNUSED(username), const char *UNUSED(hostname))
Radek Krejci469aab82012-07-22 18:42:20 +0200152{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100153 char *buf;
154 buf = strdup(password);
155 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200156}
157
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100158char *
159netconf_callback_sshauth_interactive(const char *UNUSED(name), const char *UNUSED(instruction),
160 const char *UNUSED(prompt), int UNUSED(echo))
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200161{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100162 char *buf;
163 buf = strdup(password);
164 return (buf);
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200165}
166
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100167void
168netconf_callback_error_process(const char *UNUSED(tag),
169 const char *UNUSED(type),
170 const char *UNUSED(severity),
171 const char *UNUSED(apptag),
172 const char *UNUSED(path),
173 const char *message,
174 const char *UNUSED(attribute),
175 const char *UNUSED(element),
176 const char *UNUSED(ns),
177 const char *UNUSED(sid))
Radek Krejcic11fd862012-07-26 12:41:21 +0200178{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100179 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
180 if (err_reply_p == NULL) {
181 ERROR("Error message was not allocated. %s", __func__);
182 return;
183 }
184 json_object *err_reply = *err_reply_p;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100185
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100186 json_object *array = NULL;
187 if (err_reply == NULL) {
188 ERROR("error calback: empty error list");
189 pthread_mutex_lock(&json_lock);
190 err_reply = json_object_new_object();
191 array = json_object_new_array();
192 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
193 json_object_object_add(err_reply, "errors", array);
194 if (message != NULL) {
195 json_object_array_add(array, json_object_new_string(message));
196 }
197 pthread_mutex_unlock(&json_lock);
198 (*err_reply_p) = err_reply;
199 } else {
200 ERROR("error calback: nonempty error list");
201 pthread_mutex_lock(&json_lock);
202 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
203 if (message != NULL) {
204 json_object_array_add(array, json_object_new_string(message));
205 }
206 }
207 pthread_mutex_unlock(&json_lock);
208 }
209 pthread_setspecific(err_reply_key, err_reply_p);
210 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200211}
212
Tomas Cejka47387fd2013-06-10 20:37:46 +0200213/**
214 * should be used in locked area
215 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100216void
217prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200218{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100219 json_object *json_obj = NULL;
220 json_object *js_tmp = NULL;
221 char *old_sid = NULL;
222 const char *j_old_sid = NULL;
223 const char *cpbltstr;
224 struct nc_cpblts* cpblts = NULL;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200225
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100226 if (s == NULL) {
227 ERROR("No session given.");
228 return;
229 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200230
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100231 pthread_mutex_lock(&json_lock);
232 if (s->hello_message != NULL) {
233 ERROR("clean previous hello message");
234 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
235 j_old_sid = json_object_get_string(js_tmp);
236 if (j_old_sid != NULL) {
237 old_sid = strdup(j_old_sid);
238 }
239 }
240 json_object_put(s->hello_message);
241 s->hello_message = NULL;
242 }
243 s->hello_message = json_object_new_object();
244 if (session != NULL) {
245 if (old_sid != NULL) {
246 /* use previous sid */
247 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
248 free(old_sid);
249 old_sid = NULL;
250 } else {
251 /* we don't have old sid */
252 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
253 }
254 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
255 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
256 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
257 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
258 cpblts = nc_session_get_cpblts (session);
259 if (cpblts != NULL) {
260 json_obj = json_object_new_array();
261 nc_cpblts_iter_start (cpblts);
262 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
263 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
264 }
265 json_object_object_add(s->hello_message, "capabilities", json_obj);
266 }
267 DEBUG("%s", json_object_to_json_string(s->hello_message));
268 } else {
269 ERROR("Session was not given.");
270 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
271 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
272 }
273 DEBUG("Status info from hello message prepared");
274 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200275
276}
277
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100278void
279create_err_reply_p()
Tomas Cejka442258e2014-04-01 18:17:18 +0200280{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100281 json_object **err_reply = calloc(1, sizeof(json_object **));
282 if (err_reply == NULL) {
283 ERROR("Allocation of err_reply storage failed!");
284 return;
285 }
286 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
287 ERROR("cannot set thread-specific value.");
288 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200289}
290
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100291void
292clean_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200293{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100294 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
295 if (err_reply != NULL) {
296 if (*err_reply != NULL) {
297 pthread_mutex_lock(&json_lock);
298 json_object_put(*err_reply);
299 pthread_mutex_unlock(&json_lock);
300 }
301 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
302 ERROR("Cannot set thread-specific hash value.");
303 }
304 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200305}
306
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100307void
308free_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200309{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100310 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
311 if (err_reply != NULL) {
312 if (*err_reply != NULL) {
313 pthread_mutex_lock(&json_lock);
314 json_object_put(*err_reply);
315 pthread_mutex_unlock(&json_lock);
316 }
317 free(err_reply);
318 err_reply = NULL;
319 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
320 ERROR("Cannot set thread-specific hash value.");
321 }
322 }
323}
324
325static struct session_with_mutex *
326session_get_locked(unsigned int session_key, json_object **err)
327{
328 struct session_with_mutex *locked_session;
329
330 /* get non-exclusive (read) access to sessions_list (conns) */
331 DEBUG("LOCK wrlock %s", __func__);
332 if (pthread_rwlock_rdlock(&session_lock) != 0) {
333 if (*err) {
334 *err = create_error_reply("Locking failed.");
335 }
336 return NULL;
337 }
338 /* get session where send the RPC */
339 for (locked_session = netconf_sessions_list;
340 locked_session && (locked_session->session_key != session_key);
341 locked_session = locked_session->next);
342 if (!locked_session) {
343 if (*err) {
344 *err = create_error_reply("Session not found.");
345 }
346 return NULL;
347 }
348
349 /* get exclusive access to session */
350 DEBUG("LOCK mutex %s", __func__);
351 if (pthread_mutex_lock(&locked_session->lock) != 0) {
352 if (*err) {
353 *err = create_error_reply("Locking failed.");
354 }
355 goto wrlock_fail;
356 }
357 return locked_session;
358
359wrlock_fail:
360 DEBUG("UNLOCK wrlock %s", __func__);
361 pthread_rwlock_unlock(&session_lock);
362 return NULL;
363}
364
365static void
366session_unlock(struct session_with_mutex *locked_session)
367{
368 DEBUG("UNLOCK mutex %s", __func__);
369 pthread_mutex_unlock(&locked_session->lock);
370 DEBUG("UNLOCK wrlock %s", __func__);
371 pthread_rwlock_unlock(&session_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +0200372}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200373
Michal Vasko3fda9a92015-11-23 10:10:57 +0100374static void
375node_metadata_text(const char *text, const char *name, json_object *parent)
376{
377 json_object *obj;
378
379 if (!text) {
380 return;
381 }
382
383 obj = json_object_new_string(text);
384 json_object_object_add(parent, name, obj);
385}
386
387static void
388node_metadata_restr(struct lys_restr *restr, const char *name, json_object *parent)
389{
390 json_object *obj;
391
392 if (!restr) {
393 return;
394 }
395
396 obj = json_object_new_string(restr->expr);
397 json_object_object_add(parent, name, obj);
398}
399
400static void
401node_metadata_must(uint8_t must_size, struct lys_restr *must, json_object *parent)
402{
403 uint8_t i;
404 json_object *array, *obj;
405
406 if (!must_size || !must) {
407 return;
408 }
409
410 array = json_object_new_array();
411
412 for (i = 0; i < must_size; ++i) {
413 obj = json_object_new_string(must[i].expr);
414 json_object_array_add(array, obj);
415 }
416
417 json_object_object_add(parent, "must", array);
418}
419
420static void
421node_metadata_basic(struct lys_node *node, json_object *parent)
422{
423 json_object *obj;
424
425 /* description */
426 node_metadata_text(node->dsc, "description", parent);
427
428 /* reference */
429 node_metadata_text(node->ref, "reference", parent);
430
431 /* config */
432 if (node->flags & LYS_CONFIG_R) {
433 obj = json_object_new_boolean(0);
434 } else {
435 obj = json_object_new_boolean(1);
436 }
437 json_object_object_add(parent, "config", obj);
438
439 /* status */
440 if (node->flags & LYS_STATUS_DEPRC) {
441 obj = json_object_new_string("deprecated");
442 } else if (node->flags & LYS_STATUS_OBSLT) {
443 obj = json_object_new_string("obsolete");
444 } else {
445 obj = json_object_new_string("current");
446 }
447 json_object_object_add(parent, "status", obj);
448
449 /* mandatory */
450 if (node->flags & LYS_MAND_TRUE) {
451 obj = json_object_new_boolean(1);
452 } else {
453 obj = json_object_new_boolean(0);
454 }
455 json_object_object_add(parent, "mandatory", obj);
456
457 /* NACM extensions */
458 if (node->nacm) {
459 if (node->nacm & LYS_NACM_DENYW) {
460 obj = json_object_new_string("default-deny-write");
461 } else {
462 obj = json_object_new_string("default-deny-all");
463 }
464 json_object_object_add(parent, "ext", obj);
465 }
466}
467
468static void
469node_metadata_when(struct lys_when *when, json_object *parent)
470{
471 json_object *obj;
472
473 if (!when) {
474 return;
475 }
476
477 obj = json_object_new_string(when->cond);
478 json_object_object_add(parent, "when", obj);
479}
480
481static void
482node_metadata_children(struct lys_node *node, json_object *parent)
483{
484 json_object *child_array = NULL, *choice_array = NULL, *obj;
485 struct lys_node *child;
486
487 if (!node->child) {
488 return;
489 }
490
491 LY_TREE_FOR(node->child, child) {
492 if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML)) {
493 obj = json_object_new_string(child->name);
494 if (!child_array) {
495 child_array = json_object_new_array();
496 }
497 json_object_array_add(child_array, obj);
498 } else if (child->nodetype == LYS_CHOICE) {
499 obj = json_object_new_string(child->name);
500 if (!choice_array) {
501 choice_array = json_object_new_array();
502 }
503 json_object_array_add(choice_array, obj);
504 }
505 }
506
507 if (child_array) {
508 json_object_object_add(parent, "children", child_array);
509 }
510 if (choice_array) {
511 json_object_object_add(parent, "choice", choice_array);
512 }
513}
514
515static void
516node_metadata_cases(struct lys_node_choice *choice, json_object *parent)
517{
518 json_object *array, *obj;
519 struct lys_node *child;
520
521 if (!choice->child) {
522 return;
523 }
524
525 array = json_object_new_array();
526
527 LY_TREE_FOR(choice->child, child) {
528 if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_CASE)) {
529 obj = json_object_new_string(child->name);
530 json_object_array_add(array, obj);
531 }
532 }
533
534 json_object_object_add(parent, "cases", array);
535}
536
537static void
538node_metadata_min_max(uint32_t min, uint32_t max, json_object *parent)
539{
540 json_object *obj;
541
542 if (min) {
543 obj = json_object_new_int(min);
544 json_object_object_add(parent, "min-elements", obj);
545 }
546
547 if (max) {
548 obj = json_object_new_int(max);
549 json_object_object_add(parent, "max-elements", obj);
550 }
551}
552
553static void
554node_metadata_ident_recursive(struct lys_ident *ident, json_object *array)
555{
556 struct lys_ident_der *cur;
557 json_object *obj;
558
559 if (!ident) {
560 return;
561 }
562
563 obj = json_object_new_string(ident->name);
564 json_object_array_add(array, obj);
565
566 for (cur = ident->der; cur; cur = cur->next) {
567 node_metadata_ident_recursive(cur->ident, array);
568 }
569}
570
571static void
572node_metadata_type(struct lys_type *type, struct lys_module *module, json_object *parent)
573{
574 json_object *obj, *array, *item;
575 char *str;
576 int i;
577
578 /* built-in YANG type */
579 if (!type->der->module) {
580 switch (type->base) {
581 case LY_TYPE_BINARY:
582 node_metadata_text("binary", "type", parent);
583 node_metadata_restr(type->info.binary.length, "length", parent);
584 break;
585 case LY_TYPE_BITS:
586 node_metadata_text("bits", "type", parent);
587
588 array = json_object_new_array();
589 for (i = 0; i < type->info.bits.count; ++i) {
590 item = json_object_new_object();
591 obj = json_object_new_string(type->info.bits.bit[i].name);
592 json_object_object_add(item, "name", obj);
593 obj = json_object_new_int(type->info.bits.bit[i].pos);
594 json_object_object_add(item, "position", obj);
595 json_object_array_add(array, item);
596 }
597 json_object_object_add(parent, "bits", array);
598 break;
599 case LY_TYPE_BOOL:
600 node_metadata_text("bool", "type", parent);
601 break;
602 case LY_TYPE_DEC64:
603 node_metadata_text("decimal64", "type", parent);
604 node_metadata_restr(type->info.dec64.range, "range", parent);
605 obj = json_object_new_int(type->info.dec64.dig);
606 json_object_object_add(parent, "fraction-digits", obj);
607 break;
608 case LY_TYPE_EMPTY:
609 node_metadata_text("empty", "type", parent);
610 break;
611 case LY_TYPE_ENUM:
612 node_metadata_text("enumeration", "type", parent);
613
614 array = json_object_new_array();
615 for (i = 0; i < type->info.enums.count; ++i) {
616 obj = json_object_new_string(type->info.enums.enm[i].name);
617 json_object_array_add(array, obj);
618 }
619 json_object_object_add(parent, "enumval", array);
620 break;
621 case LY_TYPE_IDENT:
622 node_metadata_text("identityref", "type", parent);
623
624 array = json_object_new_array();
625 node_metadata_ident_recursive(type->info.ident.ref, array);
626 json_object_object_add(parent, "identityval", array);
627 break;
628 case LY_TYPE_INST:
629 node_metadata_text("instance-identifier", "type", parent);
630 if (type->info.inst.req == -1) {
631 obj = json_object_new_boolean(0);
632 } else {
633 obj = json_object_new_boolean(1);
634 }
635 json_object_object_add(parent, "require-instance", obj);
636 break;
637 case LY_TYPE_LEAFREF:
638 node_metadata_text("leafref", "type", parent);
639 node_metadata_text(type->info.lref.path, "path", parent);
640 break;
641 case LY_TYPE_STRING:
642 node_metadata_text("string", "type", parent);
643 node_metadata_restr(type->info.str.length, "length", parent);
644 if (type->info.str.pat_count) {
645 array = json_object_new_array();
646 for (i = 0; i < type->info.str.pat_count; ++i) {
647 obj = json_object_new_string(type->info.str.patterns[i].expr);
648 json_object_array_add(array, obj);
649 }
650 json_object_object_add(parent, "pattern", array);
651 }
652 break;
653 case LY_TYPE_UNION:
654 node_metadata_text("union", "type", parent);
655 array = json_object_new_array();
656 for (i = 0; i < type->info.uni.count; ++i) {
657 obj = json_object_new_object();
658 node_metadata_type(&type->info.uni.types[i], module, obj);
659 json_object_array_add(array, obj);
660 }
661 json_object_object_add(parent, "types", array);
662 break;
663 case LY_TYPE_INT8:
664 node_metadata_text("int8", "type", parent);
665 node_metadata_restr(type->info.num.range, "range", parent);
666 break;
667 case LY_TYPE_UINT8:
668 node_metadata_text("uint8", "type", parent);
669 node_metadata_restr(type->info.num.range, "range", parent);
670 break;
671 case LY_TYPE_INT16:
672 node_metadata_text("int16", "type", parent);
673 node_metadata_restr(type->info.num.range, "range", parent);
674 break;
675 case LY_TYPE_UINT16:
676 node_metadata_text("uint16", "type", parent);
677 node_metadata_restr(type->info.num.range, "range", parent);
678 break;
679 case LY_TYPE_INT32:
680 node_metadata_text("int32", "type", parent);
681 node_metadata_restr(type->info.num.range, "range", parent);
682 break;
683 case LY_TYPE_UINT32:
684 node_metadata_text("uint32", "type", parent);
685 node_metadata_restr(type->info.num.range, "range", parent);
686 break;
687 case LY_TYPE_INT64:
688 node_metadata_text("int64", "type", parent);
689 node_metadata_restr(type->info.num.range, "range", parent);
690 break;
691 case LY_TYPE_UINT64:
692 node_metadata_text("uint64", "type", parent);
693 node_metadata_restr(type->info.num.range, "range", parent);
694 break;
695 default:
696 ERROR("Internal: unknown type (%s:%d)", __FILE__, __LINE__);
697 break;
698 }
699
700 /* typedef */
701 } else {
702 if (!module || !type->module_name || !strcmp(type->module_name, module->name)) {
703 node_metadata_text(type->der->name, "type", parent);
704 } else {
705 asprintf(&str, "%s:%s", type->module_name, type->der->name);
706 node_metadata_text(str, "type", parent);
707 free(str);
708 }
709 obj = json_object_new_object();
710 node_metadata_typedef(type->der, obj);
711 json_object_object_add(parent, "typedef", obj);
712 }
713}
714
715static void
716node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent)
717{
718 json_object *obj;
719
720 /* description */
721 node_metadata_text(tpdf->dsc, "description", parent);
722
723 /* reference */
724 node_metadata_text(tpdf->ref, "reference", parent);
725
726 /* status */
727 if (tpdf->flags & LYS_STATUS_DEPRC) {
728 obj = json_object_new_string("deprecated");
729 } else if (tpdf->flags & LYS_STATUS_OBSLT) {
730 obj = json_object_new_string("obsolete");
731 } else {
732 obj = json_object_new_string("current");
733 }
734 json_object_object_add(parent, "status", obj);
735
736 /* type */
737 node_metadata_type(&tpdf->type, tpdf->module, parent);
738
739 /* units */
740 node_metadata_text(tpdf->units, "units", parent);
741
742 /* default */
743 node_metadata_text(tpdf->dflt, "default", parent);
744}
745
746static void
747node_metadata_container(struct lys_node_container *cont, json_object *parent)
748{
749 json_object *obj;
750
751 /* element type */
752 obj = json_object_new_string("container");
753 json_object_object_add(parent, "eltype", obj);
754
755 /* shared info */
756 node_metadata_basic((struct lys_node *)cont, parent);
757
758 /* must */
759 node_metadata_must(cont->must_size, cont->must, parent);
760
761 /* presence */
762 node_metadata_text(cont->presence, "presence", parent);
763
764 /* when */
765 node_metadata_when(cont->when, parent);
766
767 /* children & choice */
768 node_metadata_children((struct lys_node *)cont, parent);
769}
770
771static void
772node_metadata_choice(struct lys_node_choice *choice, json_object *parent)
773{
774 json_object *obj;
775
776 /* element type */
777 obj = json_object_new_string("choice");
778 json_object_object_add(parent, "eltype", obj);
779
780 /* shared info */
781 node_metadata_basic((struct lys_node *)choice, parent);
782
783 /* default */
784 node_metadata_text(choice->dflt->name, "default", parent);
785
786 /* when */
787 node_metadata_when(choice->when, parent);
788
789 /* cases */
790 node_metadata_cases(choice, parent);
791}
792
793static void
794node_metadata_leaf(struct lys_node_leaf *leaf, json_object *parent)
795{
796 json_object *obj;
797 struct lys_node_list *list;
798 int is_key, i;
799
800 /* element type */
801 obj = json_object_new_string("leaf");
802 json_object_object_add(parent, "eltype", obj);
803
804 /* shared info */
805 node_metadata_basic((struct lys_node *)leaf, parent);
806
807 /* type */
808 node_metadata_type(&leaf->type, leaf->module, parent);
809
810 /* units */
811 node_metadata_text(leaf->units, "units", parent);
812
813 /* default */
814 node_metadata_text(leaf->dflt, "default", parent);
815
816 /* must */
817 node_metadata_must(leaf->must_size, leaf->must, parent);
818
819 /* when */
820 node_metadata_when(leaf->when, parent);
821
822 /* iskey */
823 is_key = 0;
824 list = (struct lys_node_list *)lys_parent((struct lys_node *)leaf);
825 if (list && (list->nodetype == LYS_LIST)) {
826 for (i = 0; i < list->keys_size; ++i) {
827 if (list->keys[i] == leaf) {
828 is_key = 1;
829 break;
830 }
831 }
832 }
833 obj = json_object_new_boolean(is_key);
834 json_object_object_add(parent, "iskey", obj);
835}
836
837static void
838node_metadata_leaflist(struct lys_node_leaflist *llist, json_object *parent)
839{
840 json_object *obj;
841
842 /* element type */
843 obj = json_object_new_string("leaf-list");
844 json_object_object_add(parent, "eltype", obj);
845
846 /* shared info */
847 node_metadata_basic((struct lys_node *)llist, parent);
848
849 /* type */
850 node_metadata_type(&llist->type, llist->module, parent);
851
852 /* units */
853 node_metadata_text(llist->units, "units", parent);
854
855 /* must */
856 node_metadata_must(llist->must_size, llist->must, parent);
857
858 /* when */
859 node_metadata_when(llist->when, parent);
860
861 /* min/max-elements */
862 node_metadata_min_max(llist->min, llist->max, parent);
863}
864
865static void
866node_metadata_list(struct lys_node_list *list, json_object *parent)
867{
868 json_object *obj, *array;
869 int i;
870 unsigned int j;
871
872 /* element type */
873 obj = json_object_new_string("list");
874 json_object_object_add(parent, "eltype", obj);
875
876 /* shared info */
877 node_metadata_basic((struct lys_node *)list, parent);
878
879 /* must */
880 node_metadata_must(list->must_size, list->must, parent);
881
882 /* when */
883 node_metadata_when(list->when, parent);
884
885 /* min/max-elements */
886 node_metadata_min_max(list->min, list->max, parent);
887
888 /* keys */
889 if (list->keys_size) {
890 array = json_object_new_array();
891 for (i = 0; i < list->keys_size; ++i) {
892 obj = json_object_new_string(list->keys[i]->name);
893 json_object_array_add(array, obj);
894 }
895 json_object_object_add(parent, "keys", array);
896 }
897
898 /* unique */
899 if (list->unique_size) {
900 array = json_object_new_array();
901 for (i = 0; i < list->unique_size; ++i) {
902 for (j = 0; j < list->unique[i].expr_size; ++j) {
903 obj = json_object_new_string(list->unique[i].expr[j]);
904 json_object_array_add(array, obj);
905 }
906 }
907 json_object_object_add(parent, "unique", array);
908 }
909}
910
911static void
912node_metadata_anyxml(struct lys_node_anyxml *anyxml, json_object *parent)
913{
914 json_object *obj;
915
916 /* element type */
917 obj = json_object_new_string("anyxml");
918 json_object_object_add(parent, "eltype", obj);
919
920 /* shared info */
921 node_metadata_basic((struct lys_node *)anyxml, parent);
922
923 /* must */
924 node_metadata_must(anyxml->must_size, anyxml->must, parent);
925
926 /* when */
927 node_metadata_when(anyxml->when, parent);
928
929}
930
931static void
932node_metadata_case(struct lys_node_case *cas, json_object *parent)
933{
934 json_object *obj;
935
936 /* element type */
937 obj = json_object_new_string("case");
938 json_object_object_add(parent, "eltype", obj);
939
940 /* shared info */
941 node_metadata_basic((struct lys_node *)cas, parent);
942
943 /* when */
944 node_metadata_when(cas->when, parent);
945}
946
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200947/**
948 * \defgroup netconf_operations NETCONF operations
949 * The list of NETCONF operations that mod_netconf supports.
950 * @{
951 */
952
953/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200954 * \brief Send RPC and wait for reply with timeout.
955 *
956 * \param[in] session libnetconf session
957 * \param[in] rpc prepared RPC message
958 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
959 * \param[out] reply reply from the server
960 * \return Value from nc_session_recv_reply() or NC_MSG_UNKNOWN when send_rpc() fails.
961 * On success, it returns NC_MSG_REPLY.
962 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100963NC_MSG_TYPE
964netconf_send_recv_timed(struct nc_session *session, nc_rpc *rpc, int timeout, nc_reply **reply)
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200965{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100966 const nc_msgid msgid = NULL;
967 NC_MSG_TYPE ret = NC_MSG_UNKNOWN;
968 msgid = nc_session_send_rpc(session, rpc);
969 if (msgid == NULL) {
970 return ret;
971 }
972 do {
973 ret = nc_session_recv_reply(session, timeout, reply);
974 if (ret == NC_MSG_HELLO) {
975 ERROR("<hello> received instead reply, it will be lost.");
976 nc_reply_free(*reply);
977 }
978 if (ret == NC_MSG_WOULDBLOCK) {
979 ERROR("Timeout for receiving RPC reply expired.");
980 break;
981 }
982 } while (ret == NC_MSG_HELLO || ret == NC_MSG_NOTIFICATION);
983 return ret;
984}
985
986static int
987ctx_download_module(struct session_with_mutex *session, const char *model_name, const char *revision, const char *schema_dir)
988{
989 json_object *err = NULL;
990 char *model_data = NULL, *model_path;
991 size_t length;
992 FILE *file;
993
994 DEBUG("UNLOCK rwlock %s", __func__);
995 if (pthread_rwlock_unlock(&session_lock) != 0) {
996 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
997 return 1;
998 }
999
1000 model_data = netconf_getschema(session->session_key, model_name, revision, "yin", &err);
1001
1002 DEBUG("LOCK rwlock %s", __func__);
1003 if (pthread_rwlock_wrlock(&session_lock) != 0) {
1004 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1005 return 1;
1006 }
1007
1008 if (!model_data) {
1009 if (err) {
1010 json_object_put(err);
1011 }
1012 ERROR("Failed to get-schema of \"%s\".", model_name);
1013 return 1;
1014 }
1015
1016 if (revision) {
1017 asprintf(&model_path, "%s/%s@%s.yin", schema_dir, model_name, revision);
1018 } else {
1019 asprintf(&model_path, "%s/%s.yin", schema_dir, model_name);
1020 }
1021
1022 file = fopen(model_path, "w");
1023 if (!file) {
1024 ERROR("Failed to open \"%s\" for writing (%s).", model_path, strerror(errno));
1025 free(model_data);
1026 free(model_path);
1027 return 1;
1028 }
1029 free(model_path);
1030
1031 length = strlen(model_data);
1032 if (fwrite(model_data, 1, length, file) < length) {
1033 ERROR("Failed to store the model \"%s\".", model_name);
1034 free(model_data);
1035 fclose(file);
1036 return 1;
1037 }
1038
1039 free(model_data);
1040 fclose(file);
1041 return 0;
1042}
1043
1044static void
1045ctx_enable_features(struct lys_module *module, const char *cpblt)
1046{
1047 char *ptr, *ptr2, *features = NULL;
1048
1049 /* parse features */
1050 ptr = strstr(cpblt, "features=");
1051 if (ptr) {
1052 ptr += 9;
1053 ptr2 = strchr(ptr, '&');
1054 if (!ptr2) {
1055 ptr2 = ptr + strlen(ptr);
1056 }
1057 features = strndup(ptr, ptr2 - ptr);
1058 }
1059
1060 /* enable features */
1061 if (features) {
1062 /* basically manual strtok_r (to avoid macro) */
1063 ptr2 = features;
1064 for (ptr = features; *ptr; ++ptr) {
1065 if (*ptr == ',') {
1066 *ptr = '\0';
1067 /* remember last feature */
1068 ptr2 = ptr + 1;
1069 }
1070 }
1071
1072 ptr = features;
1073 lys_features_enable(module, ptr);
1074 while (ptr != ptr2) {
1075 ptr += strlen(ptr) + 1;
1076 lys_features_enable(module, ptr);
1077 }
1078
1079 free(features);
1080 }
1081}
1082
1083static void
1084ctx_enable_capabs(struct lys_module *ietfnc, json_object *cpb_array)
1085{
1086 json_object *item;
1087 int i;
1088 const char *capab;
1089
1090 /* set supported capabilities from ietf-netconf */
1091 for (i = 0; i < json_object_array_length(cpb_array); ++i) {
1092 item = json_object_array_get_idx(cpb_array, i);
1093 capab = json_object_get_string(item);
1094
1095 if (!strncmp(capab, "urn:ietf:params:netconf:capability:", 35)) {
1096 if (!strncmp(capab, "writable-running", 16)) {
1097 lys_features_enable(ietfnc, "writable-running");
1098 } else if (!strncmp(capab, "candidate", 9)) {
1099 lys_features_enable(ietfnc, "candidate");
1100 } else if (!strcmp(capab, "confirmed-commit:1.1")) {
1101 lys_features_enable(ietfnc, "confirmed-commit");
1102 } else if (!strncmp(capab, "rollback-on-error", 17)) {
1103 lys_features_enable(ietfnc, "rollback-on-error");
1104 } else if (!strcmp(capab, "validate:1.1")) {
1105 lys_features_enable(ietfnc, "validate");
1106 } else if (!strncmp(capab, "startup", 7)) {
1107 lys_features_enable(ietfnc, "startup");
1108 } else if (!strncmp(capab, "url", 3)) {
1109 lys_features_enable(ietfnc, "url");
1110 } else if (!strncmp(capab, "xpath", 5)) {
1111 lys_features_enable(ietfnc, "xpath");
1112 }
1113 }
1114 }
1115}
1116
1117static int
1118prepare_context(struct session_with_mutex *session)
1119{
1120 struct lys_module *module;
1121 json_object *array, *item;
1122 char *ptr, *ptr2;
1123 char *model_name = NULL, *revision = NULL;
1124 const char *capab;
1125 int i, get_schema_support;
1126
1127 if (json_object_object_get_ex(session->hello_message, "capabilities", &array) == FALSE) {
1128 return 1;
1129 }
1130
1131 get_schema_support = 0;
1132 for (i = 0; i < json_object_array_length(array); ++i) {
1133 item = json_object_array_get_idx(array, i);
1134 capab = json_object_get_string(item);
1135
1136 if (!strncmp(capab, "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", 51)) {
1137 get_schema_support = 1;
1138 break;
1139 }
1140 }
1141
1142 if (get_schema_support) {
1143 errno = 0;
1144 if (eaccess(SCHEMA_DIR, W_OK)) {
1145 if (errno == ENOENT) {
1146 if (mkdir(SCHEMA_DIR, 00755)) {
1147 ERROR("Failed to create temp model dir \"%s\" (%s).", SCHEMA_DIR, strerror(errno));
1148 return 1;
1149 }
1150 } else {
1151 ERROR("Unable to write to temp model dir \"%s\" (%s).", SCHEMA_DIR, strerror(errno));
1152 return 1;
1153 }
1154 }
1155
1156 session->ctx = ly_ctx_new(SCHEMA_DIR);
1157 } else {
1158 /* TODO */
1159 session->ctx = ly_ctx_new(NULL);
1160 }
1161
1162loop:
1163 /* download all the models first or load them directly */
1164 for (i = 0; i < json_object_array_length(array); ++i) {
1165 item = json_object_array_get_idx(array, i);
1166 capab = json_object_get_string(item);
1167 if (!strncmp(capab, "urn:ietf:params:netconf:capability", 34)
1168 || !strncmp(capab, "urn:ietf:params:netconf:base", 28)) {
1169 continue;
1170 }
1171
1172 /* get module */
1173 ptr = strstr(capab, "module=");
1174 if (!ptr) {
1175 ERROR("Unknown capability \"%s\" could not be parsed.", capab);
1176 continue;
1177 }
1178 ptr += 7;
1179 ptr2 = strchr(ptr, '&');
1180 if (!ptr2) {
1181 ptr2 = ptr + strlen(ptr);
1182 }
1183 model_name = strndup(ptr, ptr2 - ptr);
1184
1185 /* get revision */
1186 ptr = strstr(capab, "revision=");
1187 if (ptr) {
1188 ptr += 9;
1189 ptr2 = strchr(ptr, '&');
1190 if (!ptr2) {
1191 ptr2 = ptr + strlen(ptr);
1192 }
1193 revision = strndup(ptr, ptr2 - ptr);
1194 }
1195
1196 if (get_schema_support) {
1197 ctx_download_module(session, model_name, revision, SCHEMA_DIR);
1198 } else {
1199 module = ly_ctx_get_module(session->ctx, model_name, revision);
1200 if (!module) {
1201 module = ly_ctx_load_module(session->ctx, NULL, model_name, revision);
1202 if (module) {
1203 if (!strcmp(module->name, "ietf-netconf")) {
1204 ctx_enable_capabs(module, array);
1205 } else {
1206 ctx_enable_features(module, capab);
1207 }
1208 }
1209 }
1210 }
1211
1212 free(model_name);
1213 free(revision);
1214 revision = NULL;
1215 }
1216
1217 if (get_schema_support) {
1218 /* we have downloaded all the models, load them now */
1219 get_schema_support = 0;
1220 goto loop;
1221 }
1222
1223 return 0;
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001224}
1225
1226/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001227 * \brief Connect to NETCONF server
1228 *
1229 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
1230 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001231static unsigned int
1232netconf_connect(const char *host, const char *port, const char *user, const char *pass, struct nc_cpblts *cpblts)
Radek Krejci469aab82012-07-22 18:42:20 +02001233{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001234 struct nc_session* session = NULL;
1235 struct session_with_mutex *locked_session, *last_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001236
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001237 /* connect to the requested NETCONF server */
1238 password = (char*)pass;
1239 DEBUG("prepare to connect %s@%s:%s", user, host, port);
1240 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
1241 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +02001242
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001243 /* if connected successful, add session to the list */
1244 if (session != NULL) {
1245 if ((locked_session = calloc(1, sizeof(struct session_with_mutex))) == NULL || pthread_mutex_init (&locked_session->lock, NULL) != 0) {
1246 nc_session_free(session);
1247 session = NULL;
1248 free(locked_session);
1249 locked_session = NULL;
1250 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
1251 return 0;
1252 }
1253 locked_session->session = session;
1254 locked_session->last_activity = time(NULL);
1255 locked_session->hello_message = NULL;
1256 locked_session->closed = 0;
1257 pthread_mutex_init(&locked_session->lock, NULL);
1258 DEBUG("Before session_lock");
1259 /* get exclusive access to sessions_list (conns) */
1260 DEBUG("LOCK wrlock %s", __func__);
1261 if (pthread_rwlock_wrlock(&session_lock) != 0) {
1262 nc_session_free(session);
1263 free(locked_session);
1264 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1265 return 0;
1266 }
1267 locked_session->ntfc_subscribed = 0;
1268 DEBUG("Add connection to the list");
1269 if (!netconf_sessions_list) {
Michal Vaskoc3146782015-11-04 14:46:41 +01001270 netconf_sessions_list = locked_session;
1271 } else {
1272 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
1273 last_session->next = locked_session;
1274 locked_session->prev = last_session;
1275 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001276 DEBUG("Before session_unlock");
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001277
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001278 /* no need to lock session, noone can read it while we have wrlock */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001279
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001280 /* store information about session from hello message for future usage */
1281 prepare_status_message(locked_session, session);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001282
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001283 /* create context from the hello message cpabilities */
1284 if (prepare_context(locked_session)) {
1285 nc_session_free(session);
1286 free(locked_session);
1287 DEBUG("UNLOCK wrlock %s", __func__);
1288 pthread_rwlock_unlock(&session_lock);
1289 ERROR("Failed to prepare context");
1290 return 0;
1291 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001292
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001293 DEBUG("NETCONF session established");
1294 locked_session->session_key = session_key_generator;
1295 ++session_key_generator;
1296 if (session_key_generator == UINT_MAX) {
1297 session_key_generator = 1;
1298 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001299
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001300 /* unlock session list */
1301 DEBUG("UNLOCK wrlock %s", __func__);
1302 if (pthread_rwlock_unlock(&session_lock) != 0) {
1303 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1304 }
Radek Krejci469aab82012-07-22 18:42:20 +02001305
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001306 return locked_session->session_key;
1307 }
1308
1309 ERROR("Connection could not be established");
1310 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +02001311}
1312
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001313static int
1314close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +02001315{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001316 DEBUG("lock private lock.");
1317 DEBUG("LOCK mutex %s", __func__);
1318 if (pthread_mutex_lock(&locked_session->lock) != 0) {
1319 ERROR("Error while locking rwlock");
1320 }
1321 locked_session->ntfc_subscribed = 0;
1322 locked_session->closed = 1;
1323 if (locked_session->session != NULL) {
1324 nc_session_free(locked_session->session);
1325 locked_session->session = NULL;
1326 }
1327 DEBUG("session closed.");
1328 DEBUG("unlock private lock.");
1329 DEBUG("UNLOCK mutex %s", __func__);
1330 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
1331 ERROR("Error while locking rwlock");
1332 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001333
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001334 DEBUG("unlock session lock.");
1335 DEBUG("closed session, disabled notif(?), wait 0.5s");
1336 usleep(500000); /* let notification thread stop */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001337
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001338 /* session shouldn't be used by now */
1339 /** \todo free all notifications from queue */
1340 free(locked_session->notifications);
1341 pthread_mutex_destroy(&locked_session->lock);
1342 if (locked_session->hello_message != NULL) {
1343 json_object_put(locked_session->hello_message);
1344 locked_session->hello_message = NULL;
1345 }
1346 locked_session->session = NULL;
1347 ly_ctx_destroy(locked_session->ctx);
1348 free(locked_session);
1349 locked_session = NULL;
1350 DEBUG("NETCONF session closed, everything cleared.");
1351 return (EXIT_SUCCESS);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001352}
1353
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001354static int
1355netconf_close(unsigned int session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +02001356{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001357 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001358
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001359 DEBUG("Session to close: %u", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001360
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001361 /* get exclusive (write) access to sessions_list (conns) */
1362 DEBUG("lock session lock.");
1363 DEBUG("LOCK wrlock %s", __func__);
1364 if (pthread_rwlock_wrlock (&session_lock) != 0) {
1365 ERROR("Error while locking rwlock");
1366 (*reply) = create_error_reply("Internal: Error while locking.");
1367 return EXIT_FAILURE;
1368 }
1369 /* remove session from the active sessions list -> nobody new can now work with session */
1370 for (locked_session = netconf_sessions_list;
1371 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01001372 locked_session = locked_session->next);
1373
1374 if (!locked_session) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001375 ERROR("Could not find the session %u to close.", session_key);
1376 (*reply) = create_error_reply("Internal: Error while finding a session.");
Michal Vaskoc3146782015-11-04 14:46:41 +01001377 return EXIT_FAILURE;
1378 }
1379
1380 if (!locked_session->prev) {
1381 netconf_sessions_list = netconf_sessions_list->next;
1382 netconf_sessions_list->prev = NULL;
1383 } else {
1384 locked_session->prev->next = locked_session->next;
1385 if (locked_session->next) {
1386 locked_session->next->prev = locked_session->prev;
1387 }
1388 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001389
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001390 DEBUG("UNLOCK wrlock %s", __func__);
1391 if (pthread_rwlock_unlock (&session_lock) != 0) {
1392 ERROR("Error while unlocking rwlock");
1393 (*reply) = create_error_reply("Internal: Error while unlocking.");
1394 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001395
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001396 if ((locked_session != NULL) && (locked_session->session != NULL)) {
1397 return close_and_free_session(locked_session);
1398 } else {
1399 ERROR("Unknown session to close");
1400 (*reply) = create_error_reply("Internal: Unkown session to close.");
1401 return (EXIT_FAILURE);
1402 }
1403 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +02001404}
1405
Tomas Cejkac7929632013-10-24 19:25:15 +02001406/**
1407 * Test reply message type and return error message.
1408 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001409 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +01001410 * \param[in] session_key session ID, 0 to disable disconnect on error
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001411 * \param[in] msgt RPC-REPLY message type
Tomas Cejkac7929632013-10-24 19:25:15 +02001412 * \param[out] data
1413 * \return NULL on success
1414 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001415json_object *
1416netconf_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 +02001417{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001418 NC_REPLY_TYPE replyt;
1419 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001420
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001421 /* process the result of the operation */
1422 switch (msgt) {
1423 case NC_MSG_UNKNOWN:
1424 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
1425 ERROR("mod_netconf: receiving rpc-reply failed");
1426 if (session_key) {
1427 netconf_close(session_key, &err);
1428 }
1429 if (err != NULL) {
1430 return err;
1431 }
1432 return create_error_reply("Internal: Receiving RPC-REPLY failed.");
1433 }
1434 case NC_MSG_NONE:
1435 /* there is error handled by callback */
1436 if (data != NULL) {
1437 free(*data);
1438 (*data) = NULL;
1439 }
1440 return NULL;
1441 case NC_MSG_REPLY:
1442 switch (replyt = nc_reply_get_type(reply)) {
1443 case NC_REPLY_OK:
1444 if ((data != NULL) && (*data != NULL)) {
1445 free(*data);
1446 (*data) = NULL;
1447 }
1448 return create_ok_reply();
1449 case NC_REPLY_DATA:
1450 if (((*data) = nc_reply_get_data(reply)) == NULL) {
1451 ERROR("mod_netconf: no data from reply");
1452 return create_error_reply("Internal: No data from reply received.");
1453 } else {
1454 return NULL;
1455 }
1456 break;
1457 case NC_REPLY_ERROR:
1458 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
1459 if (data != NULL) {
1460 free(*data);
1461 (*data) = NULL;
1462 }
1463 return create_error_reply(nc_reply_get_errormsg(reply));
1464 default:
1465 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
1466 if (data != NULL) {
1467 free(*data);
1468 (*data) = NULL;
1469 }
1470 return create_error_reply("Unknown type of NETCONF reply.");
1471 }
1472 break;
1473 default:
1474 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
1475 if (data != NULL) {
1476 free(*data);
1477 (*data) = NULL;
1478 }
1479 return create_error_reply("Internal: Unexpected RPC-REPLY message type.");
1480 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001481}
1482
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001483json_object *
1484netconf_unlocked_op(struct nc_session *session, nc_rpc *rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001485{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001486 nc_reply* reply = NULL;
1487 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001488
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001489 /* check requests */
1490 if (rpc == NULL) {
1491 ERROR("mod_netconf: rpc is not created");
1492 return create_error_reply("Internal error: RPC is not created");
1493 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001494
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001495 if (session != NULL) {
1496 /* send the request and get the reply */
1497 msgt = netconf_send_recv_timed(session, rpc, 50000, &reply);
1498 /* process the result of the operation */
1499 return netconf_test_reply(session, 0, msgt, reply, NULL);
1500 } else {
1501 ERROR("Unknown session to process.");
1502 return create_error_reply("Internal error: Unknown session to process.");
1503 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001504}
1505
Tomas Cejkac7929632013-10-24 19:25:15 +02001506/**
1507 * Perform RPC method that returns data.
1508 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001509 * \param[in] session_id session identifier
1510 * \param[in] rpc RPC message to perform
1511 * \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 +02001512 * \return NULL on success, json object with error otherwise
1513 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001514static json_object *
1515netconf_op(unsigned int session_key, nc_rpc *rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +02001516{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001517 struct session_with_mutex * locked_session;
1518 nc_reply* reply = NULL;
1519 json_object *res = NULL;
1520 char *data = NULL;
1521 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +02001522
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001523 /* check requests */
1524 if (rpc == NULL) {
1525 ERROR("mod_netconf: rpc is not created");
1526 res = create_error_reply("Internal: RPC could not be created.");
1527 data = NULL;
1528 goto finished;
1529 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001530
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001531 locked_session = session_get_locked(session_key, &res);
1532 if (!locked_session) {
1533 ERROR("Unknown session or locking failed.");
1534 goto finished;
1535 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001536
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001537 locked_session->last_activity = time(NULL);
Tomas Cejkac7929632013-10-24 19:25:15 +02001538
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001539 /* send the request and get the reply */
1540 msgt = netconf_send_recv_timed(locked_session->session, rpc, 2000000, &reply);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001541
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001542 session_unlock(locked_session);
Tomas Cejkac7929632013-10-24 19:25:15 +02001543
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001544 res = netconf_test_reply(locked_session->session, session_key, msgt, reply, &data);
Radek Krejcia332b692012-11-12 16:15:54 +01001545
Tomas Cejkac7929632013-10-24 19:25:15 +02001546finished:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001547 nc_reply_free(reply);
1548 if (received_data != NULL) {
1549 (*received_data) = data;
1550 } else {
1551 if (data != NULL) {
1552 free(data);
1553 data = NULL;
1554 }
1555 }
1556 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001557}
1558
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001559static char *
1560netconf_getconfig(unsigned int session_key, NC_DATASTORE source, const char *filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001561{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001562 nc_rpc* rpc;
1563 struct nc_filter *f = NULL;
1564 struct session_with_mutex *locked_session;
1565 char* data = NULL, *data_xml;
1566 json_object *res = NULL, *data_cjson;
1567 enum json_tokener_error tok_err;
1568 struct lyd_node *node, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001569
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001570 /* create filter if set */
1571 if (filter != NULL) {
1572 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
1573 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001574
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001575 /* create requests */
1576 rpc = nc_rpc_getconfig(source, f);
1577 nc_filter_free(f);
1578 if (rpc == NULL) {
1579 ERROR("mod_netconf: creating rpc request failed");
1580 return (NULL);
1581 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001582
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001583 /* tell server to show all elements even if they have default values */
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001584#ifdef HAVE_WITHDEFAULTS_TAGGED
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001585 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001586#else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001587 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET))
1588 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001589#endif
Michal Vaskoc3146782015-11-04 14:46:41 +01001590 {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001591 ERROR("mod_netconf: setting withdefaults failed");
1592 }
Tomas Cejka94674662013-09-13 15:55:24 +02001593
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001594 res = netconf_op(session_key, rpc, &data);
1595 nc_rpc_free(rpc);
1596 if (res != NULL) {
1597 (*err) = res;
1598 } else {
1599 (*err) = NULL;
1600 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001601
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001602 if (data) {
1603 for (locked_session = netconf_sessions_list;
1604 locked_session && (locked_session->session_key != session_key);
1605 locked_session = locked_session->next);
1606 /* won't fail */
1607
1608 asprintf(&data_xml, "<get-config>%s</get-config>", data);
1609 node = lyd_parse(locked_session->ctx, data_xml, LYD_XML, LYD_OPT_GETCONFIG | (strict ? LYD_OPT_STRICT : 0));
1610 free(data_xml);
1611 free(data);
1612 if (!node) {
1613 ERROR("Parsing <get-config> data failed.");
1614 return NULL;
1615 }
1616
1617 /* replace XML data with JSON data */
1618 if (lyd_print_mem(&data, node, LYD_JSON)) {
1619 ERROR("Printing JSON <get-config> data failed.");
1620 LY_TREE_FOR(node, sibling) {
1621 lyd_free(sibling);
1622 }
1623 return NULL;
1624 }
1625
1626 /* parse JSON data into cjson */
1627 pthread_mutex_lock(&json_lock);
1628 data_cjson = json_tokener_parse_verbose(data, &tok_err);
1629 if (!data_cjson) {
1630 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1631 pthread_mutex_unlock(&json_lock);
1632 LY_TREE_FOR(node, sibling) {
1633 lyd_free(sibling);
1634 }
1635 free(data);
1636 return NULL;
1637 }
1638 free(data);
1639
1640 /* go simultaneously through both trees and add metadata */
1641 LY_TREE_FOR_SAFE(node, next, sibling) {
1642 node_add_metadata_recursive(sibling, NULL, data_cjson);
1643 lyd_free(sibling);
1644 }
1645
1646 data = strdup(json_object_to_json_string_ext(data_cjson, 0));
1647 json_object_put(data_cjson);
1648 pthread_mutex_unlock(&json_lock);
1649 }
1650
1651 return (data);
Radek Krejci8e4632a2012-07-26 13:40:34 +02001652}
1653
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001654static char *
1655netconf_getschema(unsigned int session_key, const char *identifier, const char *version, const char *format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001656{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001657 nc_rpc* rpc;
1658 char* data = NULL;
1659 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001660
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001661 /* create requests */
1662 rpc = nc_rpc_getschema(identifier, version, format);
1663 if (rpc == NULL) {
1664 ERROR("mod_netconf: creating rpc request failed");
1665 return (NULL);
1666 }
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001667
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001668 res = netconf_op(session_key, rpc, &data);
1669 nc_rpc_free (rpc);
1670 if (res != NULL) {
1671 (*err) = res;
1672 } else {
1673 (*err) = NULL;
1674 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001675
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001676 return (data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001677}
1678
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001679static char *
1680netconf_get(unsigned int session_key, const char* filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001681{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001682 nc_rpc* rpc;
1683 struct nc_filter *f = NULL;
1684 char* data = NULL, *data_xml;
1685 json_object *res = NULL, *data_cjson;
1686 enum json_tokener_error tok_err;
1687 struct session_with_mutex *locked_session;
1688 struct lyd_node *node, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001689
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001690 /* create filter if set */
1691 if (filter != NULL) {
1692 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
1693 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001694
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001695 /* create requests */
1696 rpc = nc_rpc_get(f);
1697 nc_filter_free(f);
1698 if (rpc == NULL) {
1699 ERROR("mod_netconf: creating rpc request failed");
1700 return (NULL);
1701 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001702
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001703 /* tell server to show all elements even if they have default values */
1704 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
1705 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
1706 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
1707 ERROR("mod_netconf: setting withdefaults failed");
1708 }
Tomas Cejka94674662013-09-13 15:55:24 +02001709
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001710 res = netconf_op(session_key, rpc, &data);
1711 nc_rpc_free(rpc);
1712 if (res != NULL) {
1713 (*err) = res;
1714 } else {
1715 (*err) = NULL;
1716 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001717
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001718 if (data) {
1719 for (locked_session = netconf_sessions_list;
1720 locked_session && (locked_session->session_key != session_key);
1721 locked_session = locked_session->next);
1722 /* won't fail */
1723
1724 asprintf(&data_xml, "<get>%s</get>", data);
1725 node = lyd_parse(locked_session->ctx, data_xml, LYD_XML, LYD_OPT_GET | (strict ? LYD_OPT_STRICT : 0));
1726 free(data_xml);
1727 free(data);
1728 if (!node) {
1729 ERROR("Parsing <get> data failed.");
1730 return NULL;
1731 }
1732
1733 /* replace XML data with JSON data */
1734 if (lyd_print_mem(&data, node, LYD_JSON)) {
1735 ERROR("Printing JSON <get> data failed.");
1736 LY_TREE_FOR(node, sibling) {
1737 lyd_free(sibling);
1738 }
1739 return NULL;
1740 }
1741
1742 /* parse JSON data into cjson */
1743 pthread_mutex_lock(&json_lock);
1744 data_cjson = json_tokener_parse_verbose(data, &tok_err);
1745 if (!data_cjson) {
1746 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1747 pthread_mutex_unlock(&json_lock);
1748 LY_TREE_FOR(node, sibling) {
1749 lyd_free(sibling);
1750 }
1751 free(data);
1752 return NULL;
1753 }
1754 free(data);
1755
1756 /* go simultaneously through both trees and add metadata */
1757 LY_TREE_FOR_SAFE(node, next, sibling) {
1758 node_add_metadata_recursive(sibling, NULL, data_cjson);
1759 lyd_free(sibling);
1760 }
1761
1762 data = strdup(json_object_to_json_string_ext(data_cjson, 0));
1763 json_object_put(data_cjson);
1764 pthread_mutex_unlock(&json_lock);
1765 }
1766
1767 return data;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001768}
1769
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001770static json_object *
1771netconf_copyconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, const char *config,
1772 const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001773{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001774 nc_rpc* rpc;
1775 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001776
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001777 /* create requests */
1778 if (source == NC_DATASTORE_CONFIG) {
1779 if (target == NC_DATASTORE_URL) {
1780 /* config, url */
1781 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
1782 } else {
1783 /* config, datastore */
1784 rpc = nc_rpc_copyconfig(source, target, config);
1785 }
1786 } else if (source == NC_DATASTORE_URL) {
1787 if (target == NC_DATASTORE_URL) {
1788 /* url, url */
1789 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
1790 } else {
1791 /* url, datastore */
1792 rpc = nc_rpc_copyconfig(source, target, uri_src);
1793 }
1794 } else {
1795 if (target == NC_DATASTORE_URL) {
1796 /* datastore, url */
1797 rpc = nc_rpc_copyconfig(source, target, uri_trg);
1798 } else {
1799 /* datastore, datastore */
1800 rpc = nc_rpc_copyconfig(source, target);
1801 }
1802 }
1803 if (rpc == NULL) {
1804 ERROR("mod_netconf: creating rpc request failed");
1805 return create_error_reply("Internal: Creating rpc request failed");
1806 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001807
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001808 res = netconf_op(session_key, rpc, NULL);
1809 nc_rpc_free(rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001810
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001811 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001812}
Radek Krejci035bf4e2012-07-25 10:59:09 +02001813
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001814static json_object *
1815netconf_editconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, NC_EDIT_DEFOP_TYPE defop,
1816 NC_EDIT_ERROPT_TYPE erropt, NC_EDIT_TESTOPT_TYPE testopt, const char *config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +02001817{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001818 nc_rpc* rpc;
1819 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001820
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001821 /* create requests */
1822 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
1823 if (rpc == NULL) {
1824 ERROR("mod_netconf: creating rpc request failed");
1825 return create_error_reply("Internal: Creating rpc request failed");
1826 }
Radek Krejci62ab34b2012-07-26 13:42:05 +02001827
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001828 res = netconf_op(session_key, rpc, NULL);
1829 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001830
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001831 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001832}
1833
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001834static json_object *
1835netconf_killsession(unsigned int session_key, const char *sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001836{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001837 nc_rpc *rpc;
1838 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001839
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001840 /* create requests */
1841 rpc = nc_rpc_killsession(sid);
1842 if (rpc == NULL) {
1843 ERROR("mod_netconf: creating rpc request failed");
1844 return create_error_reply("Internal: Creating rpc request failed");
1845 }
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001846
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001847 res = netconf_op(session_key, rpc, NULL);
1848 nc_rpc_free(rpc);
1849 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001850}
1851
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001852static json_object *
1853netconf_onlytargetop(unsigned int session_key, NC_DATASTORE target, nc_rpc *(*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +02001854{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001855 nc_rpc* rpc;
1856 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +02001857
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001858 /* create requests */
1859 rpc = op_func(target);
1860 if (rpc == NULL) {
1861 ERROR("mod_netconf: creating rpc request failed");
1862 return create_error_reply("Internal: Creating rpc request failed");
1863 }
Radek Krejci2f318372012-07-26 14:22:35 +02001864
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001865 res = netconf_op(session_key, rpc, NULL);
1866 nc_rpc_free (rpc);
1867 return res;
Radek Krejci2f318372012-07-26 14:22:35 +02001868}
1869
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001870static json_object *
1871netconf_deleteconfig(unsigned int session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001872{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001873 nc_rpc *rpc = NULL;
1874 json_object *res = NULL;
1875 if (target != NC_DATASTORE_URL) {
1876 rpc = nc_rpc_deleteconfig(target);
1877 } else {
1878 rpc = nc_rpc_deleteconfig(target, url);
1879 }
1880 if (rpc == NULL) {
1881 ERROR("mod_netconf: creating rpc request failed");
1882 return create_error_reply("Internal: Creating rpc request failed");
1883 }
Tomas Cejka404d37e2013-04-13 02:31:35 +02001884
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001885 res = netconf_op(session_key, rpc, NULL);
1886 nc_rpc_free (rpc);
1887 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +02001888}
1889
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001890static json_object *
1891netconf_lock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001892{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001893 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001894}
1895
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001896static json_object *
1897netconf_unlock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001898{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001899 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001900}
1901
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001902static json_object *
1903netconf_generic(unsigned int session_key, const char *content, char **data)
Radek Krejci80c10d92012-07-30 08:38:50 +02001904{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001905 nc_rpc* rpc = NULL;
1906 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +02001907
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001908 /* create requests */
1909 rpc = nc_rpc_generic(content);
1910 if (rpc == NULL) {
1911 ERROR("mod_netconf: creating rpc request failed");
1912 return create_error_reply("Internal: Creating rpc request failed");
1913 }
Radek Krejci80c10d92012-07-30 08:38:50 +02001914
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001915 if (data != NULL) {
1916 // TODO ?free(*data);
1917 (*data) = NULL;
1918 }
Radek Krejci80c10d92012-07-30 08:38:50 +02001919
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001920 /* get session where send the RPC */
1921 res = netconf_op(session_key, rpc, data);
1922 nc_rpc_free (rpc);
1923 return res;
1924}
1925
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001926static int
1927node_add_metadata(struct lys_node *node, struct lys_module *module, json_object *parent)
1928{
1929 struct lys_module *cur_module;
1930 json_object *meta_obj;
1931 char *obj_name;
1932
1933 cur_module = node->module;
1934 if (cur_module->type) {
1935 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1936 }
1937 if (cur_module == module) {
1938 asprintf(&obj_name, "$@%s", node->name);
1939 } else {
1940 asprintf(&obj_name, "$@%s:%s", cur_module->name, node->name);
1941 }
1942
1943 /* in (leaf-)lists the metadata could have already been added */
1944 if (json_object_object_get_ex(parent, obj_name, NULL) == TRUE) {
1945 free(obj_name);
1946 return 1;
1947 }
1948
1949 meta_obj = json_object_new_object();
1950
1951 switch (node->nodetype) {
1952 case LYS_CONTAINER:
1953 node_metadata_container((struct lys_node_container *)node, meta_obj);
1954 break;
Michal Vasko3fda9a92015-11-23 10:10:57 +01001955 case LYS_CHOICE:
1956 node_metadata_choice((struct lys_node_choice *)node, meta_obj);
1957 break;
1958 case LYS_LEAF:
1959 node_metadata_leaf((struct lys_node_leaf *)node, meta_obj);
1960 break;
1961 case LYS_LEAFLIST:
1962 node_metadata_leaflist((struct lys_node_leaflist *)node, meta_obj);
1963 break;
1964 case LYS_LIST:
1965 node_metadata_list((struct lys_node_list *)node, meta_obj);
1966 break;
1967 case LYS_ANYXML:
1968 node_metadata_anyxml((struct lys_node_anyxml *)node, meta_obj);
1969 break;
1970 case LYS_CASE:
1971 node_metadata_case((struct lys_node_case *)node, meta_obj);
1972 break;
1973 /* TODO
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001974 LYS_INPUT
1975 LYS_OUTPUT
Michal Vasko3fda9a92015-11-23 10:10:57 +01001976 LYS_RPC*/
1977 default:
1978 ERROR("Internal: unuxpected nodetype (%s:%d)", __FILE__, __LINE__);
1979 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001980 }
1981
1982 /* just a precaution */
1983 if (json_object_get_type(parent) != json_type_object) {
1984 ERROR("Internal: wrong JSON type (%s:%d)", __FILE__, __LINE__);
1985 free(obj_name);
1986 return 1;
1987 }
1988
1989 json_object_object_add(parent, obj_name, meta_obj);
1990 free(obj_name);
1991 return 0;
1992}
1993
1994static void
1995node_add_metadata_recursive(struct lyd_node *data_tree, struct lys_module *module, json_object *data_json_parent)
1996{
1997 struct lys_module *cur_module;
1998 struct lys_node *list_schema;
1999 struct lyd_node *child, *list_item;
2000 json_object *child_json, *list_child_json;
2001 char *child_name;
2002 int list_idx;
2003
2004 /* add data_tree metadata */
2005 if (node_add_metadata(data_tree->schema, module, data_json_parent)) {
2006 return;
2007 }
2008
2009 /* get data_tree module */
2010 cur_module = data_tree->schema->module;
2011 if (cur_module->type) {
2012 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
2013 }
2014
2015 if (!(data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
2016 /* print correct data_tree JSON name */
2017 if (cur_module == module) {
2018 asprintf(&child_name, "%s", data_tree->schema->name);
2019 } else {
2020 asprintf(&child_name, "%s:%s", cur_module->name, data_tree->schema->name);
2021 }
2022
2023 /* go down in JSON object */
2024 if (json_object_object_get_ex(data_json_parent, child_name, &child_json) == FALSE) {
2025 ERROR("Internal: failed to get JSON object \"%s\".", child_name);
2026 free(child_name);
2027 return;
2028 }
2029 free(child_name);
2030
2031 if (data_tree->schema->nodetype == LYS_LIST) {
2032 if (json_object_get_type(child_json) != json_type_array) {
2033 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
2034 return;
2035 }
2036 /* go down in data tree for every item, we process them all now, skip later
2037 * (metadata duplicate will be detected at the beginning of this function) */
2038 list_idx = 0;
2039 list_schema = data_tree->schema;
2040
2041 LY_TREE_FOR(data_tree, list_item) {
2042 /* another list member */
2043 if (list_item->schema == list_schema) {
2044 list_child_json = json_object_array_get_idx(child_json, list_idx);
2045 if (!list_child_json) {
2046 ERROR("Internal: list \"%s\" idx out-of-bounds", list_schema->name);
2047 return;
2048 }
2049 LY_TREE_FOR(list_item->child, child) {
2050 node_add_metadata_recursive(child, cur_module, list_child_json);
2051 }
2052
2053 ++list_idx;
2054 }
2055 }
2056 } else {
2057 if (json_object_get_type(child_json) != json_type_object) {
2058 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
2059 return;
2060 }
2061 /* go down in data tree */
2062 LY_TREE_FOR(data_tree->child, child) {
2063 node_add_metadata_recursive(child, cur_module, child_json);
2064 }
2065 }
2066 }
2067}
2068
2069static void
2070node_add_children_with_metadata_recursive(struct lys_node *node, struct lys_module *module, json_object *parent)
2071{
2072 struct lys_module *cur_module;
2073 struct lys_node *child;
2074 json_object *node_json;
2075 char *json_name;
2076
2077 /* add node metadata */
2078 if (node_add_metadata(node, module, parent)) {
2079 ERROR("Internal: metadata duplicate for \"%s\".", node->name);
2080 return;
2081 }
2082
2083 /* get node module */
2084 cur_module = node->module;
2085 if (cur_module->type) {
2086 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
2087 }
2088
2089 /* create JSON object for child metadata */
2090 node_json = json_object_new_object();
2091 if (cur_module == module) {
2092 json_object_object_add(parent, node->name, node_json);
2093 } else {
2094 asprintf(&json_name, "%s:%s", cur_module->name, node->name);
2095 json_object_object_add(parent, json_name, node_json);
2096 free(json_name);
2097 }
2098
2099 LY_TREE_FOR(node->child, child) {
2100 node_add_children_with_metadata_recursive(child, cur_module, node_json);
2101 }
2102}
2103
2104static json_object *
2105libyang_query(unsigned int session_key, const char *filter, int load_children)
2106{
2107 struct lys_node *node;
2108 struct session_with_mutex *locked_session;
2109 json_object *ret = NULL, *data;
2110
2111 locked_session = session_get_locked(session_key, &ret);
2112 if (!locked_session) {
2113 ERROR("Locking failed or session not found.");
2114 goto finish;
2115 }
2116
2117 locked_session->last_activity = time(NULL);
2118
2119 /* collect schema metadata and create reply */
2120 node = ly_ctx_get_node(locked_session->ctx, filter);
2121
2122 if (node) {
2123 pthread_mutex_lock(&json_lock);
2124 data = json_object_new_object();
2125
2126 if (load_children) {
2127 node_add_children_with_metadata_recursive(node, NULL, data);
2128 } else {
2129 node_add_metadata(node, NULL, data);
2130 }
2131
2132 pthread_mutex_unlock(&json_lock);
2133 ret = create_data_reply(json_object_to_json_string(data));
2134 json_object_put(data);
2135 } else {
2136 ret = create_error_reply("Failed to resolve XPath filter node.");
2137 }
2138
2139 session_unlock(locked_session);
2140
2141finish:
2142 return ret;
2143}
2144
2145static json_object *
2146libyang_merge(unsigned int session_key, const char *config)
2147{
2148 struct lyd_node *data_tree = NULL, *sibling;
2149 struct session_with_mutex *locked_session;
2150 json_object *ret = NULL, *data_json = NULL;
2151 enum json_tokener_error err = 0;
2152
2153 locked_session = session_get_locked(session_key, &ret);
2154 if (!locked_session) {
2155 ERROR("Locking failed or session not found.");
2156 goto finish;
2157 }
2158
2159 locked_session->last_activity = time(NULL);
2160
2161 data_tree = lyd_parse(locked_session->ctx, config, LYD_JSON, LYD_OPT_STRICT);
2162 if (!data_tree) {
2163 ERROR("Creating data tree failed.");
2164 ret = create_error_reply("Failed to create data tree from JSON config.");
2165 session_unlock(locked_session);
2166 goto finish;
2167 }
2168
2169 session_unlock(locked_session);
2170
2171 pthread_mutex_lock(&json_lock);
2172 data_json = json_tokener_parse_verbose(config, &err);
2173 if (!data_json) {
2174 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(err));
2175 pthread_mutex_unlock(&json_lock);
2176 ret = create_error_reply(json_tokener_error_desc(err));
2177 goto finish;
2178 }
2179
2180 /* go simultaneously through both trees and add metadata */
2181 LY_TREE_FOR(data_tree, sibling) {
2182 node_add_metadata_recursive(sibling, NULL, data_json);
2183 }
2184 pthread_mutex_unlock(&json_lock);
2185 ret = create_data_reply(json_object_to_json_string(data_json));
2186
2187finish:
2188 LY_TREE_FOR(data_tree, sibling) {
2189 lyd_free(sibling);
2190 }
2191 json_object_put(data_json);
2192 return ret;
Radek Krejci80c10d92012-07-30 08:38:50 +02002193}
2194
Tomas Cejka0a4bba82013-04-19 11:51:28 +02002195/**
2196 * @}
2197 *//* netconf_operations */
2198
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002199void
2200clb_print(NC_VERB_LEVEL level, const char *msg)
Radek Krejci469aab82012-07-22 18:42:20 +02002201{
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002202#define FOREACH(I) \
Tomas Cejkacf44e522015-04-24 17:29:21 +02002203 I(NC_VERB_ERROR) I(NC_VERB_WARNING)
2204
2205#define CASE(VAL) case VAL: ERROR("%s: %s", #VAL, msg); \
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002206 break;
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002207
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002208 switch (level) {
2209 FOREACH(CASE);
2210 case NC_VERB_VERBOSE:
2211 case NC_VERB_DEBUG:
2212 DEBUG("DEBUG: %s", msg);
2213 break;
2214 }
2215 if (level == NC_VERB_ERROR) {
2216 /* return global error */
2217 netconf_callback_error_process(NULL /* tag */, NULL /* type */,
2218 NULL /* severity */, NULL /* apptag */,
2219 NULL /* path */, msg, NULL /* attribute */,
2220 NULL /* element */, NULL /* ns */, NULL /* sid */);
2221 }
Radek Krejci469aab82012-07-22 18:42:20 +02002222}
2223
Tomas Cejka64b87482013-06-03 16:30:53 +02002224/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02002225 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02002226 * Caller should free message memory.
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002227 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02002228 * \return pointer to message
2229 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002230char *
2231get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02002232{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002233 /* read json in chunked framing */
2234 unsigned int buffer_size = 0;
2235 ssize_t buffer_len = 0;
2236 char *buffer = NULL;
2237 char c;
2238 ssize_t ret;
2239 int i, chunk_len;
2240 char chunk_len_str[12];
Tomas Cejka64b87482013-06-03 16:30:53 +02002241
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002242 while (1) {
2243 /* read chunk length */
2244 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
2245 if (buffer != NULL) {
2246 free (buffer);
2247 buffer = NULL;
2248 }
2249 break;
2250 }
2251 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
2252 if (buffer != NULL) {
2253 free (buffer);
2254 buffer = NULL;
2255 }
2256 break;
2257 }
2258 i=0;
2259 memset (chunk_len_str, 0, 12);
2260 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
2261 if (i==0 && c == '#') {
2262 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
2263 /* end but invalid */
2264 if (buffer != NULL) {
2265 free (buffer);
2266 buffer = NULL;
2267 }
2268 }
2269 /* end of message, double-loop break */
2270 goto msg_complete;
2271 }
2272 chunk_len_str[i++] = c;
2273 if (i==11) {
2274 ERROR("Message is too long, buffer for length is not big enought!!!!");
2275 break;
2276 }
2277 }
2278 if (c != '\n') {
2279 if (buffer != NULL) {
2280 free (buffer);
2281 buffer = NULL;
2282 }
2283 break;
2284 }
2285 chunk_len_str[i] = 0;
2286 if ((chunk_len = atoi (chunk_len_str)) == 0) {
2287 if (buffer != NULL) {
2288 free (buffer);
2289 buffer = NULL;
2290 }
2291 break;
2292 }
2293 buffer_size += chunk_len+1;
2294 buffer = realloc (buffer, sizeof(char)*buffer_size);
2295 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
2296 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
2297 if (buffer != NULL) {
2298 free (buffer);
2299 buffer = NULL;
2300 }
2301 break;
2302 }
2303 buffer_len += ret;
2304 }
Tomas Cejka64b87482013-06-03 16:30:53 +02002305msg_complete:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002306 return buffer;
Tomas Cejka64b87482013-06-03 16:30:53 +02002307}
2308
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002309NC_DATASTORE
2310parse_datastore(const char *ds)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002311{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002312 if (strcmp(ds, "running") == 0) {
2313 return NC_DATASTORE_RUNNING;
2314 } else if (strcmp(ds, "startup") == 0) {
2315 return NC_DATASTORE_STARTUP;
2316 } else if (strcmp(ds, "candidate") == 0) {
2317 return NC_DATASTORE_CANDIDATE;
2318 } else if (strcmp(ds, "url") == 0) {
2319 return NC_DATASTORE_URL;
2320 } else if (strcmp(ds, "config") == 0) {
2321 return NC_DATASTORE_CONFIG;
2322 }
2323 return -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002324}
2325
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002326NC_EDIT_TESTOPT_TYPE
2327parse_testopt(const char *t)
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002328{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002329 if (strcmp(t, "notset") == 0) {
2330 return NC_EDIT_TESTOPT_NOTSET;
2331 } else if (strcmp(t, "testset") == 0) {
2332 return NC_EDIT_TESTOPT_TESTSET;
2333 } else if (strcmp(t, "set") == 0) {
2334 return NC_EDIT_TESTOPT_SET;
2335 } else if (strcmp(t, "test") == 0) {
2336 return NC_EDIT_TESTOPT_TEST;
2337 }
2338 return NC_EDIT_TESTOPT_ERROR;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002339}
2340
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002341json_object *
2342create_error_reply(const char *errmess)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002343{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002344 json_object *reply, *array;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002345
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002346 pthread_mutex_lock(&json_lock);
2347 reply = json_object_new_object();
2348 array = json_object_new_array();
2349 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2350 json_object_array_add(array, json_object_new_string(errmess));
2351 json_object_object_add(reply, "errors", array);
2352 pthread_mutex_unlock(&json_lock);
2353
2354 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002355}
2356
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002357json_object *
2358create_data_reply(const char *data)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002359{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002360 pthread_mutex_lock(&json_lock);
2361 json_object *reply = json_object_new_object();
2362 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
2363 json_object_object_add(reply, "data", json_object_new_string(data));
2364 pthread_mutex_unlock(&json_lock);
2365 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002366}
2367
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002368json_object *
2369create_ok_reply(void)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002370{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002371 pthread_mutex_lock(&json_lock);
2372 json_object *reply = json_object_new_object();
2373 reply = json_object_new_object();
2374 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2375 pthread_mutex_unlock(&json_lock);
2376 return reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002377}
2378
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002379json_object *
2380create_replies(void)
Tomas Cejka09629492014-07-10 15:58:06 +02002381{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002382 json_object *replies;
2383
2384 pthread_mutex_lock(&json_lock);
2385 replies = json_object_new_object();
2386 pthread_mutex_unlock(&json_lock);
2387
2388 return replies;
Tomas Cejka09629492014-07-10 15:58:06 +02002389}
2390
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002391void
2392add_reply(json_object *replies, json_object *reply, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002393{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002394 char *str;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002395
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002396 asprintf(&str, "%u", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002397
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002398 pthread_mutex_lock(&json_lock);
2399 json_object_object_add(replies, str, reply);
2400 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002401
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002402 free(str);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002403}
2404
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002405char *
2406get_param_string(json_object *data, const char *name)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002407{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002408 json_object *js_tmp = NULL;
2409 char *res = NULL;
2410 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
2411 res = strdup(json_object_get_string(js_tmp));
2412 }
2413 return res;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002414}
2415
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002416json_object *
2417handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002418{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002419 char *host = NULL;
2420 char *port = NULL;
2421 char *user = NULL;
2422 char *pass = NULL;
2423 json_object *reply = NULL;
2424 unsigned int session_key = 0;
2425 struct nc_cpblts* cpblts = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002426
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002427 DEBUG("Request: connect");
2428 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002429
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002430 host = get_param_string(request, "host");
2431 port = get_param_string(request, "port");
2432 user = get_param_string(request, "user");
2433 pass = get_param_string(request, "pass");
Tomas Cejkad5b53772013-06-08 23:01:07 +02002434
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002435 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002436
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002437 DEBUG("host: %s, port: %s, user: %s", host, port, user);
2438 if ((host == NULL) || (user == NULL)) {
2439 ERROR("Cannot connect - insufficient input.");
2440 session_key = 0;
2441 } else {
2442 session_key = netconf_connect(host, port, user, pass, cpblts);
2443 DEBUG("Session key: %u", session_key);
2444 }
2445 if (cpblts != NULL) {
2446 nc_cpblts_free(cpblts);
2447 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002448
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002449 GETSPEC_ERR_REPLY
2450
2451 pthread_mutex_lock(&json_lock);
2452 if (session_key == 0) {
2453 /* negative reply */
2454 if (err_reply == NULL) {
2455 reply = json_object_new_object();
2456 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2457 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
2458 ERROR("Connection failed.");
2459 } else {
2460 /* use filled err_reply from libnetconf's callback */
2461 reply = err_reply;
2462 ERROR("Connect - error from libnetconf's callback.");
2463 }
2464 } else {
2465 /* positive reply */
2466 reply = json_object_new_object();
2467 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2468 json_object_object_add(reply, "session", json_object_new_int(session_key));
2469 }
2470 memset(pass, 0, strlen(pass));
2471 pthread_mutex_unlock(&json_lock);
2472 CHECK_AND_FREE(host);
2473 CHECK_AND_FREE(user);
2474 CHECK_AND_FREE(port);
2475 CHECK_AND_FREE(pass);
2476 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002477}
2478
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002479json_object *
2480handle_op_disconnect(json_object *UNUSED(request), unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002481{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002482 json_object *reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002483
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002484 DEBUG("Request: disconnect (session %u)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002485
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002486 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
2487 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
2488 } else {
2489 reply = create_ok_reply();
2490 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002491
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002492 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002493}
2494
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002495json_object *
2496handle_op_get(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002497{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002498 char *filter = NULL;
2499 char *data = NULL;
2500 json_object *reply = NULL, *obj;
2501 int strict;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002502
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002503 DEBUG("Request: get (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002504
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002505 pthread_mutex_lock(&json_lock);
2506 filter = get_param_string(request, "filter");
2507 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2508 pthread_mutex_unlock(&json_lock);
2509 reply = create_error_reply("Missing strict parameter.");
2510 return reply;
2511 }
2512 strict = json_object_get_boolean(obj);
2513 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002514
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002515 if ((data = netconf_get(session_key, filter, strict, &reply)) == NULL) {
2516 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
2517 } else {
2518 reply = create_data_reply(data);
2519 free(data);
2520 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002521
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002522 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002523}
2524
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002525json_object *
2526handle_op_getconfig(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002527{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002528 NC_DATASTORE ds_type_s = -1;
2529 char *filter = NULL;
2530 char *data = NULL;
2531 char *source = NULL;
2532 json_object *reply = NULL, *obj;
2533 int strict;
Tomas Cejkab4d05872014-02-14 22:44:38 +01002534
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002535 DEBUG("Request: get-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002536
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002537 pthread_mutex_lock(&json_lock);
2538 filter = get_param_string(request, "filter");
2539 source = get_param_string(request, "source");
2540 if (source != NULL) {
2541 ds_type_s = parse_datastore(source);
2542 }
2543 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2544 pthread_mutex_unlock(&json_lock);
2545 reply = create_error_reply("Missing strict parameter.");
2546 return reply;
2547 }
2548 strict = json_object_get_boolean(obj);
2549 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002550
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002551 if ((int)ds_type_s == -1) {
2552 reply = create_error_reply("Invalid source repository type requested.");
2553 goto finalize;
2554 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002555
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002556 if ((data = netconf_getconfig(session_key, ds_type_s, filter, strict, &reply)) == NULL) {
2557 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
2558 } else {
2559 reply = create_data_reply(data);
2560 free(data);
2561 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002562
Tomas Cejka09629492014-07-10 15:58:06 +02002563finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002564 CHECK_AND_FREE(filter);
2565 CHECK_AND_FREE(source);
2566 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002567}
2568
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002569json_object *
2570handle_op_editconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002571{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002572 NC_DATASTORE ds_type_s = -1;
2573 NC_DATASTORE ds_type_t = -1;
2574 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
2575 NC_EDIT_ERROPT_TYPE erropt_type = 0;
2576 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
2577 char *defop = NULL;
2578 char *erropt = NULL;
2579 char *config = NULL;
2580 char *source = NULL;
2581 char *target = NULL;
2582 char *testopt = NULL;
2583 char *urisource = NULL;
2584 json_object *reply = NULL, *configs, *obj;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002585
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002586 DEBUG("Request: edit-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002587
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002588 pthread_mutex_lock(&json_lock);
2589 /* get parameters */
2590 target = get_param_string(request, "target");
2591 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2592 pthread_mutex_unlock(&json_lock);
2593 reply = create_error_reply("Missing configs parameter.");
2594 goto finalize;
2595 }
2596 obj = json_object_array_get_idx(configs, idx);
2597 config = strdup(json_object_get_string(obj));
Tomas Cejkad5b53772013-06-08 23:01:07 +02002598
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002599 source = get_param_string(request, "source");
2600 defop = get_param_string(request, "default-operation");
2601 erropt = get_param_string(request, "error-option");
2602 urisource = get_param_string(request, "uri-source");
2603 testopt = get_param_string(request, "test-option");
2604 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002605
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002606 if (target != NULL) {
2607 ds_type_t = parse_datastore(target);
2608 }
2609 if (source != NULL) {
2610 ds_type_s = parse_datastore(source);
2611 } else {
2612 /* source is optional, default value is config */
2613 ds_type_s = NC_DATASTORE_CONFIG;
2614 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002615
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002616 if (defop != NULL) {
2617 if (strcmp(defop, "merge") == 0) {
2618 defop_type = NC_EDIT_DEFOP_MERGE;
2619 } else if (strcmp(defop, "replace") == 0) {
2620 defop_type = NC_EDIT_DEFOP_REPLACE;
2621 } else if (strcmp(defop, "none") == 0) {
2622 defop_type = NC_EDIT_DEFOP_NONE;
2623 } else {
2624 reply = create_error_reply("Invalid default-operation parameter.");
2625 goto finalize;
2626 }
2627 } else {
2628 defop_type = NC_EDIT_DEFOP_NOTSET;
2629 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002630
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002631 if (erropt != NULL) {
2632 if (strcmp(erropt, "continue-on-error") == 0) {
2633 erropt_type = NC_EDIT_ERROPT_CONT;
2634 } else if (strcmp(erropt, "stop-on-error") == 0) {
2635 erropt_type = NC_EDIT_ERROPT_STOP;
2636 } else if (strcmp(erropt, "rollback-on-error") == 0) {
2637 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
2638 } else {
2639 reply = create_error_reply("Invalid error-option parameter.");
2640 goto finalize;
2641 }
2642 } else {
2643 erropt_type = 0;
2644 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002645
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002646 if ((int)ds_type_t == -1) {
2647 reply = create_error_reply("Invalid target repository type requested.");
2648 goto finalize;
2649 }
2650 if (ds_type_s == NC_DATASTORE_CONFIG) {
2651 if (config == NULL) {
2652 reply = create_error_reply("Invalid config data parameter.");
2653 goto finalize;
2654 }
2655 } else if (ds_type_s == NC_DATASTORE_URL){
2656 if (urisource == NULL) {
2657 reply = create_error_reply("Invalid uri-source parameter.");
2658 goto finalize;
2659 }
2660 config = urisource;
2661 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002662
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002663 if (testopt != NULL) {
2664 testopt_type = parse_testopt(testopt);
2665 } else {
2666 testopt_type = NC_EDIT_TESTOPT_TESTSET;
2667 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002668
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002669 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 +02002670
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002671 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002672
Tomas Cejka09629492014-07-10 15:58:06 +02002673finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002674 CHECK_AND_FREE(defop);
2675 CHECK_AND_FREE(erropt);
2676 CHECK_AND_FREE(config);
2677 CHECK_AND_FREE(source);
2678 CHECK_AND_FREE(urisource);
2679 CHECK_AND_FREE(target);
2680 CHECK_AND_FREE(testopt);
2681
2682 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002683}
2684
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002685json_object *
2686handle_op_copyconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002687{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002688 NC_DATASTORE ds_type_s = -1;
2689 NC_DATASTORE ds_type_t = -1;
2690 char *config = NULL;
2691 char *target = NULL;
2692 char *source = NULL;
2693 char *uri_src = NULL;
2694 char *uri_trg = NULL;
2695 json_object *reply = NULL, *configs, *obj;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002696
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002697 DEBUG("Request: copy-config (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002698
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002699 /* get parameters */
2700 pthread_mutex_lock(&json_lock);
2701 target = get_param_string(request, "target");
2702 source = get_param_string(request, "source");
2703 uri_src = get_param_string(request, "uri-source");
2704 uri_trg = get_param_string(request, "uri-target");
2705 if (!strcmp(source, "config")) {
2706 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2707 pthread_mutex_unlock(&json_lock);
2708 reply = create_error_reply("Missing configs parameter.");
2709 goto finalize;
2710 }
2711 obj = json_object_array_get_idx(configs, idx);
2712 if (!obj) {
2713 pthread_mutex_unlock(&json_lock);
2714 reply = create_error_reply("Configs array parameter shorter than sessions.");
2715 goto finalize;
2716 }
2717 config = strdup(json_object_get_string(obj));
2718 }
2719 pthread_mutex_unlock(&json_lock);
2720
2721 if (target != NULL) {
2722 ds_type_t = parse_datastore(target);
2723 }
2724 if (source != NULL) {
2725 ds_type_s = parse_datastore(source);
2726 }
2727
2728 if ((int)ds_type_s == -1) {
2729 /* invalid source datastore specified */
2730 reply = create_error_reply("Invalid source repository type requested.");
2731 goto finalize;
2732 }
2733
2734 if ((int)ds_type_t == -1) {
2735 /* invalid target datastore specified */
2736 reply = create_error_reply("Invalid target repository type requested.");
2737 goto finalize;
2738 }
2739
2740 if (ds_type_s == NC_DATASTORE_URL) {
2741 if (uri_src == NULL) {
2742 uri_src = "";
2743 }
2744 }
2745 if (ds_type_t == NC_DATASTORE_URL) {
2746 if (uri_trg == NULL) {
2747 uri_trg = "";
2748 }
2749 }
2750 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
2751
2752 CHECK_ERR_SET_REPLY
2753
2754finalize:
2755 CHECK_AND_FREE(config);
2756 CHECK_AND_FREE(target);
2757 CHECK_AND_FREE(source);
2758 CHECK_AND_FREE(uri_src);
2759 CHECK_AND_FREE(uri_trg);
2760
2761 return reply;
2762}
2763
2764json_object *
2765handle_op_deleteconfig(json_object *request, unsigned int session_key)
2766{
2767 json_object *reply;
2768 NC_DATASTORE ds_type = -1;
2769 char *target, *url;
2770
2771 DEBUG("Request: delete-config (session %u)", session_key);
2772
2773 pthread_mutex_lock(&json_lock);
2774 target = get_param_string(request, "target");
2775 url = get_param_string(request, "url");
2776 pthread_mutex_unlock(&json_lock);
2777
2778 if (target != NULL) {
2779 ds_type = parse_datastore(target);
2780 }
2781 if ((int)ds_type == -1) {
2782 reply = create_error_reply("Invalid target repository type requested.");
2783 goto finalize;
2784 }
2785 if (ds_type == NC_DATASTORE_URL) {
2786 if (!url) {
2787 url = "";
2788 }
2789 }
2790
2791 reply = netconf_deleteconfig(session_key, ds_type, url);
2792
2793 CHECK_ERR_SET_REPLY
2794 if (reply == NULL) {
2795 reply = create_ok_reply();
2796 }
2797
2798finalize:
2799 CHECK_AND_FREE(target);
2800 CHECK_AND_FREE(url);
2801 return reply;
2802}
2803
2804json_object *
2805handle_op_lock(json_object *request, unsigned int session_key)
2806{
2807 json_object *reply;
2808 NC_DATASTORE ds_type = -1;
2809 char *target;
2810
2811 DEBUG("Request: lock (session %u)", session_key);
2812
2813 pthread_mutex_lock(&json_lock);
2814 target = get_param_string(request, "target");
2815 pthread_mutex_unlock(&json_lock);
2816
2817 if (target != NULL) {
2818 ds_type = parse_datastore(target);
2819 }
2820 if ((int)ds_type == -1) {
2821 reply = create_error_reply("Invalid target repository type requested.");
2822 goto finalize;
2823 }
2824
2825 reply = netconf_lock(session_key, ds_type);
2826
2827 CHECK_ERR_SET_REPLY
2828 if (reply == NULL) {
2829 reply = create_ok_reply();
2830 }
2831
2832finalize:
2833 CHECK_AND_FREE(target);
2834 return reply;
2835}
2836
2837json_object *
2838handle_op_unlock(json_object *request, unsigned int session_key)
2839{
2840 json_object *reply;
2841 NC_DATASTORE ds_type = -1;
2842 char *target;
2843
2844 DEBUG("Request: unlock (session %u)", session_key);
2845
2846 pthread_mutex_lock(&json_lock);
2847 target = get_param_string(request, "target");
2848 pthread_mutex_unlock(&json_lock);
2849
2850 if (target != NULL) {
2851 ds_type = parse_datastore(target);
2852 }
2853 if ((int)ds_type == -1) {
2854 reply = create_error_reply("Invalid target repository type requested.");
2855 goto finalize;
2856 }
2857
2858 reply = netconf_unlock(session_key, ds_type);
2859
2860 CHECK_ERR_SET_REPLY
2861 if (reply == NULL) {
2862 reply = create_ok_reply();
2863 }
2864
2865finalize:
2866 CHECK_AND_FREE(target);
2867 return reply;
2868}
2869
2870json_object *
2871handle_op_kill(json_object *request, unsigned int session_key)
2872{
2873 json_object *reply = NULL;
2874 char *sid = NULL;
2875
2876 DEBUG("Request: kill-session (session %u)", session_key);
2877
2878 pthread_mutex_lock(&json_lock);
2879 sid = get_param_string(request, "session-id");
2880 pthread_mutex_unlock(&json_lock);
2881
2882 if (sid == NULL) {
2883 reply = create_error_reply("Missing session-id parameter.");
2884 goto finalize;
2885 }
2886
2887 reply = netconf_killsession(session_key, sid);
2888
2889 CHECK_ERR_SET_REPLY
2890
2891finalize:
2892 CHECK_AND_FREE(sid);
2893 return reply;
2894}
2895
2896json_object *
2897handle_op_info(json_object *UNUSED(request), unsigned int session_key)
2898{
2899 json_object *reply = NULL;
2900 struct session_with_mutex *locked_session = NULL;
2901 DEBUG("Request: get info about session %u", session_key);
2902
2903 DEBUG("LOCK wrlock %s", __func__);
2904 if (pthread_rwlock_rdlock(&session_lock) != 0) {
2905 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2906 }
2907
2908 for (locked_session = netconf_sessions_list;
2909 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002910 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002911 if (locked_session != NULL) {
2912 DEBUG("LOCK mutex %s", __func__);
2913 pthread_mutex_lock(&locked_session->lock);
2914 DEBUG("UNLOCK wrlock %s", __func__);
2915 if (pthread_rwlock_unlock(&session_lock) != 0) {
2916 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2917 }
2918 if (locked_session->hello_message != NULL) {
2919 reply = locked_session->hello_message;
2920 } else {
2921 reply = create_error_reply("Invalid session identifier.");
2922 }
2923 DEBUG("UNLOCK mutex %s", __func__);
2924 pthread_mutex_unlock(&locked_session->lock);
2925 } else {
2926 DEBUG("UNLOCK wrlock %s", __func__);
2927 if (pthread_rwlock_unlock(&session_lock) != 0) {
2928 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2929 }
2930 reply = create_error_reply("Invalid session identifier.");
2931 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002932
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002933 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002934}
2935
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002936json_object *
2937handle_op_generic(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002938{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002939 json_object *reply = NULL, *contents, *obj;
2940 char *config = NULL;
2941 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002942
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002943 DEBUG("Request: generic request (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002944
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002945 pthread_mutex_lock(&json_lock);
2946 if (json_object_object_get_ex(request, "contents", &contents) == FALSE) {
2947 pthread_mutex_unlock(&json_lock);
2948 reply = create_error_reply("Missing contents parameter.");
2949 goto finalize;
2950 }
2951 obj = json_object_array_get_idx(contents, idx);
2952 if (!obj) {
2953 pthread_mutex_unlock(&json_lock);
2954 reply = create_error_reply("Contents array parameter shorter than sessions.");
2955 goto finalize;
2956 }
2957 config = strdup(json_object_get_string(obj));
2958 pthread_mutex_unlock(&json_lock);
2959
2960 reply = netconf_generic(session_key, config, &data);
2961 if (reply == NULL) {
2962 GETSPEC_ERR_REPLY
2963 if (err_reply != NULL) {
2964 /* use filled err_reply from libnetconf's callback */
2965 reply = err_reply;
2966 }
2967 } else {
2968 if (data == NULL) {
2969 pthread_mutex_lock(&json_lock);
2970 reply = json_object_new_object();
2971 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2972 pthread_mutex_unlock(&json_lock);
2973 } else {
2974 reply = create_data_reply(data);
2975 free(data);
2976 }
2977 }
2978
2979finalize:
2980 CHECK_AND_FREE(config);
2981 return reply;
2982}
2983
2984json_object *
2985handle_op_getschema(json_object *request, unsigned int session_key)
2986{
2987 char *data = NULL;
2988 char *identifier = NULL;
2989 char *version = NULL;
2990 char *format = NULL;
2991 json_object *reply = NULL;
2992
2993 DEBUG("Request: get-schema (session %u)", session_key);
2994
2995 pthread_mutex_lock(&json_lock);
2996 identifier = get_param_string(request, "identifier");
2997 version = get_param_string(request, "version");
2998 format = get_param_string(request, "format");
2999 pthread_mutex_unlock(&json_lock);
3000
3001 if (identifier == NULL) {
3002 reply = create_error_reply("No identifier for get-schema supplied.");
3003 goto finalize;
3004 }
3005
3006 DEBUG("get-schema(version: %s, format: %s)", version, format);
3007 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
3008 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
3009 } else {
3010 reply = create_data_reply(data);
3011 free(data);
3012 }
3013
3014finalize:
3015 CHECK_AND_FREE(identifier);
3016 CHECK_AND_FREE(version);
3017 CHECK_AND_FREE(format);
3018 return reply;
3019}
3020
3021json_object *
3022handle_op_reloadhello(json_object *UNUSED(request), unsigned int session_key)
3023{
3024 struct nc_session *temp_session = NULL;
3025 struct session_with_mutex * locked_session = NULL;
3026 json_object *reply = NULL;
3027
3028 DEBUG("Request: reload hello (session %u)", session_key);
3029
3030 DEBUG("LOCK wrlock %s", __func__);
3031 if (pthread_rwlock_wrlock(&session_lock) != 0) {
3032 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3033 return NULL;
3034 }
3035
3036 for (locked_session = netconf_sessions_list;
3037 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003038 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003039 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
3040 DEBUG("LOCK mutex %s", __func__);
3041 pthread_mutex_lock(&locked_session->lock);
3042 DEBUG("creating temporary NC session.");
3043 temp_session = nc_session_connect_channel(locked_session->session, NULL);
3044 if (temp_session != NULL) {
3045 prepare_status_message(locked_session, temp_session);
3046 DEBUG("closing temporal NC session.");
3047 nc_session_free(temp_session);
3048 temp_session = NULL;
3049 } else {
3050 DEBUG("Reload hello failed due to channel establishment");
3051 reply = create_error_reply("Reload was unsuccessful, connection failed.");
3052 }
3053 DEBUG("UNLOCK mutex %s", __func__);
3054 pthread_mutex_unlock(&locked_session->lock);
3055 DEBUG("UNLOCK wrlock %s", __func__);
3056 if (pthread_rwlock_unlock(&session_lock) != 0) {
3057 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3058 }
3059 } else {
3060 DEBUG("UNLOCK wrlock %s", __func__);
3061 if (pthread_rwlock_unlock(&session_lock) != 0) {
3062 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3063 }
3064 reply = create_error_reply("Invalid session identifier.");
3065 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02003066
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003067 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
3068 reply = locked_session->hello_message;
3069 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02003070
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003071 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003072}
3073
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003074void
3075notification_history(time_t eventtime, const char *content)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003076{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003077 json_object *notif_history_array = (json_object *)pthread_getspecific(notif_history_key);
3078 if (notif_history_array == NULL) {
3079 ERROR("No list of notification history found.");
3080 return;
3081 }
3082 DEBUG("Got notification from history %lu.", (long unsigned)eventtime);
3083 pthread_mutex_lock(&json_lock);
3084 json_object *notif = json_object_new_object();
3085 if (notif == NULL) {
3086 ERROR("Could not allocate memory for notification (json).");
3087 goto failed;
3088 }
3089 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
3090 json_object_object_add(notif, "content", json_object_new_string(content));
3091 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003092failed:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003093 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003094}
3095
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003096json_object *
3097handle_op_ntfgethistory(json_object *request, unsigned int session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003098{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003099 json_object *reply = NULL;
3100 json_object *js_tmp = NULL;
3101 struct session_with_mutex *locked_session = NULL;
3102 struct nc_session *temp_session = NULL;
3103 nc_rpc *rpc = NULL;
3104 time_t start = 0;
3105 time_t stop = 0;
3106 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003107
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003108 DEBUG("Request: get notification history (session %u)", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003109
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003110 pthread_mutex_lock(&json_lock);
3111 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
3112 from = json_object_get_int64(js_tmp);
3113 }
3114 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
3115 to = json_object_get_int64(js_tmp);
3116 }
3117 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02003118
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003119 start = time(NULL) + from;
3120 stop = time(NULL) + to;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003121
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003122 DEBUG("notification history interval %li %li", (long int)from, (long int)to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003123
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003124 DEBUG("LOCK wrlock %s", __func__);
3125 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3126 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3127 reply = create_error_reply("Internal lock failed.");
3128 goto finalize;
3129 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003130
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003131 for (locked_session = netconf_sessions_list;
3132 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003133 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003134 if (locked_session != NULL) {
3135 DEBUG("LOCK mutex %s", __func__);
3136 pthread_mutex_lock(&locked_session->lock);
3137 DEBUG("UNLOCK wrlock %s", __func__);
3138 if (pthread_rwlock_unlock(&session_lock) != 0) {
3139 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3140 }
3141 DEBUG("creating temporal NC session.");
3142 temp_session = nc_session_connect_channel(locked_session->session, NULL);
3143 if (temp_session != NULL) {
3144 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
3145 if (rpc == NULL) {
3146 DEBUG("UNLOCK mutex %s", __func__);
3147 pthread_mutex_unlock(&locked_session->lock);
3148 DEBUG("notifications: creating an rpc request failed.");
3149 reply = create_error_reply("notifications: creating an rpc request failed.");
3150 goto finalize;
3151 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003152
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003153 DEBUG("Send NC subscribe.");
3154 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
3155 json_object *res = netconf_unlocked_op(temp_session, rpc);
3156 if (res != NULL) {
3157 DEBUG("UNLOCK mutex %s", __func__);
3158 pthread_mutex_unlock(&locked_session->lock);
3159 DEBUG("Subscription RPC failed.");
3160 reply = res;
3161 goto finalize;
3162 }
3163 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejka6b886e02013-07-05 09:53:17 +02003164
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003165 DEBUG("UNLOCK mutex %s", __func__);
3166 pthread_mutex_unlock(&locked_session->lock);
3167 DEBUG("LOCK mutex %s", __func__);
3168 pthread_mutex_lock(&ntf_history_lock);
3169 pthread_mutex_lock(&json_lock);
3170 json_object *notif_history_array = json_object_new_array();
3171 pthread_mutex_unlock(&json_lock);
3172 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
3173 ERROR("notif_history: cannot set thread-specific hash value.");
3174 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003175
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003176 ncntf_dispatch_receive(temp_session, notification_history);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003177
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003178 pthread_mutex_lock(&json_lock);
3179 reply = json_object_new_object();
3180 json_object_object_add(reply, "notifications", notif_history_array);
3181 //json_object_put(notif_history_array);
3182 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003183
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003184 DEBUG("UNLOCK mutex %s", __func__);
3185 pthread_mutex_unlock(&ntf_history_lock);
3186 DEBUG("closing temporal NC session.");
3187 nc_session_free(temp_session);
3188 temp_session = NULL;
3189 } else {
3190 DEBUG("UNLOCK mutex %s", __func__);
3191 pthread_mutex_unlock(&locked_session->lock);
3192 DEBUG("Get history of notification failed due to channel establishment");
3193 reply = create_error_reply("Get history of notification was unsuccessful, connection failed.");
3194 }
3195 } else {
3196 DEBUG("UNLOCK wrlock %s", __func__);
3197 if (pthread_rwlock_unlock(&session_lock) != 0) {
3198 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3199 }
3200 reply = create_error_reply("Invalid session identifier.");
3201 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003202
Tomas Cejka09629492014-07-10 15:58:06 +02003203finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003204 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02003205}
3206
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003207json_object *
3208handle_op_validate(json_object *request, unsigned int session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02003209{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003210 json_object *reply = NULL;
3211 char *target = NULL;
3212 char *url = NULL;
3213 nc_rpc *rpc = NULL;
3214 NC_DATASTORE target_ds;
Tomas Cejka4003a702013-10-01 00:02:45 +02003215
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003216 DEBUG("Request: validate datastore (session %u)", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02003217
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003218 pthread_mutex_lock(&json_lock);
3219 target = get_param_string(request, "target");
3220 url = get_param_string(request, "url");
3221 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02003222
3223
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003224 if (target == NULL) {
3225 reply = create_error_reply("Missing target parameter.");
3226 goto finalize;
3227 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003228
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003229 /* validation */
3230 target_ds = parse_datastore(target);
3231 if (target_ds == NC_DATASTORE_URL) {
3232 if (url != NULL) {
3233 rpc = nc_rpc_validate(target_ds, url);
3234 }
3235 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
3236 || (target_ds == NC_DATASTORE_CANDIDATE)) {
3237 rpc = nc_rpc_validate(target_ds);
3238 }
3239 if (rpc == NULL) {
3240 DEBUG("mod_netconf: creating rpc request failed");
3241 reply = create_error_reply("Creation of RPC request failed.");
3242 goto finalize;
3243 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003244
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003245 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
3246 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003247
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003248 if (reply == NULL) {
3249 DEBUG("Request: validation ok.");
3250 reply = create_ok_reply();
3251 }
3252 }
3253 nc_rpc_free (rpc);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003254
Tomas Cejka09629492014-07-10 15:58:06 +02003255finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003256 CHECK_AND_FREE(target);
3257 CHECK_AND_FREE(url);
3258 return reply;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003259}
3260
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003261json_object *
3262handle_op_query(json_object *request, unsigned int session_key, int idx)
David Kupka8e60a372012-09-04 09:15:20 +02003263{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003264 json_object *reply = NULL, *filters, *obj;
3265 char *filter = NULL;
3266 int load_children = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003267
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003268 DEBUG("Request: query (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003269
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003270 pthread_mutex_lock(&json_lock);
3271 if (json_object_object_get_ex(request, "filters", &filters) == FALSE) {
3272 pthread_mutex_unlock(&json_lock);
3273 reply = create_error_reply("Missing filters parameter.");
3274 goto finalize;
3275 }
3276 obj = json_object_array_get_idx(filters, idx);
3277 if (!obj) {
3278 pthread_mutex_unlock(&json_lock);
3279 reply = create_error_reply("Filters array parameter shorter than sessions.");
3280 goto finalize;
3281 }
3282 filter = strdup(json_object_get_string(obj));
3283 if (json_object_object_get_ex(request, "load_children", &obj) == TRUE) {
3284 load_children = json_object_get_boolean(obj);
3285 }
3286 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02003287
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003288 reply = libyang_query(session_key, filter, load_children);
David Kupka8e60a372012-09-04 09:15:20 +02003289
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003290 CHECK_ERR_SET_REPLY
3291 if (!reply) {
3292 reply = create_error_reply("Query failed.");
3293 }
David Kupka8e60a372012-09-04 09:15:20 +02003294
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003295finalize:
3296 CHECK_AND_FREE(filter);
3297 return reply;
3298}
David Kupka8e60a372012-09-04 09:15:20 +02003299
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003300json_object *
3301handle_op_merge(json_object *request, unsigned int session_key, int idx)
3302{
3303 json_object *reply = NULL, *configs, *obj;
3304 char *config = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003305
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003306 DEBUG("Request: merge (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003307
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003308 pthread_mutex_lock(&json_lock);
3309 if (json_object_object_get_ex(request, "configurations", &configs) == FALSE) {
3310 pthread_mutex_unlock(&json_lock);
3311 reply = create_error_reply("Missing configurations parameter.");
3312 goto finalize;
3313 }
3314 obj = json_object_array_get_idx(configs, idx);
3315 if (!obj) {
3316 pthread_mutex_unlock(&json_lock);
3317 reply = create_error_reply("Filters array parameter shorter than sessions.");
3318 goto finalize;
3319 }
3320 config = strdup(json_object_get_string(obj));
3321 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02003322
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003323 reply = libyang_merge(session_key, config);
David Kupka8e60a372012-09-04 09:15:20 +02003324
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003325 CHECK_ERR_SET_REPLY
3326 if (!reply) {
3327 reply = create_error_reply("Merge failed.");
3328 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003329
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003330finalize:
3331 CHECK_AND_FREE(config);
3332 return reply;
3333}
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003334
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003335void *
3336thread_routine(void *arg)
3337{
3338 void *retval = NULL;
3339 struct pollfd fds;
3340 json_object *request = NULL, *replies = NULL, *reply, *sessions = NULL;
3341 json_object *js_tmp = NULL;
3342 int operation = (-1), count, i;
3343 int status = 0;
3344 const char *msgtext;
3345 unsigned int session_key = 0;
3346 char *chunked_out_msg = NULL;
3347 int client = ((struct pass_to_thread *)arg)->client;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003348
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003349 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003350
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003351 /* init thread specific err_reply memory */
3352 create_err_reply_p();
David Kupka8e60a372012-09-04 09:15:20 +02003353
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003354 while (!isterminated) {
3355 fds.fd = client;
3356 fds.events = POLLIN;
3357 fds.revents = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003358
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003359 status = poll(&fds, 1, 1000);
Tomas Cejkad5b53772013-06-08 23:01:07 +02003360
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003361 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
3362 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
3363 continue;
3364 } else if (status < 0) {
3365 /* 0: poll time outed
3366 * close socket and ignore this request from the client, it can try it again
3367 * -1: poll failed
3368 * something wrong happend, close this socket and wait for another request
3369 */
3370 close(client);
3371 break;
3372 }
3373 /* status > 0 */
David Kupka8e60a372012-09-04 09:15:20 +02003374
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003375 /* check the status of the socket */
David Kupka8e60a372012-09-04 09:15:20 +02003376
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003377 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
3378 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
3379 /* close client's socket (it's probably already closed by client */
3380 close(client);
3381 break;
3382 }
Tomas Cejka09629492014-07-10 15:58:06 +02003383
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003384 DEBUG("Get framed message...");
3385 buffer = get_framed_message(client);
Tomas Cejka09629492014-07-10 15:58:06 +02003386
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003387 DEBUG("Check read buffer.");
3388 if (buffer != NULL) {
3389 enum json_tokener_error jerr;
3390 pthread_mutex_lock(&json_lock);
3391 request = json_tokener_parse_verbose(buffer, &jerr);
3392 if (jerr != json_tokener_success) {
3393 ERROR("JSON parsing error");
3394 pthread_mutex_unlock(&json_lock);
3395 continue;
3396 }
3397
3398 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
3399 operation = json_object_get_int(js_tmp);
3400 }
3401 pthread_mutex_unlock(&json_lock);
3402 if (operation == -1) {
3403 replies = create_replies();
3404 add_reply(replies, create_error_reply("Missing operation type from frontend."), 0);
3405 goto send_reply;
3406 }
3407
3408 if ((operation < 4) || ((operation > 19) && (operation < 100)) || (operation > 101)) {
3409 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
3410 replies = create_replies();
3411 add_reply(replies, create_error_reply("Operation not supported."), 0);
3412 goto send_reply;
3413 }
3414
3415 DEBUG("operation %d", operation);
3416
3417 /* null global JSON error-reply */
3418 clean_err_reply();
3419
3420 /* clean replies envelope */
3421 if (replies != NULL) {
3422 pthread_mutex_lock(&json_lock);
3423 json_object_put(replies);
3424 pthread_mutex_unlock(&json_lock);
3425 }
3426 replies = create_replies();
3427
3428 if (operation == MSG_CONNECT) {
3429 count = 1;
3430 } else {
3431 pthread_mutex_lock(&json_lock);
3432 if (json_object_object_get_ex(request, "sessions", &sessions) == FALSE) {
3433 add_reply(replies, create_error_reply("Operation missing \"sessions\" arg"), 0);
3434 goto send_reply;
3435 }
3436 count = json_object_array_length(sessions);
3437 pthread_mutex_unlock(&json_lock);
3438 }
3439
3440 for (i = 0; i < count; ++i) {
3441 if (operation != MSG_CONNECT) {
3442 js_tmp = json_object_array_get_idx(sessions, i);
3443 session_key = json_object_get_int(js_tmp);
3444 }
3445
3446 /* process required operation */
3447 switch (operation) {
3448 case MSG_CONNECT:
3449 reply = handle_op_connect(request);
3450 break;
3451 case MSG_DISCONNECT:
3452 reply = handle_op_disconnect(request, session_key);
3453 break;
3454 case MSG_GET:
3455 reply = handle_op_get(request, session_key);
3456 break;
3457 case MSG_GETCONFIG:
3458 reply = handle_op_getconfig(request, session_key);
3459 break;
3460 case MSG_EDITCONFIG:
3461 reply = handle_op_editconfig(request, session_key, i);
3462 break;
3463 case MSG_COPYCONFIG:
3464 reply = handle_op_copyconfig(request, session_key, i);
3465 break;
3466 case MSG_DELETECONFIG:
3467 reply = handle_op_deleteconfig(request, session_key);
3468 break;
3469 case MSG_LOCK:
3470 reply = handle_op_lock(request, session_key);
3471 break;
3472 case MSG_UNLOCK:
3473 reply = handle_op_unlock(request, session_key);
3474 break;
3475 case MSG_KILL:
3476 reply = handle_op_kill(request, session_key);
3477 break;
3478 case MSG_INFO:
3479 reply = handle_op_info(request, session_key);
3480 break;
3481 case MSG_GENERIC:
3482 reply = handle_op_generic(request, session_key, i);
3483 break;
3484 case MSG_GETSCHEMA:
3485 reply = handle_op_getschema(request, session_key);
3486 break;
3487 case MSG_RELOADHELLO:
3488 reply = handle_op_reloadhello(request, session_key);
3489 break;
3490 case MSG_NTF_GETHISTORY:
3491 reply = handle_op_ntfgethistory(request, session_key);
3492 break;
3493 case MSG_VALIDATE:
3494 reply = handle_op_validate(request, session_key);
3495 break;
3496 case SCH_QUERY:
3497 reply = handle_op_query(request, session_key, i);
3498 break;
3499 case SCH_MERGE:
3500 reply = handle_op_merge(request, session_key, i);
3501 break;
3502 }
3503
3504 add_reply(replies, reply, session_key);
3505 }
3506
3507 /* free parameters */
3508 operation = (-1);
3509
3510 DEBUG("Clean request json object.");
3511 if (request != NULL) {
3512 pthread_mutex_lock(&json_lock);
3513 json_object_put(request);
3514 pthread_mutex_unlock(&json_lock);
3515 }
3516 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02003517
3518send_reply:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003519 /* send reply to caller */
3520 if (replies) {
3521 pthread_mutex_lock(&json_lock);
3522 msgtext = json_object_to_json_string(replies);
3523 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
3524 if (buffer != NULL) {
3525 free(buffer);
3526 buffer = NULL;
3527 }
3528 pthread_mutex_unlock(&json_lock);
3529 break;
3530 }
3531 pthread_mutex_unlock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003532
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003533 DEBUG("Send framed reply json object.");
3534 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
3535 DEBUG("Clean reply json object.");
3536 pthread_mutex_lock(&json_lock);
3537 json_object_put(replies);
3538 replies = NULL;
3539 DEBUG("Clean message buffer.");
3540 CHECK_AND_FREE(chunked_out_msg);
3541 chunked_out_msg = NULL;
3542 if (buffer) {
3543 free(buffer);
3544 buffer = NULL;
3545 }
3546 pthread_mutex_unlock(&json_lock);
3547 clean_err_reply();
3548 } else {
3549 ERROR("Reply is NULL, shouldn't be...");
3550 continue;
3551 }
3552 }
3553 }
3554 free(arg);
3555 free_err_reply();
David Kupka8e60a372012-09-04 09:15:20 +02003556
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003557 return retval;
David Kupka8e60a372012-09-04 09:15:20 +02003558}
3559
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003560/**
3561 * \brief Close all open NETCONF sessions.
3562 *
3563 * During termination of mod_netconf, it is useful to close all remaining
3564 * sessions. This function iterates over the list of sessions and close them
3565 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003566 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003567static void
3568close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003569{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003570 struct session_with_mutex *locked_session, *next_session;
3571 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003572
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003573 /* get exclusive access to sessions_list (conns) */
3574 DEBUG("LOCK wrlock %s", __func__);
3575 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
3576 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3577 return;
3578 }
3579 for (next_session = netconf_sessions_list; next_session;) {
3580 locked_session = next_session;
3581 next_session = locked_session->next;
Tomas Cejka47387fd2013-06-10 20:37:46 +02003582
Michal Vaskoc3146782015-11-04 14:46:41 +01003583 /* close_and_free_session handles locking on its own */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003584 DEBUG("Closing NETCONF session %u (SID %s).", locked_session->session_key, nc_session_get_id(locked_session->session));
3585 close_and_free_session(locked_session);
3586 }
3587 netconf_sessions_list = NULL;
3588
3589 /* get exclusive access to sessions_list (conns) */
3590 DEBUG("UNLOCK wrlock %s", __func__);
3591 if (pthread_rwlock_unlock (&session_lock) != 0) {
3592 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3593 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003594}
3595
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003596static void
3597check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003598{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003599 struct nc_session *ns = NULL;
3600 struct session_with_mutex *locked_session = NULL;
3601 time_t current_time = time(NULL);
3602 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003603
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003604 /* get exclusive access to sessions_list (conns) */
3605 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
3606 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3607 return;
3608 }
3609 for (locked_session = netconf_sessions_list; locked_session; locked_session = locked_session->next) {
3610 ns = locked_session->session;
3611 if (ns == NULL) {
3612 continue;
3613 }
3614 pthread_mutex_lock(&locked_session->lock);
3615 if ((current_time - locked_session->last_activity) > ACTIVITY_TIMEOUT) {
3616 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 +02003617
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003618 /* close_and_free_session handles locking on its own */
3619 close_and_free_session(locked_session);
3620 } else {
3621 pthread_mutex_unlock(&locked_session->lock);
3622 }
3623 }
3624 /* get exclusive access to sessions_list (conns) */
3625 if (pthread_rwlock_unlock(&session_lock) != 0) {
3626 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3627 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003628}
3629
3630
3631/**
Radek Krejcif23850c2012-07-23 16:14:17 +02003632 * This is actually implementation of NETCONF client
3633 * - requests are received from UNIX socket in the predefined format
3634 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01003635 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02003636 *
3637 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003638static void
3639forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02003640{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003641 struct timeval tv;
3642 struct sockaddr_un local, remote;
3643 int lsock, client, ret, i, pthread_count = 0;
3644 unsigned int olds = 0, timediff = 0;
3645 socklen_t len;
3646 struct pass_to_thread *arg;
3647 pthread_t *ptids = calloc(1, sizeof(pthread_t));
3648 struct timespec maxtime;
3649 pthread_rwlockattr_t lock_attrs;
3650 #ifdef WITH_NOTIFICATIONS
3651 char use_notifications = 0;
3652 #endif
David Kupka8e60a372012-09-04 09:15:20 +02003653
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003654 /* wait at most 5 seconds for every thread to terminate */
3655 maxtime.tv_sec = 5;
3656 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02003657
Tomas Cejka04e08f42014-03-27 19:52:34 +01003658#ifdef HAVE_UNIXD_SETUP_CHILD
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003659 /* change uid and gid of process for security reasons */
3660 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01003661#else
3662# ifdef SU_GROUP
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003663 if (strlen(SU_GROUP) > 0) {
3664 struct group *g = getgrnam(SU_GROUP);
3665 if (g == NULL) {
3666 ERROR("GID (%s) was not found.", SU_GROUP);
3667 return;
3668 }
3669 if (setgid(g->gr_gid) != 0) {
3670 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
3671 return;
3672 }
3673 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003674# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003675 DEBUG("no SU_GROUP");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003676# endif
3677# ifdef SU_USER
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003678 if (strlen(SU_USER) > 0) {
3679 struct passwd *p = getpwnam(SU_USER);
3680 if (p == NULL) {
3681 ERROR("UID (%s) was not found.", SU_USER);
3682 return;
3683 }
3684 if (setuid(p->pw_uid) != 0) {
3685 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
3686 return;
3687 }
3688 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003689# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003690 DEBUG("no SU_USER");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003691# endif
3692#endif
Radek Krejci469aab82012-07-22 18:42:20 +02003693
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003694 /* try to remove if exists */
3695 unlink(sockname);
Tomas Cejka04e08f42014-03-27 19:52:34 +01003696
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003697 /* create listening UNIX socket to accept incoming connections */
3698 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
3699 ERROR("Creating socket failed (%s)", strerror(errno));
3700 goto error_exit;
3701 }
Radek Krejci469aab82012-07-22 18:42:20 +02003702
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003703 local.sun_family = AF_UNIX;
3704 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
3705 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
Radek Krejci469aab82012-07-22 18:42:20 +02003706
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003707 if (bind(lsock, (struct sockaddr *)&local, len) == -1) {
3708 if (errno == EADDRINUSE) {
3709 ERROR("mod_netconf socket address already in use");
3710 goto error_exit;
3711 }
3712 ERROR("Binding socket failed (%s)", strerror(errno));
3713 goto error_exit;
3714 }
Radek Krejci469aab82012-07-22 18:42:20 +02003715
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003716 if (listen(lsock, MAX_SOCKET_CL) == -1) {
3717 ERROR("Setting up listen socket failed (%s)", strerror(errno));
3718 goto error_exit;
3719 }
3720 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02003721
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003722 uid_t user = -1;
3723 if (strlen(CHOWN_USER) > 0) {
3724 struct passwd *p = getpwnam(CHOWN_USER);
3725 if (p != NULL) {
3726 user = p->pw_uid;
3727 }
3728 }
3729 gid_t group = -1;
3730 if (strlen(CHOWN_GROUP) > 0) {
3731 struct group *g = getgrnam(CHOWN_GROUP);
3732 if (g != NULL) {
3733 group = g->gr_gid;
3734 }
3735 }
3736 if (chown(sockname, user, group) == -1) {
3737 ERROR("Chown on socket file failed (%s).", strerror(errno));
3738 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003739
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003740 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02003741
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003742 #ifdef WITH_NOTIFICATIONS
3743 if (notification_init() == -1) {
3744 ERROR("libwebsockets initialization failed");
3745 use_notifications = 0;
3746 } else {
3747 use_notifications = 1;
3748 }
3749 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003750
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003751 /* setup libnetconf's callbacks */
3752 nc_verbosity(NC_VERB_DEBUG);
3753 nc_callback_print(clb_print);
3754 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
3755 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
3756 nc_callback_sshauth_password(netconf_callback_sshauth_password);
3757 nc_callback_sshauth_passphrase(netconf_callback_sshauth_passphrase);
3758 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02003759
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003760 /* disable publickey authentication */
3761 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
Radek Krejci469aab82012-07-22 18:42:20 +02003762
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003763 /* create mutex protecting session list */
3764 pthread_rwlockattr_init(&lock_attrs);
3765 /* rwlock is shared only with threads in this process */
3766 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
3767 /* create rw lock */
3768 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
3769 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
3770 goto error_exit;
3771 }
3772 pthread_mutex_init(&ntf_history_lock, NULL);
3773 pthread_mutex_init(&json_lock, NULL);
3774 DEBUG("Initialization of notification history.");
3775 if (pthread_key_create(&notif_history_key, NULL) != 0) {
3776 ERROR("Initialization of notification history failed.");
3777 }
3778 if (pthread_key_create(&err_reply_key, NULL) != 0) {
3779 ERROR("Initialization of reply key failed.");
3780 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02003781
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003782 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
3783 while (isterminated == 0) {
3784 gettimeofday(&tv, NULL);
3785 timediff = (unsigned int)tv.tv_sec - olds;
3786 #ifdef WITH_NOTIFICATIONS
3787 if (use_notifications == 1) {
3788 notification_handle();
3789 }
3790 #endif
3791 if (timediff > ACTIVITY_CHECK_INTERVAL) {
3792 check_timeout_and_close();
3793 }
Radek Krejci469aab82012-07-22 18:42:20 +02003794
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003795 /* open incoming connection if any */
3796 len = sizeof(remote);
3797 client = accept(lsock, (struct sockaddr *) &remote, &len);
3798 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
3799 usleep(SLEEP_TIME * 1000);
3800 continue;
3801 } else if (client == -1 && (errno == EINTR)) {
3802 continue;
3803 } else if (client == -1) {
3804 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
3805 continue;
3806 }
Radek Krejci469aab82012-07-22 18:42:20 +02003807
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003808 /* set client's socket as non-blocking */
3809 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02003810
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003811 arg = malloc(sizeof(struct pass_to_thread));
3812 arg->client = client;
3813 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02003814
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003815 /* start new thread. It will serve this particular request and then terminate */
3816 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void *)arg)) != 0) {
3817 ERROR("Creating POSIX thread failed: %d\n", ret);
3818 } else {
3819 DEBUG("Thread %lu created", ptids[pthread_count]);
3820 pthread_count++;
3821 ptids = realloc (ptids, sizeof(pthread_t) * (pthread_count+1));
3822 ptids[pthread_count] = 0;
3823 }
Radek Krejci469aab82012-07-22 18:42:20 +02003824
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003825 /* check if some thread already terminated, free some resources by joining it */
3826 for (i = 0; i < pthread_count; i++) {
3827 if (pthread_tryjoin_np(ptids[i], (void **)&arg) == 0) {
3828 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
3829 pthread_count--;
3830 if (pthread_count > 0) {
3831 /* place last Thread ID on the place of joined one */
3832 ptids[i] = ptids[pthread_count];
3833 }
3834 }
3835 }
3836 DEBUG("Running %d threads", pthread_count);
3837 }
Radek Krejci469aab82012-07-22 18:42:20 +02003838
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003839 DEBUG("mod_netconf terminating...");
3840 /* join all threads */
3841 for (i = 0; i < pthread_count; i++) {
3842 pthread_timedjoin_np(ptids[i], (void **)&arg, &maxtime);
3843 }
Radek Krejci469aab82012-07-22 18:42:20 +02003844
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003845 #ifdef WITH_NOTIFICATIONS
3846 notification_close();
3847 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003848
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003849 /* close all NETCONF sessions */
3850 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003851
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003852 /* destroy rwlock */
3853 pthread_rwlock_destroy(&session_lock);
3854 pthread_rwlockattr_destroy(&lock_attrs);
David Kupka8e60a372012-09-04 09:15:20 +02003855
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003856 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02003857
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003858 free(ptids);
3859 close(lsock);
3860 exit(0);
3861 return;
3862
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003863error_exit:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003864 close(lsock);
3865 free(ptids);
3866 return;
Radek Krejci469aab82012-07-22 18:42:20 +02003867}
3868
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003869int
3870main(int argc, char **argv)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003871{
Michal Vaskoc3146782015-11-04 14:46:41 +01003872 struct sigaction action;
3873 sigset_t block_mask;
3874
3875 if (argc > 1) {
3876 sockname = argv[1];
3877 } else {
3878 sockname = SOCKET_FILENAME;
3879 }
3880
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003881 sigfillset(&block_mask);
Michal Vaskoc3146782015-11-04 14:46:41 +01003882 action.sa_handler = signal_handler;
3883 action.sa_mask = block_mask;
3884 action.sa_flags = 0;
3885 sigaction(SIGINT, &action, NULL);
3886 sigaction(SIGTERM, &action, NULL);
3887
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003888 forked_proc();
3889 DEBUG("Terminated");
3890 return 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003891}