blob: 0cbb87526b09069879ab1bc6061da61e5d00de5c [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);
122
123static void
124signal_handler(int sign)
Radek Krejci469aab82012-07-22 18:42:20 +0200125{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100126 switch (sign) {
127 case SIGINT:
128 case SIGTERM:
129 isterminated = 1;
130 break;
131 }
Radek Krejci469aab82012-07-22 18:42:20 +0200132}
133
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100134int
135netconf_callback_ssh_hostkey_check(const char* UNUSED(hostname), ssh_session UNUSED(session))
Radek Krejci469aab82012-07-22 18:42:20 +0200136{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100137 /* always approve */
138 return (EXIT_SUCCESS);
Radek Krejci469aab82012-07-22 18:42:20 +0200139}
140
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100141char *
142netconf_callback_sshauth_passphrase(const char *UNUSED(username), const char *UNUSED(hostname), const char *UNUSED(priv_key_file))
Radek Krejci469aab82012-07-22 18:42:20 +0200143{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100144 char *buf;
145 buf = strdup(password);
146 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200147}
148
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100149char *
150netconf_callback_sshauth_password(const char *UNUSED(username), const char *UNUSED(hostname))
Radek Krejci469aab82012-07-22 18:42:20 +0200151{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100152 char *buf;
153 buf = strdup(password);
154 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200155}
156
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100157char *
158netconf_callback_sshauth_interactive(const char *UNUSED(name), const char *UNUSED(instruction),
159 const char *UNUSED(prompt), int UNUSED(echo))
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200160{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100161 char *buf;
162 buf = strdup(password);
163 return (buf);
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200164}
165
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100166void
167netconf_callback_error_process(const char *UNUSED(tag),
168 const char *UNUSED(type),
169 const char *UNUSED(severity),
170 const char *UNUSED(apptag),
171 const char *UNUSED(path),
172 const char *message,
173 const char *UNUSED(attribute),
174 const char *UNUSED(element),
175 const char *UNUSED(ns),
176 const char *UNUSED(sid))
Radek Krejcic11fd862012-07-26 12:41:21 +0200177{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100178 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
179 if (err_reply_p == NULL) {
180 ERROR("Error message was not allocated. %s", __func__);
181 return;
182 }
183 json_object *err_reply = *err_reply_p;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100184
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100185 json_object *array = NULL;
186 if (err_reply == NULL) {
187 ERROR("error calback: empty error list");
188 pthread_mutex_lock(&json_lock);
189 err_reply = json_object_new_object();
190 array = json_object_new_array();
191 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
192 json_object_object_add(err_reply, "errors", array);
193 if (message != NULL) {
194 json_object_array_add(array, json_object_new_string(message));
195 }
196 pthread_mutex_unlock(&json_lock);
197 (*err_reply_p) = err_reply;
198 } else {
199 ERROR("error calback: nonempty error list");
200 pthread_mutex_lock(&json_lock);
201 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
202 if (message != NULL) {
203 json_object_array_add(array, json_object_new_string(message));
204 }
205 }
206 pthread_mutex_unlock(&json_lock);
207 }
208 pthread_setspecific(err_reply_key, err_reply_p);
209 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200210}
211
Tomas Cejka47387fd2013-06-10 20:37:46 +0200212/**
213 * should be used in locked area
214 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100215void
216prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200217{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100218 json_object *json_obj = NULL;
219 json_object *js_tmp = NULL;
220 char *old_sid = NULL;
221 const char *j_old_sid = NULL;
222 const char *cpbltstr;
223 struct nc_cpblts* cpblts = NULL;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200224
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100225 if (s == NULL) {
226 ERROR("No session given.");
227 return;
228 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200229
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100230 pthread_mutex_lock(&json_lock);
231 if (s->hello_message != NULL) {
232 ERROR("clean previous hello message");
233 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
234 j_old_sid = json_object_get_string(js_tmp);
235 if (j_old_sid != NULL) {
236 old_sid = strdup(j_old_sid);
237 }
238 }
239 json_object_put(s->hello_message);
240 s->hello_message = NULL;
241 }
242 s->hello_message = json_object_new_object();
243 if (session != NULL) {
244 if (old_sid != NULL) {
245 /* use previous sid */
246 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
247 free(old_sid);
248 old_sid = NULL;
249 } else {
250 /* we don't have old sid */
251 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
252 }
253 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
254 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
255 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
256 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
257 cpblts = nc_session_get_cpblts (session);
258 if (cpblts != NULL) {
259 json_obj = json_object_new_array();
260 nc_cpblts_iter_start (cpblts);
261 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
262 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
263 }
264 json_object_object_add(s->hello_message, "capabilities", json_obj);
265 }
266 DEBUG("%s", json_object_to_json_string(s->hello_message));
267 } else {
268 ERROR("Session was not given.");
269 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
270 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
271 }
272 DEBUG("Status info from hello message prepared");
273 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200274
275}
276
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100277void
278create_err_reply_p()
Tomas Cejka442258e2014-04-01 18:17:18 +0200279{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100280 json_object **err_reply = calloc(1, sizeof(json_object **));
281 if (err_reply == NULL) {
282 ERROR("Allocation of err_reply storage failed!");
283 return;
284 }
285 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
286 ERROR("cannot set thread-specific value.");
287 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200288}
289
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100290void
291clean_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200292{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100293 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
294 if (err_reply != NULL) {
295 if (*err_reply != NULL) {
296 pthread_mutex_lock(&json_lock);
297 json_object_put(*err_reply);
298 pthread_mutex_unlock(&json_lock);
299 }
300 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
301 ERROR("Cannot set thread-specific hash value.");
302 }
303 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200304}
305
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100306void
307free_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200308{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100309 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
310 if (err_reply != NULL) {
311 if (*err_reply != NULL) {
312 pthread_mutex_lock(&json_lock);
313 json_object_put(*err_reply);
314 pthread_mutex_unlock(&json_lock);
315 }
316 free(err_reply);
317 err_reply = NULL;
318 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
319 ERROR("Cannot set thread-specific hash value.");
320 }
321 }
322}
323
324static struct session_with_mutex *
325session_get_locked(unsigned int session_key, json_object **err)
326{
327 struct session_with_mutex *locked_session;
328
329 /* get non-exclusive (read) access to sessions_list (conns) */
330 DEBUG("LOCK wrlock %s", __func__);
331 if (pthread_rwlock_rdlock(&session_lock) != 0) {
332 if (*err) {
333 *err = create_error_reply("Locking failed.");
334 }
335 return NULL;
336 }
337 /* get session where send the RPC */
338 for (locked_session = netconf_sessions_list;
339 locked_session && (locked_session->session_key != session_key);
340 locked_session = locked_session->next);
341 if (!locked_session) {
342 if (*err) {
343 *err = create_error_reply("Session not found.");
344 }
345 return NULL;
346 }
347
348 /* get exclusive access to session */
349 DEBUG("LOCK mutex %s", __func__);
350 if (pthread_mutex_lock(&locked_session->lock) != 0) {
351 if (*err) {
352 *err = create_error_reply("Locking failed.");
353 }
354 goto wrlock_fail;
355 }
356 return locked_session;
357
358wrlock_fail:
359 DEBUG("UNLOCK wrlock %s", __func__);
360 pthread_rwlock_unlock(&session_lock);
361 return NULL;
362}
363
364static void
365session_unlock(struct session_with_mutex *locked_session)
366{
367 DEBUG("UNLOCK mutex %s", __func__);
368 pthread_mutex_unlock(&locked_session->lock);
369 DEBUG("UNLOCK wrlock %s", __func__);
370 pthread_rwlock_unlock(&session_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +0200371}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200372
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200373/**
374 * \defgroup netconf_operations NETCONF operations
375 * The list of NETCONF operations that mod_netconf supports.
376 * @{
377 */
378
379/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200380 * \brief Send RPC and wait for reply with timeout.
381 *
382 * \param[in] session libnetconf session
383 * \param[in] rpc prepared RPC message
384 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
385 * \param[out] reply reply from the server
386 * \return Value from nc_session_recv_reply() or NC_MSG_UNKNOWN when send_rpc() fails.
387 * On success, it returns NC_MSG_REPLY.
388 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100389NC_MSG_TYPE
390netconf_send_recv_timed(struct nc_session *session, nc_rpc *rpc, int timeout, nc_reply **reply)
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200391{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100392 const nc_msgid msgid = NULL;
393 NC_MSG_TYPE ret = NC_MSG_UNKNOWN;
394 msgid = nc_session_send_rpc(session, rpc);
395 if (msgid == NULL) {
396 return ret;
397 }
398 do {
399 ret = nc_session_recv_reply(session, timeout, reply);
400 if (ret == NC_MSG_HELLO) {
401 ERROR("<hello> received instead reply, it will be lost.");
402 nc_reply_free(*reply);
403 }
404 if (ret == NC_MSG_WOULDBLOCK) {
405 ERROR("Timeout for receiving RPC reply expired.");
406 break;
407 }
408 } while (ret == NC_MSG_HELLO || ret == NC_MSG_NOTIFICATION);
409 return ret;
410}
411
412static int
413ctx_download_module(struct session_with_mutex *session, const char *model_name, const char *revision, const char *schema_dir)
414{
415 json_object *err = NULL;
416 char *model_data = NULL, *model_path;
417 size_t length;
418 FILE *file;
419
420 DEBUG("UNLOCK rwlock %s", __func__);
421 if (pthread_rwlock_unlock(&session_lock) != 0) {
422 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
423 return 1;
424 }
425
426 model_data = netconf_getschema(session->session_key, model_name, revision, "yin", &err);
427
428 DEBUG("LOCK rwlock %s", __func__);
429 if (pthread_rwlock_wrlock(&session_lock) != 0) {
430 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
431 return 1;
432 }
433
434 if (!model_data) {
435 if (err) {
436 json_object_put(err);
437 }
438 ERROR("Failed to get-schema of \"%s\".", model_name);
439 return 1;
440 }
441
442 if (revision) {
443 asprintf(&model_path, "%s/%s@%s.yin", schema_dir, model_name, revision);
444 } else {
445 asprintf(&model_path, "%s/%s.yin", schema_dir, model_name);
446 }
447
448 file = fopen(model_path, "w");
449 if (!file) {
450 ERROR("Failed to open \"%s\" for writing (%s).", model_path, strerror(errno));
451 free(model_data);
452 free(model_path);
453 return 1;
454 }
455 free(model_path);
456
457 length = strlen(model_data);
458 if (fwrite(model_data, 1, length, file) < length) {
459 ERROR("Failed to store the model \"%s\".", model_name);
460 free(model_data);
461 fclose(file);
462 return 1;
463 }
464
465 free(model_data);
466 fclose(file);
467 return 0;
468}
469
470static void
471ctx_enable_features(struct lys_module *module, const char *cpblt)
472{
473 char *ptr, *ptr2, *features = NULL;
474
475 /* parse features */
476 ptr = strstr(cpblt, "features=");
477 if (ptr) {
478 ptr += 9;
479 ptr2 = strchr(ptr, '&');
480 if (!ptr2) {
481 ptr2 = ptr + strlen(ptr);
482 }
483 features = strndup(ptr, ptr2 - ptr);
484 }
485
486 /* enable features */
487 if (features) {
488 /* basically manual strtok_r (to avoid macro) */
489 ptr2 = features;
490 for (ptr = features; *ptr; ++ptr) {
491 if (*ptr == ',') {
492 *ptr = '\0';
493 /* remember last feature */
494 ptr2 = ptr + 1;
495 }
496 }
497
498 ptr = features;
499 lys_features_enable(module, ptr);
500 while (ptr != ptr2) {
501 ptr += strlen(ptr) + 1;
502 lys_features_enable(module, ptr);
503 }
504
505 free(features);
506 }
507}
508
509static void
510ctx_enable_capabs(struct lys_module *ietfnc, json_object *cpb_array)
511{
512 json_object *item;
513 int i;
514 const char *capab;
515
516 /* set supported capabilities from ietf-netconf */
517 for (i = 0; i < json_object_array_length(cpb_array); ++i) {
518 item = json_object_array_get_idx(cpb_array, i);
519 capab = json_object_get_string(item);
520
521 if (!strncmp(capab, "urn:ietf:params:netconf:capability:", 35)) {
522 if (!strncmp(capab, "writable-running", 16)) {
523 lys_features_enable(ietfnc, "writable-running");
524 } else if (!strncmp(capab, "candidate", 9)) {
525 lys_features_enable(ietfnc, "candidate");
526 } else if (!strcmp(capab, "confirmed-commit:1.1")) {
527 lys_features_enable(ietfnc, "confirmed-commit");
528 } else if (!strncmp(capab, "rollback-on-error", 17)) {
529 lys_features_enable(ietfnc, "rollback-on-error");
530 } else if (!strcmp(capab, "validate:1.1")) {
531 lys_features_enable(ietfnc, "validate");
532 } else if (!strncmp(capab, "startup", 7)) {
533 lys_features_enable(ietfnc, "startup");
534 } else if (!strncmp(capab, "url", 3)) {
535 lys_features_enable(ietfnc, "url");
536 } else if (!strncmp(capab, "xpath", 5)) {
537 lys_features_enable(ietfnc, "xpath");
538 }
539 }
540 }
541}
542
543static int
544prepare_context(struct session_with_mutex *session)
545{
546 struct lys_module *module;
547 json_object *array, *item;
548 char *ptr, *ptr2;
549 char *model_name = NULL, *revision = NULL;
550 const char *capab;
551 int i, get_schema_support;
552
553 if (json_object_object_get_ex(session->hello_message, "capabilities", &array) == FALSE) {
554 return 1;
555 }
556
557 get_schema_support = 0;
558 for (i = 0; i < json_object_array_length(array); ++i) {
559 item = json_object_array_get_idx(array, i);
560 capab = json_object_get_string(item);
561
562 if (!strncmp(capab, "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", 51)) {
563 get_schema_support = 1;
564 break;
565 }
566 }
567
568 if (get_schema_support) {
569 errno = 0;
570 if (eaccess(SCHEMA_DIR, W_OK)) {
571 if (errno == ENOENT) {
572 if (mkdir(SCHEMA_DIR, 00755)) {
573 ERROR("Failed to create temp model dir \"%s\" (%s).", SCHEMA_DIR, strerror(errno));
574 return 1;
575 }
576 } else {
577 ERROR("Unable to write to temp model dir \"%s\" (%s).", SCHEMA_DIR, strerror(errno));
578 return 1;
579 }
580 }
581
582 session->ctx = ly_ctx_new(SCHEMA_DIR);
583 } else {
584 /* TODO */
585 session->ctx = ly_ctx_new(NULL);
586 }
587
588loop:
589 /* download all the models first or load them directly */
590 for (i = 0; i < json_object_array_length(array); ++i) {
591 item = json_object_array_get_idx(array, i);
592 capab = json_object_get_string(item);
593 if (!strncmp(capab, "urn:ietf:params:netconf:capability", 34)
594 || !strncmp(capab, "urn:ietf:params:netconf:base", 28)) {
595 continue;
596 }
597
598 /* get module */
599 ptr = strstr(capab, "module=");
600 if (!ptr) {
601 ERROR("Unknown capability \"%s\" could not be parsed.", capab);
602 continue;
603 }
604 ptr += 7;
605 ptr2 = strchr(ptr, '&');
606 if (!ptr2) {
607 ptr2 = ptr + strlen(ptr);
608 }
609 model_name = strndup(ptr, ptr2 - ptr);
610
611 /* get revision */
612 ptr = strstr(capab, "revision=");
613 if (ptr) {
614 ptr += 9;
615 ptr2 = strchr(ptr, '&');
616 if (!ptr2) {
617 ptr2 = ptr + strlen(ptr);
618 }
619 revision = strndup(ptr, ptr2 - ptr);
620 }
621
622 if (get_schema_support) {
623 ctx_download_module(session, model_name, revision, SCHEMA_DIR);
624 } else {
625 module = ly_ctx_get_module(session->ctx, model_name, revision);
626 if (!module) {
627 module = ly_ctx_load_module(session->ctx, NULL, model_name, revision);
628 if (module) {
629 if (!strcmp(module->name, "ietf-netconf")) {
630 ctx_enable_capabs(module, array);
631 } else {
632 ctx_enable_features(module, capab);
633 }
634 }
635 }
636 }
637
638 free(model_name);
639 free(revision);
640 revision = NULL;
641 }
642
643 if (get_schema_support) {
644 /* we have downloaded all the models, load them now */
645 get_schema_support = 0;
646 goto loop;
647 }
648
649 return 0;
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200650}
651
652/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200653 * \brief Connect to NETCONF server
654 *
655 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
656 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100657static unsigned int
658netconf_connect(const char *host, const char *port, const char *user, const char *pass, struct nc_cpblts *cpblts)
Radek Krejci469aab82012-07-22 18:42:20 +0200659{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100660 struct nc_session* session = NULL;
661 struct session_with_mutex *locked_session, *last_session;
Radek Krejci469aab82012-07-22 18:42:20 +0200662
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100663 /* connect to the requested NETCONF server */
664 password = (char*)pass;
665 DEBUG("prepare to connect %s@%s:%s", user, host, port);
666 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
667 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +0200668
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100669 /* if connected successful, add session to the list */
670 if (session != NULL) {
671 if ((locked_session = calloc(1, sizeof(struct session_with_mutex))) == NULL || pthread_mutex_init (&locked_session->lock, NULL) != 0) {
672 nc_session_free(session);
673 session = NULL;
674 free(locked_session);
675 locked_session = NULL;
676 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
677 return 0;
678 }
679 locked_session->session = session;
680 locked_session->last_activity = time(NULL);
681 locked_session->hello_message = NULL;
682 locked_session->closed = 0;
683 pthread_mutex_init(&locked_session->lock, NULL);
684 DEBUG("Before session_lock");
685 /* get exclusive access to sessions_list (conns) */
686 DEBUG("LOCK wrlock %s", __func__);
687 if (pthread_rwlock_wrlock(&session_lock) != 0) {
688 nc_session_free(session);
689 free(locked_session);
690 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
691 return 0;
692 }
693 locked_session->ntfc_subscribed = 0;
694 DEBUG("Add connection to the list");
695 if (!netconf_sessions_list) {
Michal Vaskoc3146782015-11-04 14:46:41 +0100696 netconf_sessions_list = locked_session;
697 } else {
698 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
699 last_session->next = locked_session;
700 locked_session->prev = last_session;
701 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100702 DEBUG("Before session_unlock");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200703
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100704 /* no need to lock session, noone can read it while we have wrlock */
Tomas Cejka47387fd2013-06-10 20:37:46 +0200705
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100706 /* store information about session from hello message for future usage */
707 prepare_status_message(locked_session, session);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200708
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100709 /* create context from the hello message cpabilities */
710 if (prepare_context(locked_session)) {
711 nc_session_free(session);
712 free(locked_session);
713 DEBUG("UNLOCK wrlock %s", __func__);
714 pthread_rwlock_unlock(&session_lock);
715 ERROR("Failed to prepare context");
716 return 0;
717 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200718
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100719 DEBUG("NETCONF session established");
720 locked_session->session_key = session_key_generator;
721 ++session_key_generator;
722 if (session_key_generator == UINT_MAX) {
723 session_key_generator = 1;
724 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200725
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100726 /* unlock session list */
727 DEBUG("UNLOCK wrlock %s", __func__);
728 if (pthread_rwlock_unlock(&session_lock) != 0) {
729 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
730 }
Radek Krejci469aab82012-07-22 18:42:20 +0200731
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100732 return locked_session->session_key;
733 }
734
735 ERROR("Connection could not be established");
736 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200737}
738
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100739static int
740close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +0200741{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100742 DEBUG("lock private lock.");
743 DEBUG("LOCK mutex %s", __func__);
744 if (pthread_mutex_lock(&locked_session->lock) != 0) {
745 ERROR("Error while locking rwlock");
746 }
747 locked_session->ntfc_subscribed = 0;
748 locked_session->closed = 1;
749 if (locked_session->session != NULL) {
750 nc_session_free(locked_session->session);
751 locked_session->session = NULL;
752 }
753 DEBUG("session closed.");
754 DEBUG("unlock private lock.");
755 DEBUG("UNLOCK mutex %s", __func__);
756 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
757 ERROR("Error while locking rwlock");
758 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200759
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100760 DEBUG("unlock session lock.");
761 DEBUG("closed session, disabled notif(?), wait 0.5s");
762 usleep(500000); /* let notification thread stop */
Tomas Cejka47387fd2013-06-10 20:37:46 +0200763
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100764 /* session shouldn't be used by now */
765 /** \todo free all notifications from queue */
766 free(locked_session->notifications);
767 pthread_mutex_destroy(&locked_session->lock);
768 if (locked_session->hello_message != NULL) {
769 json_object_put(locked_session->hello_message);
770 locked_session->hello_message = NULL;
771 }
772 locked_session->session = NULL;
773 ly_ctx_destroy(locked_session->ctx);
774 free(locked_session);
775 locked_session = NULL;
776 DEBUG("NETCONF session closed, everything cleared.");
777 return (EXIT_SUCCESS);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200778}
779
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100780static int
781netconf_close(unsigned int session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +0200782{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100783 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +0200784
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100785 DEBUG("Session to close: %u", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200786
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100787 /* get exclusive (write) access to sessions_list (conns) */
788 DEBUG("lock session lock.");
789 DEBUG("LOCK wrlock %s", __func__);
790 if (pthread_rwlock_wrlock (&session_lock) != 0) {
791 ERROR("Error while locking rwlock");
792 (*reply) = create_error_reply("Internal: Error while locking.");
793 return EXIT_FAILURE;
794 }
795 /* remove session from the active sessions list -> nobody new can now work with session */
796 for (locked_session = netconf_sessions_list;
797 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +0100798 locked_session = locked_session->next);
799
800 if (!locked_session) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100801 ERROR("Could not find the session %u to close.", session_key);
802 (*reply) = create_error_reply("Internal: Error while finding a session.");
Michal Vaskoc3146782015-11-04 14:46:41 +0100803 return EXIT_FAILURE;
804 }
805
806 if (!locked_session->prev) {
807 netconf_sessions_list = netconf_sessions_list->next;
808 netconf_sessions_list->prev = NULL;
809 } else {
810 locked_session->prev->next = locked_session->next;
811 if (locked_session->next) {
812 locked_session->next->prev = locked_session->prev;
813 }
814 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200815
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100816 DEBUG("UNLOCK wrlock %s", __func__);
817 if (pthread_rwlock_unlock (&session_lock) != 0) {
818 ERROR("Error while unlocking rwlock");
819 (*reply) = create_error_reply("Internal: Error while unlocking.");
820 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200821
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100822 if ((locked_session != NULL) && (locked_session->session != NULL)) {
823 return close_and_free_session(locked_session);
824 } else {
825 ERROR("Unknown session to close");
826 (*reply) = create_error_reply("Internal: Unkown session to close.");
827 return (EXIT_FAILURE);
828 }
829 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +0200830}
831
Tomas Cejkac7929632013-10-24 19:25:15 +0200832/**
833 * Test reply message type and return error message.
834 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100835 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +0100836 * \param[in] session_key session ID, 0 to disable disconnect on error
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100837 * \param[in] msgt RPC-REPLY message type
Tomas Cejkac7929632013-10-24 19:25:15 +0200838 * \param[out] data
839 * \return NULL on success
840 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100841json_object *
842netconf_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 +0200843{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100844 NC_REPLY_TYPE replyt;
845 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200846
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100847 /* process the result of the operation */
848 switch (msgt) {
849 case NC_MSG_UNKNOWN:
850 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
851 ERROR("mod_netconf: receiving rpc-reply failed");
852 if (session_key) {
853 netconf_close(session_key, &err);
854 }
855 if (err != NULL) {
856 return err;
857 }
858 return create_error_reply("Internal: Receiving RPC-REPLY failed.");
859 }
860 case NC_MSG_NONE:
861 /* there is error handled by callback */
862 if (data != NULL) {
863 free(*data);
864 (*data) = NULL;
865 }
866 return NULL;
867 case NC_MSG_REPLY:
868 switch (replyt = nc_reply_get_type(reply)) {
869 case NC_REPLY_OK:
870 if ((data != NULL) && (*data != NULL)) {
871 free(*data);
872 (*data) = NULL;
873 }
874 return create_ok_reply();
875 case NC_REPLY_DATA:
876 if (((*data) = nc_reply_get_data(reply)) == NULL) {
877 ERROR("mod_netconf: no data from reply");
878 return create_error_reply("Internal: No data from reply received.");
879 } else {
880 return NULL;
881 }
882 break;
883 case NC_REPLY_ERROR:
884 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
885 if (data != NULL) {
886 free(*data);
887 (*data) = NULL;
888 }
889 return create_error_reply(nc_reply_get_errormsg(reply));
890 default:
891 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
892 if (data != NULL) {
893 free(*data);
894 (*data) = NULL;
895 }
896 return create_error_reply("Unknown type of NETCONF reply.");
897 }
898 break;
899 default:
900 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
901 if (data != NULL) {
902 free(*data);
903 (*data) = NULL;
904 }
905 return create_error_reply("Internal: Unexpected RPC-REPLY message type.");
906 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200907}
908
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100909json_object *
910netconf_unlocked_op(struct nc_session *session, nc_rpc *rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +0200911{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100912 nc_reply* reply = NULL;
913 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200914
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100915 /* check requests */
916 if (rpc == NULL) {
917 ERROR("mod_netconf: rpc is not created");
918 return create_error_reply("Internal error: RPC is not created");
919 }
Tomas Cejka6b886e02013-07-05 09:53:17 +0200920
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100921 if (session != NULL) {
922 /* send the request and get the reply */
923 msgt = netconf_send_recv_timed(session, rpc, 50000, &reply);
924 /* process the result of the operation */
925 return netconf_test_reply(session, 0, msgt, reply, NULL);
926 } else {
927 ERROR("Unknown session to process.");
928 return create_error_reply("Internal error: Unknown session to process.");
929 }
Tomas Cejka6b886e02013-07-05 09:53:17 +0200930}
931
Tomas Cejkac7929632013-10-24 19:25:15 +0200932/**
933 * Perform RPC method that returns data.
934 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100935 * \param[in] session_id session identifier
936 * \param[in] rpc RPC message to perform
937 * \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 +0200938 * \return NULL on success, json object with error otherwise
939 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100940static json_object *
941netconf_op(unsigned int session_key, nc_rpc *rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +0200942{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100943 struct session_with_mutex * locked_session;
944 nc_reply* reply = NULL;
945 json_object *res = NULL;
946 char *data = NULL;
947 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200948
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100949 /* check requests */
950 if (rpc == NULL) {
951 ERROR("mod_netconf: rpc is not created");
952 res = create_error_reply("Internal: RPC could not be created.");
953 data = NULL;
954 goto finished;
955 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200956
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100957 locked_session = session_get_locked(session_key, &res);
958 if (!locked_session) {
959 ERROR("Unknown session or locking failed.");
960 goto finished;
961 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200962
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100963 locked_session->last_activity = time(NULL);
Tomas Cejkac7929632013-10-24 19:25:15 +0200964
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100965 /* send the request and get the reply */
966 msgt = netconf_send_recv_timed(locked_session->session, rpc, 2000000, &reply);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200967
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100968 session_unlock(locked_session);
Tomas Cejkac7929632013-10-24 19:25:15 +0200969
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100970 res = netconf_test_reply(locked_session->session, session_key, msgt, reply, &data);
Radek Krejcia332b692012-11-12 16:15:54 +0100971
Tomas Cejkac7929632013-10-24 19:25:15 +0200972finished:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100973 nc_reply_free(reply);
974 if (received_data != NULL) {
975 (*received_data) = data;
976 } else {
977 if (data != NULL) {
978 free(data);
979 data = NULL;
980 }
981 }
982 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200983}
984
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100985static char *
986netconf_getconfig(unsigned int session_key, NC_DATASTORE source, const char *filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200987{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100988 nc_rpc* rpc;
989 struct nc_filter *f = NULL;
990 struct session_with_mutex *locked_session;
991 char* data = NULL, *data_xml;
992 json_object *res = NULL, *data_cjson;
993 enum json_tokener_error tok_err;
994 struct lyd_node *node, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200995
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100996 /* create filter if set */
997 if (filter != NULL) {
998 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
999 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001000
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001001 /* create requests */
1002 rpc = nc_rpc_getconfig(source, f);
1003 nc_filter_free(f);
1004 if (rpc == NULL) {
1005 ERROR("mod_netconf: creating rpc request failed");
1006 return (NULL);
1007 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001008
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001009 /* tell server to show all elements even if they have default values */
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001010#ifdef HAVE_WITHDEFAULTS_TAGGED
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001011 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001012#else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001013 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET))
1014 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +01001015#endif
Michal Vaskoc3146782015-11-04 14:46:41 +01001016 {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001017 ERROR("mod_netconf: setting withdefaults failed");
1018 }
Tomas Cejka94674662013-09-13 15:55:24 +02001019
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001020 res = netconf_op(session_key, rpc, &data);
1021 nc_rpc_free(rpc);
1022 if (res != NULL) {
1023 (*err) = res;
1024 } else {
1025 (*err) = NULL;
1026 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001027
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001028 if (data) {
1029 for (locked_session = netconf_sessions_list;
1030 locked_session && (locked_session->session_key != session_key);
1031 locked_session = locked_session->next);
1032 /* won't fail */
1033
1034 asprintf(&data_xml, "<get-config>%s</get-config>", data);
1035 node = lyd_parse(locked_session->ctx, data_xml, LYD_XML, LYD_OPT_GETCONFIG | (strict ? LYD_OPT_STRICT : 0));
1036 free(data_xml);
1037 free(data);
1038 if (!node) {
1039 ERROR("Parsing <get-config> data failed.");
1040 return NULL;
1041 }
1042
1043 /* replace XML data with JSON data */
1044 if (lyd_print_mem(&data, node, LYD_JSON)) {
1045 ERROR("Printing JSON <get-config> data failed.");
1046 LY_TREE_FOR(node, sibling) {
1047 lyd_free(sibling);
1048 }
1049 return NULL;
1050 }
1051
1052 /* parse JSON data into cjson */
1053 pthread_mutex_lock(&json_lock);
1054 data_cjson = json_tokener_parse_verbose(data, &tok_err);
1055 if (!data_cjson) {
1056 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1057 pthread_mutex_unlock(&json_lock);
1058 LY_TREE_FOR(node, sibling) {
1059 lyd_free(sibling);
1060 }
1061 free(data);
1062 return NULL;
1063 }
1064 free(data);
1065
1066 /* go simultaneously through both trees and add metadata */
1067 LY_TREE_FOR_SAFE(node, next, sibling) {
1068 node_add_metadata_recursive(sibling, NULL, data_cjson);
1069 lyd_free(sibling);
1070 }
1071
1072 data = strdup(json_object_to_json_string_ext(data_cjson, 0));
1073 json_object_put(data_cjson);
1074 pthread_mutex_unlock(&json_lock);
1075 }
1076
1077 return (data);
Radek Krejci8e4632a2012-07-26 13:40:34 +02001078}
1079
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001080static char *
1081netconf_getschema(unsigned int session_key, const char *identifier, const char *version, const char *format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001082{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001083 nc_rpc* rpc;
1084 char* data = NULL;
1085 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001086
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001087 /* create requests */
1088 rpc = nc_rpc_getschema(identifier, version, format);
1089 if (rpc == NULL) {
1090 ERROR("mod_netconf: creating rpc request failed");
1091 return (NULL);
1092 }
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001093
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001094 res = netconf_op(session_key, rpc, &data);
1095 nc_rpc_free (rpc);
1096 if (res != NULL) {
1097 (*err) = res;
1098 } else {
1099 (*err) = NULL;
1100 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001101
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001102 return (data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001103}
1104
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001105static char *
1106netconf_get(unsigned int session_key, const char* filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001107{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001108 nc_rpc* rpc;
1109 struct nc_filter *f = NULL;
1110 char* data = NULL, *data_xml;
1111 json_object *res = NULL, *data_cjson;
1112 enum json_tokener_error tok_err;
1113 struct session_with_mutex *locked_session;
1114 struct lyd_node *node, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001115
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001116 /* create filter if set */
1117 if (filter != NULL) {
1118 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
1119 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001120
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001121 /* create requests */
1122 rpc = nc_rpc_get(f);
1123 nc_filter_free(f);
1124 if (rpc == NULL) {
1125 ERROR("mod_netconf: creating rpc request failed");
1126 return (NULL);
1127 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001128
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001129 /* tell server to show all elements even if they have default values */
1130 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
1131 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
1132 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
1133 ERROR("mod_netconf: setting withdefaults failed");
1134 }
Tomas Cejka94674662013-09-13 15:55:24 +02001135
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001136 res = netconf_op(session_key, rpc, &data);
1137 nc_rpc_free(rpc);
1138 if (res != NULL) {
1139 (*err) = res;
1140 } else {
1141 (*err) = NULL;
1142 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001143
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001144 if (data) {
1145 for (locked_session = netconf_sessions_list;
1146 locked_session && (locked_session->session_key != session_key);
1147 locked_session = locked_session->next);
1148 /* won't fail */
1149
1150 asprintf(&data_xml, "<get>%s</get>", data);
1151 node = lyd_parse(locked_session->ctx, data_xml, LYD_XML, LYD_OPT_GET | (strict ? LYD_OPT_STRICT : 0));
1152 free(data_xml);
1153 free(data);
1154 if (!node) {
1155 ERROR("Parsing <get> data failed.");
1156 return NULL;
1157 }
1158
1159 /* replace XML data with JSON data */
1160 if (lyd_print_mem(&data, node, LYD_JSON)) {
1161 ERROR("Printing JSON <get> data failed.");
1162 LY_TREE_FOR(node, sibling) {
1163 lyd_free(sibling);
1164 }
1165 return NULL;
1166 }
1167
1168 /* parse JSON data into cjson */
1169 pthread_mutex_lock(&json_lock);
1170 data_cjson = json_tokener_parse_verbose(data, &tok_err);
1171 if (!data_cjson) {
1172 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1173 pthread_mutex_unlock(&json_lock);
1174 LY_TREE_FOR(node, sibling) {
1175 lyd_free(sibling);
1176 }
1177 free(data);
1178 return NULL;
1179 }
1180 free(data);
1181
1182 /* go simultaneously through both trees and add metadata */
1183 LY_TREE_FOR_SAFE(node, next, sibling) {
1184 node_add_metadata_recursive(sibling, NULL, data_cjson);
1185 lyd_free(sibling);
1186 }
1187
1188 data = strdup(json_object_to_json_string_ext(data_cjson, 0));
1189 json_object_put(data_cjson);
1190 pthread_mutex_unlock(&json_lock);
1191 }
1192
1193 return data;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001194}
1195
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001196static json_object *
1197netconf_copyconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, const char *config,
1198 const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001199{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001200 nc_rpc* rpc;
1201 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001202
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001203 /* create requests */
1204 if (source == NC_DATASTORE_CONFIG) {
1205 if (target == NC_DATASTORE_URL) {
1206 /* config, url */
1207 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
1208 } else {
1209 /* config, datastore */
1210 rpc = nc_rpc_copyconfig(source, target, config);
1211 }
1212 } else if (source == NC_DATASTORE_URL) {
1213 if (target == NC_DATASTORE_URL) {
1214 /* url, url */
1215 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
1216 } else {
1217 /* url, datastore */
1218 rpc = nc_rpc_copyconfig(source, target, uri_src);
1219 }
1220 } else {
1221 if (target == NC_DATASTORE_URL) {
1222 /* datastore, url */
1223 rpc = nc_rpc_copyconfig(source, target, uri_trg);
1224 } else {
1225 /* datastore, datastore */
1226 rpc = nc_rpc_copyconfig(source, target);
1227 }
1228 }
1229 if (rpc == NULL) {
1230 ERROR("mod_netconf: creating rpc request failed");
1231 return create_error_reply("Internal: Creating rpc request failed");
1232 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001233
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001234 res = netconf_op(session_key, rpc, NULL);
1235 nc_rpc_free(rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001236
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001237 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001238}
Radek Krejci035bf4e2012-07-25 10:59:09 +02001239
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001240static json_object *
1241netconf_editconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, NC_EDIT_DEFOP_TYPE defop,
1242 NC_EDIT_ERROPT_TYPE erropt, NC_EDIT_TESTOPT_TYPE testopt, const char *config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +02001243{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001244 nc_rpc* rpc;
1245 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001246
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001247 /* create requests */
1248 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
1249 if (rpc == NULL) {
1250 ERROR("mod_netconf: creating rpc request failed");
1251 return create_error_reply("Internal: Creating rpc request failed");
1252 }
Radek Krejci62ab34b2012-07-26 13:42:05 +02001253
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001254 res = netconf_op(session_key, rpc, NULL);
1255 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001256
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001257 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001258}
1259
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001260static json_object *
1261netconf_killsession(unsigned int session_key, const char *sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001262{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001263 nc_rpc *rpc;
1264 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001265
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001266 /* create requests */
1267 rpc = nc_rpc_killsession(sid);
1268 if (rpc == NULL) {
1269 ERROR("mod_netconf: creating rpc request failed");
1270 return create_error_reply("Internal: Creating rpc request failed");
1271 }
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001272
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001273 res = netconf_op(session_key, rpc, NULL);
1274 nc_rpc_free(rpc);
1275 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001276}
1277
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001278static json_object *
1279netconf_onlytargetop(unsigned int session_key, NC_DATASTORE target, nc_rpc *(*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +02001280{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001281 nc_rpc* rpc;
1282 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +02001283
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001284 /* create requests */
1285 rpc = op_func(target);
1286 if (rpc == NULL) {
1287 ERROR("mod_netconf: creating rpc request failed");
1288 return create_error_reply("Internal: Creating rpc request failed");
1289 }
Radek Krejci2f318372012-07-26 14:22:35 +02001290
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001291 res = netconf_op(session_key, rpc, NULL);
1292 nc_rpc_free (rpc);
1293 return res;
Radek Krejci2f318372012-07-26 14:22:35 +02001294}
1295
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001296static json_object *
1297netconf_deleteconfig(unsigned int session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001298{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001299 nc_rpc *rpc = NULL;
1300 json_object *res = NULL;
1301 if (target != NC_DATASTORE_URL) {
1302 rpc = nc_rpc_deleteconfig(target);
1303 } else {
1304 rpc = nc_rpc_deleteconfig(target, url);
1305 }
1306 if (rpc == NULL) {
1307 ERROR("mod_netconf: creating rpc request failed");
1308 return create_error_reply("Internal: Creating rpc request failed");
1309 }
Tomas Cejka404d37e2013-04-13 02:31:35 +02001310
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001311 res = netconf_op(session_key, rpc, NULL);
1312 nc_rpc_free (rpc);
1313 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +02001314}
1315
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001316static json_object *
1317netconf_lock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001318{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001319 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001320}
1321
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001322static json_object *
1323netconf_unlock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001324{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001325 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001326}
1327
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001328static json_object *
1329netconf_generic(unsigned int session_key, const char *content, char **data)
Radek Krejci80c10d92012-07-30 08:38:50 +02001330{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001331 nc_rpc* rpc = NULL;
1332 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +02001333
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001334 /* create requests */
1335 rpc = nc_rpc_generic(content);
1336 if (rpc == NULL) {
1337 ERROR("mod_netconf: creating rpc request failed");
1338 return create_error_reply("Internal: Creating rpc request failed");
1339 }
Radek Krejci80c10d92012-07-30 08:38:50 +02001340
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001341 if (data != NULL) {
1342 // TODO ?free(*data);
1343 (*data) = NULL;
1344 }
Radek Krejci80c10d92012-07-30 08:38:50 +02001345
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001346 /* get session where send the RPC */
1347 res = netconf_op(session_key, rpc, data);
1348 nc_rpc_free (rpc);
1349 return res;
1350}
1351
1352static void
1353node_metadata_children(struct lys_node *node, json_object *parent)
1354{
1355 json_object *array, *array_item;
1356 struct lys_node *child;
1357
1358 array = json_object_new_array();
1359
1360 LY_TREE_FOR(node->child, child) {
1361 array_item = json_object_new_string(child->name);
1362 json_object_array_add(array, array_item);
1363 }
1364
1365 json_object_object_add(parent, "children", array);
1366}
1367
1368static void
1369node_metadata_container(struct lys_node_container *cont, json_object *parent)
1370{
1371 json_object *obj;
1372
1373 /* element type */
1374 obj = json_object_new_string("container");
1375 json_object_object_add(parent, "eltype", obj);
1376
1377 /* config */
1378 if (cont->flags & LYS_CONFIG_R) {
1379 obj = json_object_new_boolean(0);
1380 } else {
1381 obj = json_object_new_boolean(1);
1382 }
1383 json_object_object_add(parent, "config", obj);
1384
1385 /* TODO */
1386 /* description */
1387
1388 /* if-feature */
1389
1390 /* must */
1391
1392 /* presence */
1393
1394 /* reference */
1395
1396 /* status */
1397
1398 /* typedef */
1399
1400 /* when */
1401
1402 /* children */
1403 node_metadata_children((struct lys_node *)cont, parent);
1404}
1405
1406static int
1407node_add_metadata(struct lys_node *node, struct lys_module *module, json_object *parent)
1408{
1409 struct lys_module *cur_module;
1410 json_object *meta_obj;
1411 char *obj_name;
1412
1413 cur_module = node->module;
1414 if (cur_module->type) {
1415 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1416 }
1417 if (cur_module == module) {
1418 asprintf(&obj_name, "$@%s", node->name);
1419 } else {
1420 asprintf(&obj_name, "$@%s:%s", cur_module->name, node->name);
1421 }
1422
1423 /* in (leaf-)lists the metadata could have already been added */
1424 if (json_object_object_get_ex(parent, obj_name, NULL) == TRUE) {
1425 free(obj_name);
1426 return 1;
1427 }
1428
1429 meta_obj = json_object_new_object();
1430
1431 switch (node->nodetype) {
1432 case LYS_CONTAINER:
1433 node_metadata_container((struct lys_node_container *)node, meta_obj);
1434 break;
1435 /* TODO LYS_AUGMENT
1436 LYS_CHOICE
1437 LYS_LEAF
1438 LYS_LEAFLIST
1439 LYS_LIST
1440 LYS_ANYXML
1441 LYS_GROUPING
1442 LYS_CASE
1443 LYS_INPUT
1444 LYS_OUTPUT
1445 LYS_NOTIF
1446 LYS_RPC
1447 LYS_USES*/
1448 }
1449
1450 /* just a precaution */
1451 if (json_object_get_type(parent) != json_type_object) {
1452 ERROR("Internal: wrong JSON type (%s:%d)", __FILE__, __LINE__);
1453 free(obj_name);
1454 return 1;
1455 }
1456
1457 json_object_object_add(parent, obj_name, meta_obj);
1458 free(obj_name);
1459 return 0;
1460}
1461
1462static void
1463node_add_metadata_recursive(struct lyd_node *data_tree, struct lys_module *module, json_object *data_json_parent)
1464{
1465 struct lys_module *cur_module;
1466 struct lys_node *list_schema;
1467 struct lyd_node *child, *list_item;
1468 json_object *child_json, *list_child_json;
1469 char *child_name;
1470 int list_idx;
1471
1472 /* add data_tree metadata */
1473 if (node_add_metadata(data_tree->schema, module, data_json_parent)) {
1474 return;
1475 }
1476
1477 /* get data_tree module */
1478 cur_module = data_tree->schema->module;
1479 if (cur_module->type) {
1480 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1481 }
1482
1483 if (!(data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1484 /* print correct data_tree JSON name */
1485 if (cur_module == module) {
1486 asprintf(&child_name, "%s", data_tree->schema->name);
1487 } else {
1488 asprintf(&child_name, "%s:%s", cur_module->name, data_tree->schema->name);
1489 }
1490
1491 /* go down in JSON object */
1492 if (json_object_object_get_ex(data_json_parent, child_name, &child_json) == FALSE) {
1493 ERROR("Internal: failed to get JSON object \"%s\".", child_name);
1494 free(child_name);
1495 return;
1496 }
1497 free(child_name);
1498
1499 if (data_tree->schema->nodetype == LYS_LIST) {
1500 if (json_object_get_type(child_json) != json_type_array) {
1501 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1502 return;
1503 }
1504 /* go down in data tree for every item, we process them all now, skip later
1505 * (metadata duplicate will be detected at the beginning of this function) */
1506 list_idx = 0;
1507 list_schema = data_tree->schema;
1508
1509 LY_TREE_FOR(data_tree, list_item) {
1510 /* another list member */
1511 if (list_item->schema == list_schema) {
1512 list_child_json = json_object_array_get_idx(child_json, list_idx);
1513 if (!list_child_json) {
1514 ERROR("Internal: list \"%s\" idx out-of-bounds", list_schema->name);
1515 return;
1516 }
1517 LY_TREE_FOR(list_item->child, child) {
1518 node_add_metadata_recursive(child, cur_module, list_child_json);
1519 }
1520
1521 ++list_idx;
1522 }
1523 }
1524 } else {
1525 if (json_object_get_type(child_json) != json_type_object) {
1526 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1527 return;
1528 }
1529 /* go down in data tree */
1530 LY_TREE_FOR(data_tree->child, child) {
1531 node_add_metadata_recursive(child, cur_module, child_json);
1532 }
1533 }
1534 }
1535}
1536
1537static void
1538node_add_children_with_metadata_recursive(struct lys_node *node, struct lys_module *module, json_object *parent)
1539{
1540 struct lys_module *cur_module;
1541 struct lys_node *child;
1542 json_object *node_json;
1543 char *json_name;
1544
1545 /* add node metadata */
1546 if (node_add_metadata(node, module, parent)) {
1547 ERROR("Internal: metadata duplicate for \"%s\".", node->name);
1548 return;
1549 }
1550
1551 /* get node module */
1552 cur_module = node->module;
1553 if (cur_module->type) {
1554 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1555 }
1556
1557 /* create JSON object for child metadata */
1558 node_json = json_object_new_object();
1559 if (cur_module == module) {
1560 json_object_object_add(parent, node->name, node_json);
1561 } else {
1562 asprintf(&json_name, "%s:%s", cur_module->name, node->name);
1563 json_object_object_add(parent, json_name, node_json);
1564 free(json_name);
1565 }
1566
1567 LY_TREE_FOR(node->child, child) {
1568 node_add_children_with_metadata_recursive(child, cur_module, node_json);
1569 }
1570}
1571
1572static json_object *
1573libyang_query(unsigned int session_key, const char *filter, int load_children)
1574{
1575 struct lys_node *node;
1576 struct session_with_mutex *locked_session;
1577 json_object *ret = NULL, *data;
1578
1579 locked_session = session_get_locked(session_key, &ret);
1580 if (!locked_session) {
1581 ERROR("Locking failed or session not found.");
1582 goto finish;
1583 }
1584
1585 locked_session->last_activity = time(NULL);
1586
1587 /* collect schema metadata and create reply */
1588 node = ly_ctx_get_node(locked_session->ctx, filter);
1589
1590 if (node) {
1591 pthread_mutex_lock(&json_lock);
1592 data = json_object_new_object();
1593
1594 if (load_children) {
1595 node_add_children_with_metadata_recursive(node, NULL, data);
1596 } else {
1597 node_add_metadata(node, NULL, data);
1598 }
1599
1600 pthread_mutex_unlock(&json_lock);
1601 ret = create_data_reply(json_object_to_json_string(data));
1602 json_object_put(data);
1603 } else {
1604 ret = create_error_reply("Failed to resolve XPath filter node.");
1605 }
1606
1607 session_unlock(locked_session);
1608
1609finish:
1610 return ret;
1611}
1612
1613static json_object *
1614libyang_merge(unsigned int session_key, const char *config)
1615{
1616 struct lyd_node *data_tree = NULL, *sibling;
1617 struct session_with_mutex *locked_session;
1618 json_object *ret = NULL, *data_json = NULL;
1619 enum json_tokener_error err = 0;
1620
1621 locked_session = session_get_locked(session_key, &ret);
1622 if (!locked_session) {
1623 ERROR("Locking failed or session not found.");
1624 goto finish;
1625 }
1626
1627 locked_session->last_activity = time(NULL);
1628
1629 data_tree = lyd_parse(locked_session->ctx, config, LYD_JSON, LYD_OPT_STRICT);
1630 if (!data_tree) {
1631 ERROR("Creating data tree failed.");
1632 ret = create_error_reply("Failed to create data tree from JSON config.");
1633 session_unlock(locked_session);
1634 goto finish;
1635 }
1636
1637 session_unlock(locked_session);
1638
1639 pthread_mutex_lock(&json_lock);
1640 data_json = json_tokener_parse_verbose(config, &err);
1641 if (!data_json) {
1642 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(err));
1643 pthread_mutex_unlock(&json_lock);
1644 ret = create_error_reply(json_tokener_error_desc(err));
1645 goto finish;
1646 }
1647
1648 /* go simultaneously through both trees and add metadata */
1649 LY_TREE_FOR(data_tree, sibling) {
1650 node_add_metadata_recursive(sibling, NULL, data_json);
1651 }
1652 pthread_mutex_unlock(&json_lock);
1653 ret = create_data_reply(json_object_to_json_string(data_json));
1654
1655finish:
1656 LY_TREE_FOR(data_tree, sibling) {
1657 lyd_free(sibling);
1658 }
1659 json_object_put(data_json);
1660 return ret;
Radek Krejci80c10d92012-07-30 08:38:50 +02001661}
1662
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001663/**
1664 * @}
1665 *//* netconf_operations */
1666
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001667void
1668clb_print(NC_VERB_LEVEL level, const char *msg)
Radek Krejci469aab82012-07-22 18:42:20 +02001669{
Tomas Cejka8a86cc22014-09-18 15:35:07 +02001670#define FOREACH(I) \
Tomas Cejkacf44e522015-04-24 17:29:21 +02001671 I(NC_VERB_ERROR) I(NC_VERB_WARNING)
1672
1673#define CASE(VAL) case VAL: ERROR("%s: %s", #VAL, msg); \
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001674 break;
Tomas Cejka8a86cc22014-09-18 15:35:07 +02001675
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001676 switch (level) {
1677 FOREACH(CASE);
1678 case NC_VERB_VERBOSE:
1679 case NC_VERB_DEBUG:
1680 DEBUG("DEBUG: %s", msg);
1681 break;
1682 }
1683 if (level == NC_VERB_ERROR) {
1684 /* return global error */
1685 netconf_callback_error_process(NULL /* tag */, NULL /* type */,
1686 NULL /* severity */, NULL /* apptag */,
1687 NULL /* path */, msg, NULL /* attribute */,
1688 NULL /* element */, NULL /* ns */, NULL /* sid */);
1689 }
Radek Krejci469aab82012-07-22 18:42:20 +02001690}
1691
Tomas Cejka64b87482013-06-03 16:30:53 +02001692/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02001693 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02001694 * Caller should free message memory.
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001695 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02001696 * \return pointer to message
1697 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001698char *
1699get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02001700{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001701 /* read json in chunked framing */
1702 unsigned int buffer_size = 0;
1703 ssize_t buffer_len = 0;
1704 char *buffer = NULL;
1705 char c;
1706 ssize_t ret;
1707 int i, chunk_len;
1708 char chunk_len_str[12];
Tomas Cejka64b87482013-06-03 16:30:53 +02001709
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001710 while (1) {
1711 /* read chunk length */
1712 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
1713 if (buffer != NULL) {
1714 free (buffer);
1715 buffer = NULL;
1716 }
1717 break;
1718 }
1719 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
1720 if (buffer != NULL) {
1721 free (buffer);
1722 buffer = NULL;
1723 }
1724 break;
1725 }
1726 i=0;
1727 memset (chunk_len_str, 0, 12);
1728 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
1729 if (i==0 && c == '#') {
1730 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
1731 /* end but invalid */
1732 if (buffer != NULL) {
1733 free (buffer);
1734 buffer = NULL;
1735 }
1736 }
1737 /* end of message, double-loop break */
1738 goto msg_complete;
1739 }
1740 chunk_len_str[i++] = c;
1741 if (i==11) {
1742 ERROR("Message is too long, buffer for length is not big enought!!!!");
1743 break;
1744 }
1745 }
1746 if (c != '\n') {
1747 if (buffer != NULL) {
1748 free (buffer);
1749 buffer = NULL;
1750 }
1751 break;
1752 }
1753 chunk_len_str[i] = 0;
1754 if ((chunk_len = atoi (chunk_len_str)) == 0) {
1755 if (buffer != NULL) {
1756 free (buffer);
1757 buffer = NULL;
1758 }
1759 break;
1760 }
1761 buffer_size += chunk_len+1;
1762 buffer = realloc (buffer, sizeof(char)*buffer_size);
1763 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
1764 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
1765 if (buffer != NULL) {
1766 free (buffer);
1767 buffer = NULL;
1768 }
1769 break;
1770 }
1771 buffer_len += ret;
1772 }
Tomas Cejka64b87482013-06-03 16:30:53 +02001773msg_complete:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001774 return buffer;
Tomas Cejka64b87482013-06-03 16:30:53 +02001775}
1776
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001777NC_DATASTORE
1778parse_datastore(const char *ds)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001779{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001780 if (strcmp(ds, "running") == 0) {
1781 return NC_DATASTORE_RUNNING;
1782 } else if (strcmp(ds, "startup") == 0) {
1783 return NC_DATASTORE_STARTUP;
1784 } else if (strcmp(ds, "candidate") == 0) {
1785 return NC_DATASTORE_CANDIDATE;
1786 } else if (strcmp(ds, "url") == 0) {
1787 return NC_DATASTORE_URL;
1788 } else if (strcmp(ds, "config") == 0) {
1789 return NC_DATASTORE_CONFIG;
1790 }
1791 return -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001792}
1793
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001794NC_EDIT_TESTOPT_TYPE
1795parse_testopt(const char *t)
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001796{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001797 if (strcmp(t, "notset") == 0) {
1798 return NC_EDIT_TESTOPT_NOTSET;
1799 } else if (strcmp(t, "testset") == 0) {
1800 return NC_EDIT_TESTOPT_TESTSET;
1801 } else if (strcmp(t, "set") == 0) {
1802 return NC_EDIT_TESTOPT_SET;
1803 } else if (strcmp(t, "test") == 0) {
1804 return NC_EDIT_TESTOPT_TEST;
1805 }
1806 return NC_EDIT_TESTOPT_ERROR;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001807}
1808
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001809json_object *
1810create_error_reply(const char *errmess)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001811{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001812 json_object *reply, *array;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001813
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001814 pthread_mutex_lock(&json_lock);
1815 reply = json_object_new_object();
1816 array = json_object_new_array();
1817 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1818 json_object_array_add(array, json_object_new_string(errmess));
1819 json_object_object_add(reply, "errors", array);
1820 pthread_mutex_unlock(&json_lock);
1821
1822 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001823}
1824
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001825json_object *
1826create_data_reply(const char *data)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001827{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001828 pthread_mutex_lock(&json_lock);
1829 json_object *reply = json_object_new_object();
1830 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1831 json_object_object_add(reply, "data", json_object_new_string(data));
1832 pthread_mutex_unlock(&json_lock);
1833 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001834}
1835
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001836json_object *
1837create_ok_reply(void)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001838{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001839 pthread_mutex_lock(&json_lock);
1840 json_object *reply = json_object_new_object();
1841 reply = json_object_new_object();
1842 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1843 pthread_mutex_unlock(&json_lock);
1844 return reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001845}
1846
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001847json_object *
1848create_replies(void)
Tomas Cejka09629492014-07-10 15:58:06 +02001849{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001850 json_object *replies;
1851
1852 pthread_mutex_lock(&json_lock);
1853 replies = json_object_new_object();
1854 pthread_mutex_unlock(&json_lock);
1855
1856 return replies;
Tomas Cejka09629492014-07-10 15:58:06 +02001857}
1858
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001859void
1860add_reply(json_object *replies, json_object *reply, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001861{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001862 char *str;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001863
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001864 asprintf(&str, "%u", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001865
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001866 pthread_mutex_lock(&json_lock);
1867 json_object_object_add(replies, str, reply);
1868 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001869
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001870 free(str);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001871}
1872
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001873char *
1874get_param_string(json_object *data, const char *name)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001875{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001876 json_object *js_tmp = NULL;
1877 char *res = NULL;
1878 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
1879 res = strdup(json_object_get_string(js_tmp));
1880 }
1881 return res;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001882}
1883
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001884json_object *
1885handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001886{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001887 char *host = NULL;
1888 char *port = NULL;
1889 char *user = NULL;
1890 char *pass = NULL;
1891 json_object *reply = NULL;
1892 unsigned int session_key = 0;
1893 struct nc_cpblts* cpblts = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001894
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001895 DEBUG("Request: connect");
1896 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001897
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001898 host = get_param_string(request, "host");
1899 port = get_param_string(request, "port");
1900 user = get_param_string(request, "user");
1901 pass = get_param_string(request, "pass");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001902
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001903 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001904
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001905 DEBUG("host: %s, port: %s, user: %s", host, port, user);
1906 if ((host == NULL) || (user == NULL)) {
1907 ERROR("Cannot connect - insufficient input.");
1908 session_key = 0;
1909 } else {
1910 session_key = netconf_connect(host, port, user, pass, cpblts);
1911 DEBUG("Session key: %u", session_key);
1912 }
1913 if (cpblts != NULL) {
1914 nc_cpblts_free(cpblts);
1915 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001916
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001917 GETSPEC_ERR_REPLY
1918
1919 pthread_mutex_lock(&json_lock);
1920 if (session_key == 0) {
1921 /* negative reply */
1922 if (err_reply == NULL) {
1923 reply = json_object_new_object();
1924 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1925 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
1926 ERROR("Connection failed.");
1927 } else {
1928 /* use filled err_reply from libnetconf's callback */
1929 reply = err_reply;
1930 ERROR("Connect - error from libnetconf's callback.");
1931 }
1932 } else {
1933 /* positive reply */
1934 reply = json_object_new_object();
1935 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1936 json_object_object_add(reply, "session", json_object_new_int(session_key));
1937 }
1938 memset(pass, 0, strlen(pass));
1939 pthread_mutex_unlock(&json_lock);
1940 CHECK_AND_FREE(host);
1941 CHECK_AND_FREE(user);
1942 CHECK_AND_FREE(port);
1943 CHECK_AND_FREE(pass);
1944 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001945}
1946
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001947json_object *
1948handle_op_disconnect(json_object *UNUSED(request), unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001949{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001950 json_object *reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001951
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001952 DEBUG("Request: disconnect (session %u)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001953
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001954 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
1955 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
1956 } else {
1957 reply = create_ok_reply();
1958 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001959
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001960 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001961}
1962
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001963json_object *
1964handle_op_get(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001965{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001966 char *filter = NULL;
1967 char *data = NULL;
1968 json_object *reply = NULL, *obj;
1969 int strict;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001970
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001971 DEBUG("Request: get (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001972
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001973 pthread_mutex_lock(&json_lock);
1974 filter = get_param_string(request, "filter");
1975 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
1976 pthread_mutex_unlock(&json_lock);
1977 reply = create_error_reply("Missing strict parameter.");
1978 return reply;
1979 }
1980 strict = json_object_get_boolean(obj);
1981 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001982
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001983 if ((data = netconf_get(session_key, filter, strict, &reply)) == NULL) {
1984 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
1985 } else {
1986 reply = create_data_reply(data);
1987 free(data);
1988 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001989
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001990 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001991}
1992
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001993json_object *
1994handle_op_getconfig(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001995{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001996 NC_DATASTORE ds_type_s = -1;
1997 char *filter = NULL;
1998 char *data = NULL;
1999 char *source = NULL;
2000 json_object *reply = NULL, *obj;
2001 int strict;
Tomas Cejkab4d05872014-02-14 22:44:38 +01002002
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002003 DEBUG("Request: get-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002004
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002005 pthread_mutex_lock(&json_lock);
2006 filter = get_param_string(request, "filter");
2007 source = get_param_string(request, "source");
2008 if (source != NULL) {
2009 ds_type_s = parse_datastore(source);
2010 }
2011 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2012 pthread_mutex_unlock(&json_lock);
2013 reply = create_error_reply("Missing strict parameter.");
2014 return reply;
2015 }
2016 strict = json_object_get_boolean(obj);
2017 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002018
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002019 if ((int)ds_type_s == -1) {
2020 reply = create_error_reply("Invalid source repository type requested.");
2021 goto finalize;
2022 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002023
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002024 if ((data = netconf_getconfig(session_key, ds_type_s, filter, strict, &reply)) == NULL) {
2025 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
2026 } else {
2027 reply = create_data_reply(data);
2028 free(data);
2029 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002030
Tomas Cejka09629492014-07-10 15:58:06 +02002031finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002032 CHECK_AND_FREE(filter);
2033 CHECK_AND_FREE(source);
2034 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002035}
2036
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002037json_object *
2038handle_op_editconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002039{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002040 NC_DATASTORE ds_type_s = -1;
2041 NC_DATASTORE ds_type_t = -1;
2042 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
2043 NC_EDIT_ERROPT_TYPE erropt_type = 0;
2044 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
2045 char *defop = NULL;
2046 char *erropt = NULL;
2047 char *config = NULL;
2048 char *source = NULL;
2049 char *target = NULL;
2050 char *testopt = NULL;
2051 char *urisource = NULL;
2052 json_object *reply = NULL, *configs, *obj;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002053
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002054 DEBUG("Request: edit-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002055
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002056 pthread_mutex_lock(&json_lock);
2057 /* get parameters */
2058 target = get_param_string(request, "target");
2059 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2060 pthread_mutex_unlock(&json_lock);
2061 reply = create_error_reply("Missing configs parameter.");
2062 goto finalize;
2063 }
2064 obj = json_object_array_get_idx(configs, idx);
2065 config = strdup(json_object_get_string(obj));
Tomas Cejkad5b53772013-06-08 23:01:07 +02002066
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002067 source = get_param_string(request, "source");
2068 defop = get_param_string(request, "default-operation");
2069 erropt = get_param_string(request, "error-option");
2070 urisource = get_param_string(request, "uri-source");
2071 testopt = get_param_string(request, "test-option");
2072 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002073
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002074 if (target != NULL) {
2075 ds_type_t = parse_datastore(target);
2076 }
2077 if (source != NULL) {
2078 ds_type_s = parse_datastore(source);
2079 } else {
2080 /* source is optional, default value is config */
2081 ds_type_s = NC_DATASTORE_CONFIG;
2082 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002083
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002084 if (defop != NULL) {
2085 if (strcmp(defop, "merge") == 0) {
2086 defop_type = NC_EDIT_DEFOP_MERGE;
2087 } else if (strcmp(defop, "replace") == 0) {
2088 defop_type = NC_EDIT_DEFOP_REPLACE;
2089 } else if (strcmp(defop, "none") == 0) {
2090 defop_type = NC_EDIT_DEFOP_NONE;
2091 } else {
2092 reply = create_error_reply("Invalid default-operation parameter.");
2093 goto finalize;
2094 }
2095 } else {
2096 defop_type = NC_EDIT_DEFOP_NOTSET;
2097 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002098
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002099 if (erropt != NULL) {
2100 if (strcmp(erropt, "continue-on-error") == 0) {
2101 erropt_type = NC_EDIT_ERROPT_CONT;
2102 } else if (strcmp(erropt, "stop-on-error") == 0) {
2103 erropt_type = NC_EDIT_ERROPT_STOP;
2104 } else if (strcmp(erropt, "rollback-on-error") == 0) {
2105 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
2106 } else {
2107 reply = create_error_reply("Invalid error-option parameter.");
2108 goto finalize;
2109 }
2110 } else {
2111 erropt_type = 0;
2112 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002113
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002114 if ((int)ds_type_t == -1) {
2115 reply = create_error_reply("Invalid target repository type requested.");
2116 goto finalize;
2117 }
2118 if (ds_type_s == NC_DATASTORE_CONFIG) {
2119 if (config == NULL) {
2120 reply = create_error_reply("Invalid config data parameter.");
2121 goto finalize;
2122 }
2123 } else if (ds_type_s == NC_DATASTORE_URL){
2124 if (urisource == NULL) {
2125 reply = create_error_reply("Invalid uri-source parameter.");
2126 goto finalize;
2127 }
2128 config = urisource;
2129 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002130
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002131 if (testopt != NULL) {
2132 testopt_type = parse_testopt(testopt);
2133 } else {
2134 testopt_type = NC_EDIT_TESTOPT_TESTSET;
2135 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002136
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002137 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 +02002138
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002139 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002140
Tomas Cejka09629492014-07-10 15:58:06 +02002141finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002142 CHECK_AND_FREE(defop);
2143 CHECK_AND_FREE(erropt);
2144 CHECK_AND_FREE(config);
2145 CHECK_AND_FREE(source);
2146 CHECK_AND_FREE(urisource);
2147 CHECK_AND_FREE(target);
2148 CHECK_AND_FREE(testopt);
2149
2150 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002151}
2152
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002153json_object *
2154handle_op_copyconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002155{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002156 NC_DATASTORE ds_type_s = -1;
2157 NC_DATASTORE ds_type_t = -1;
2158 char *config = NULL;
2159 char *target = NULL;
2160 char *source = NULL;
2161 char *uri_src = NULL;
2162 char *uri_trg = NULL;
2163 json_object *reply = NULL, *configs, *obj;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002164
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002165 DEBUG("Request: copy-config (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002166
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002167 /* get parameters */
2168 pthread_mutex_lock(&json_lock);
2169 target = get_param_string(request, "target");
2170 source = get_param_string(request, "source");
2171 uri_src = get_param_string(request, "uri-source");
2172 uri_trg = get_param_string(request, "uri-target");
2173 if (!strcmp(source, "config")) {
2174 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2175 pthread_mutex_unlock(&json_lock);
2176 reply = create_error_reply("Missing configs parameter.");
2177 goto finalize;
2178 }
2179 obj = json_object_array_get_idx(configs, idx);
2180 if (!obj) {
2181 pthread_mutex_unlock(&json_lock);
2182 reply = create_error_reply("Configs array parameter shorter than sessions.");
2183 goto finalize;
2184 }
2185 config = strdup(json_object_get_string(obj));
2186 }
2187 pthread_mutex_unlock(&json_lock);
2188
2189 if (target != NULL) {
2190 ds_type_t = parse_datastore(target);
2191 }
2192 if (source != NULL) {
2193 ds_type_s = parse_datastore(source);
2194 }
2195
2196 if ((int)ds_type_s == -1) {
2197 /* invalid source datastore specified */
2198 reply = create_error_reply("Invalid source repository type requested.");
2199 goto finalize;
2200 }
2201
2202 if ((int)ds_type_t == -1) {
2203 /* invalid target datastore specified */
2204 reply = create_error_reply("Invalid target repository type requested.");
2205 goto finalize;
2206 }
2207
2208 if (ds_type_s == NC_DATASTORE_URL) {
2209 if (uri_src == NULL) {
2210 uri_src = "";
2211 }
2212 }
2213 if (ds_type_t == NC_DATASTORE_URL) {
2214 if (uri_trg == NULL) {
2215 uri_trg = "";
2216 }
2217 }
2218 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
2219
2220 CHECK_ERR_SET_REPLY
2221
2222finalize:
2223 CHECK_AND_FREE(config);
2224 CHECK_AND_FREE(target);
2225 CHECK_AND_FREE(source);
2226 CHECK_AND_FREE(uri_src);
2227 CHECK_AND_FREE(uri_trg);
2228
2229 return reply;
2230}
2231
2232json_object *
2233handle_op_deleteconfig(json_object *request, unsigned int session_key)
2234{
2235 json_object *reply;
2236 NC_DATASTORE ds_type = -1;
2237 char *target, *url;
2238
2239 DEBUG("Request: delete-config (session %u)", session_key);
2240
2241 pthread_mutex_lock(&json_lock);
2242 target = get_param_string(request, "target");
2243 url = get_param_string(request, "url");
2244 pthread_mutex_unlock(&json_lock);
2245
2246 if (target != NULL) {
2247 ds_type = parse_datastore(target);
2248 }
2249 if ((int)ds_type == -1) {
2250 reply = create_error_reply("Invalid target repository type requested.");
2251 goto finalize;
2252 }
2253 if (ds_type == NC_DATASTORE_URL) {
2254 if (!url) {
2255 url = "";
2256 }
2257 }
2258
2259 reply = netconf_deleteconfig(session_key, ds_type, url);
2260
2261 CHECK_ERR_SET_REPLY
2262 if (reply == NULL) {
2263 reply = create_ok_reply();
2264 }
2265
2266finalize:
2267 CHECK_AND_FREE(target);
2268 CHECK_AND_FREE(url);
2269 return reply;
2270}
2271
2272json_object *
2273handle_op_lock(json_object *request, unsigned int session_key)
2274{
2275 json_object *reply;
2276 NC_DATASTORE ds_type = -1;
2277 char *target;
2278
2279 DEBUG("Request: lock (session %u)", session_key);
2280
2281 pthread_mutex_lock(&json_lock);
2282 target = get_param_string(request, "target");
2283 pthread_mutex_unlock(&json_lock);
2284
2285 if (target != NULL) {
2286 ds_type = parse_datastore(target);
2287 }
2288 if ((int)ds_type == -1) {
2289 reply = create_error_reply("Invalid target repository type requested.");
2290 goto finalize;
2291 }
2292
2293 reply = netconf_lock(session_key, ds_type);
2294
2295 CHECK_ERR_SET_REPLY
2296 if (reply == NULL) {
2297 reply = create_ok_reply();
2298 }
2299
2300finalize:
2301 CHECK_AND_FREE(target);
2302 return reply;
2303}
2304
2305json_object *
2306handle_op_unlock(json_object *request, unsigned int session_key)
2307{
2308 json_object *reply;
2309 NC_DATASTORE ds_type = -1;
2310 char *target;
2311
2312 DEBUG("Request: unlock (session %u)", session_key);
2313
2314 pthread_mutex_lock(&json_lock);
2315 target = get_param_string(request, "target");
2316 pthread_mutex_unlock(&json_lock);
2317
2318 if (target != NULL) {
2319 ds_type = parse_datastore(target);
2320 }
2321 if ((int)ds_type == -1) {
2322 reply = create_error_reply("Invalid target repository type requested.");
2323 goto finalize;
2324 }
2325
2326 reply = netconf_unlock(session_key, ds_type);
2327
2328 CHECK_ERR_SET_REPLY
2329 if (reply == NULL) {
2330 reply = create_ok_reply();
2331 }
2332
2333finalize:
2334 CHECK_AND_FREE(target);
2335 return reply;
2336}
2337
2338json_object *
2339handle_op_kill(json_object *request, unsigned int session_key)
2340{
2341 json_object *reply = NULL;
2342 char *sid = NULL;
2343
2344 DEBUG("Request: kill-session (session %u)", session_key);
2345
2346 pthread_mutex_lock(&json_lock);
2347 sid = get_param_string(request, "session-id");
2348 pthread_mutex_unlock(&json_lock);
2349
2350 if (sid == NULL) {
2351 reply = create_error_reply("Missing session-id parameter.");
2352 goto finalize;
2353 }
2354
2355 reply = netconf_killsession(session_key, sid);
2356
2357 CHECK_ERR_SET_REPLY
2358
2359finalize:
2360 CHECK_AND_FREE(sid);
2361 return reply;
2362}
2363
2364json_object *
2365handle_op_info(json_object *UNUSED(request), unsigned int session_key)
2366{
2367 json_object *reply = NULL;
2368 struct session_with_mutex *locked_session = NULL;
2369 DEBUG("Request: get info about session %u", session_key);
2370
2371 DEBUG("LOCK wrlock %s", __func__);
2372 if (pthread_rwlock_rdlock(&session_lock) != 0) {
2373 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2374 }
2375
2376 for (locked_session = netconf_sessions_list;
2377 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002378 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002379 if (locked_session != NULL) {
2380 DEBUG("LOCK mutex %s", __func__);
2381 pthread_mutex_lock(&locked_session->lock);
2382 DEBUG("UNLOCK wrlock %s", __func__);
2383 if (pthread_rwlock_unlock(&session_lock) != 0) {
2384 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2385 }
2386 if (locked_session->hello_message != NULL) {
2387 reply = locked_session->hello_message;
2388 } else {
2389 reply = create_error_reply("Invalid session identifier.");
2390 }
2391 DEBUG("UNLOCK mutex %s", __func__);
2392 pthread_mutex_unlock(&locked_session->lock);
2393 } else {
2394 DEBUG("UNLOCK wrlock %s", __func__);
2395 if (pthread_rwlock_unlock(&session_lock) != 0) {
2396 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2397 }
2398 reply = create_error_reply("Invalid session identifier.");
2399 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002400
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002401 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002402}
2403
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002404json_object *
2405handle_op_generic(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002406{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002407 json_object *reply = NULL, *contents, *obj;
2408 char *config = NULL;
2409 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002410
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002411 DEBUG("Request: generic request (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002412
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002413 pthread_mutex_lock(&json_lock);
2414 if (json_object_object_get_ex(request, "contents", &contents) == FALSE) {
2415 pthread_mutex_unlock(&json_lock);
2416 reply = create_error_reply("Missing contents parameter.");
2417 goto finalize;
2418 }
2419 obj = json_object_array_get_idx(contents, idx);
2420 if (!obj) {
2421 pthread_mutex_unlock(&json_lock);
2422 reply = create_error_reply("Contents array parameter shorter than sessions.");
2423 goto finalize;
2424 }
2425 config = strdup(json_object_get_string(obj));
2426 pthread_mutex_unlock(&json_lock);
2427
2428 reply = netconf_generic(session_key, config, &data);
2429 if (reply == NULL) {
2430 GETSPEC_ERR_REPLY
2431 if (err_reply != NULL) {
2432 /* use filled err_reply from libnetconf's callback */
2433 reply = err_reply;
2434 }
2435 } else {
2436 if (data == NULL) {
2437 pthread_mutex_lock(&json_lock);
2438 reply = json_object_new_object();
2439 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2440 pthread_mutex_unlock(&json_lock);
2441 } else {
2442 reply = create_data_reply(data);
2443 free(data);
2444 }
2445 }
2446
2447finalize:
2448 CHECK_AND_FREE(config);
2449 return reply;
2450}
2451
2452json_object *
2453handle_op_getschema(json_object *request, unsigned int session_key)
2454{
2455 char *data = NULL;
2456 char *identifier = NULL;
2457 char *version = NULL;
2458 char *format = NULL;
2459 json_object *reply = NULL;
2460
2461 DEBUG("Request: get-schema (session %u)", session_key);
2462
2463 pthread_mutex_lock(&json_lock);
2464 identifier = get_param_string(request, "identifier");
2465 version = get_param_string(request, "version");
2466 format = get_param_string(request, "format");
2467 pthread_mutex_unlock(&json_lock);
2468
2469 if (identifier == NULL) {
2470 reply = create_error_reply("No identifier for get-schema supplied.");
2471 goto finalize;
2472 }
2473
2474 DEBUG("get-schema(version: %s, format: %s)", version, format);
2475 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
2476 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
2477 } else {
2478 reply = create_data_reply(data);
2479 free(data);
2480 }
2481
2482finalize:
2483 CHECK_AND_FREE(identifier);
2484 CHECK_AND_FREE(version);
2485 CHECK_AND_FREE(format);
2486 return reply;
2487}
2488
2489json_object *
2490handle_op_reloadhello(json_object *UNUSED(request), unsigned int session_key)
2491{
2492 struct nc_session *temp_session = NULL;
2493 struct session_with_mutex * locked_session = NULL;
2494 json_object *reply = NULL;
2495
2496 DEBUG("Request: reload hello (session %u)", session_key);
2497
2498 DEBUG("LOCK wrlock %s", __func__);
2499 if (pthread_rwlock_wrlock(&session_lock) != 0) {
2500 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2501 return NULL;
2502 }
2503
2504 for (locked_session = netconf_sessions_list;
2505 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002506 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002507 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
2508 DEBUG("LOCK mutex %s", __func__);
2509 pthread_mutex_lock(&locked_session->lock);
2510 DEBUG("creating temporary NC session.");
2511 temp_session = nc_session_connect_channel(locked_session->session, NULL);
2512 if (temp_session != NULL) {
2513 prepare_status_message(locked_session, temp_session);
2514 DEBUG("closing temporal NC session.");
2515 nc_session_free(temp_session);
2516 temp_session = NULL;
2517 } else {
2518 DEBUG("Reload hello failed due to channel establishment");
2519 reply = create_error_reply("Reload was unsuccessful, connection failed.");
2520 }
2521 DEBUG("UNLOCK mutex %s", __func__);
2522 pthread_mutex_unlock(&locked_session->lock);
2523 DEBUG("UNLOCK wrlock %s", __func__);
2524 if (pthread_rwlock_unlock(&session_lock) != 0) {
2525 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2526 }
2527 } else {
2528 DEBUG("UNLOCK wrlock %s", __func__);
2529 if (pthread_rwlock_unlock(&session_lock) != 0) {
2530 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2531 }
2532 reply = create_error_reply("Invalid session identifier.");
2533 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002534
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002535 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
2536 reply = locked_session->hello_message;
2537 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002538
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002539 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002540}
2541
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002542void
2543notification_history(time_t eventtime, const char *content)
Tomas Cejka6b886e02013-07-05 09:53:17 +02002544{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002545 json_object *notif_history_array = (json_object *)pthread_getspecific(notif_history_key);
2546 if (notif_history_array == NULL) {
2547 ERROR("No list of notification history found.");
2548 return;
2549 }
2550 DEBUG("Got notification from history %lu.", (long unsigned)eventtime);
2551 pthread_mutex_lock(&json_lock);
2552 json_object *notif = json_object_new_object();
2553 if (notif == NULL) {
2554 ERROR("Could not allocate memory for notification (json).");
2555 goto failed;
2556 }
2557 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
2558 json_object_object_add(notif, "content", json_object_new_string(content));
2559 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002560failed:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002561 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02002562}
2563
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002564json_object *
2565handle_op_ntfgethistory(json_object *request, unsigned int session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02002566{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002567 json_object *reply = NULL;
2568 json_object *js_tmp = NULL;
2569 struct session_with_mutex *locked_session = NULL;
2570 struct nc_session *temp_session = NULL;
2571 nc_rpc *rpc = NULL;
2572 time_t start = 0;
2573 time_t stop = 0;
2574 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02002575
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002576 DEBUG("Request: get notification history (session %u)", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02002577
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002578 pthread_mutex_lock(&json_lock);
2579 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
2580 from = json_object_get_int64(js_tmp);
2581 }
2582 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
2583 to = json_object_get_int64(js_tmp);
2584 }
2585 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002586
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002587 start = time(NULL) + from;
2588 stop = time(NULL) + to;
Tomas Cejka6b886e02013-07-05 09:53:17 +02002589
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002590 DEBUG("notification history interval %li %li", (long int)from, (long int)to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02002591
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002592 DEBUG("LOCK wrlock %s", __func__);
2593 if (pthread_rwlock_rdlock(&session_lock) != 0) {
2594 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2595 reply = create_error_reply("Internal lock failed.");
2596 goto finalize;
2597 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02002598
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002599 for (locked_session = netconf_sessions_list;
2600 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002601 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002602 if (locked_session != NULL) {
2603 DEBUG("LOCK mutex %s", __func__);
2604 pthread_mutex_lock(&locked_session->lock);
2605 DEBUG("UNLOCK wrlock %s", __func__);
2606 if (pthread_rwlock_unlock(&session_lock) != 0) {
2607 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2608 }
2609 DEBUG("creating temporal NC session.");
2610 temp_session = nc_session_connect_channel(locked_session->session, NULL);
2611 if (temp_session != NULL) {
2612 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
2613 if (rpc == NULL) {
2614 DEBUG("UNLOCK mutex %s", __func__);
2615 pthread_mutex_unlock(&locked_session->lock);
2616 DEBUG("notifications: creating an rpc request failed.");
2617 reply = create_error_reply("notifications: creating an rpc request failed.");
2618 goto finalize;
2619 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02002620
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002621 DEBUG("Send NC subscribe.");
2622 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
2623 json_object *res = netconf_unlocked_op(temp_session, rpc);
2624 if (res != NULL) {
2625 DEBUG("UNLOCK mutex %s", __func__);
2626 pthread_mutex_unlock(&locked_session->lock);
2627 DEBUG("Subscription RPC failed.");
2628 reply = res;
2629 goto finalize;
2630 }
2631 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejka6b886e02013-07-05 09:53:17 +02002632
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002633 DEBUG("UNLOCK mutex %s", __func__);
2634 pthread_mutex_unlock(&locked_session->lock);
2635 DEBUG("LOCK mutex %s", __func__);
2636 pthread_mutex_lock(&ntf_history_lock);
2637 pthread_mutex_lock(&json_lock);
2638 json_object *notif_history_array = json_object_new_array();
2639 pthread_mutex_unlock(&json_lock);
2640 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
2641 ERROR("notif_history: cannot set thread-specific hash value.");
2642 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02002643
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002644 ncntf_dispatch_receive(temp_session, notification_history);
Tomas Cejka6b886e02013-07-05 09:53:17 +02002645
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002646 pthread_mutex_lock(&json_lock);
2647 reply = json_object_new_object();
2648 json_object_object_add(reply, "notifications", notif_history_array);
2649 //json_object_put(notif_history_array);
2650 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02002651
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002652 DEBUG("UNLOCK mutex %s", __func__);
2653 pthread_mutex_unlock(&ntf_history_lock);
2654 DEBUG("closing temporal NC session.");
2655 nc_session_free(temp_session);
2656 temp_session = NULL;
2657 } else {
2658 DEBUG("UNLOCK mutex %s", __func__);
2659 pthread_mutex_unlock(&locked_session->lock);
2660 DEBUG("Get history of notification failed due to channel establishment");
2661 reply = create_error_reply("Get history of notification was unsuccessful, connection failed.");
2662 }
2663 } else {
2664 DEBUG("UNLOCK wrlock %s", __func__);
2665 if (pthread_rwlock_unlock(&session_lock) != 0) {
2666 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2667 }
2668 reply = create_error_reply("Invalid session identifier.");
2669 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02002670
Tomas Cejka09629492014-07-10 15:58:06 +02002671finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002672 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02002673}
2674
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002675json_object *
2676handle_op_validate(json_object *request, unsigned int session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02002677{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002678 json_object *reply = NULL;
2679 char *target = NULL;
2680 char *url = NULL;
2681 nc_rpc *rpc = NULL;
2682 NC_DATASTORE target_ds;
Tomas Cejka4003a702013-10-01 00:02:45 +02002683
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002684 DEBUG("Request: validate datastore (session %u)", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02002685
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002686 pthread_mutex_lock(&json_lock);
2687 target = get_param_string(request, "target");
2688 url = get_param_string(request, "url");
2689 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02002690
2691
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002692 if (target == NULL) {
2693 reply = create_error_reply("Missing target parameter.");
2694 goto finalize;
2695 }
Tomas Cejka4003a702013-10-01 00:02:45 +02002696
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002697 /* validation */
2698 target_ds = parse_datastore(target);
2699 if (target_ds == NC_DATASTORE_URL) {
2700 if (url != NULL) {
2701 rpc = nc_rpc_validate(target_ds, url);
2702 }
2703 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
2704 || (target_ds == NC_DATASTORE_CANDIDATE)) {
2705 rpc = nc_rpc_validate(target_ds);
2706 }
2707 if (rpc == NULL) {
2708 DEBUG("mod_netconf: creating rpc request failed");
2709 reply = create_error_reply("Creation of RPC request failed.");
2710 goto finalize;
2711 }
Tomas Cejka4003a702013-10-01 00:02:45 +02002712
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002713 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
2714 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002715
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002716 if (reply == NULL) {
2717 DEBUG("Request: validation ok.");
2718 reply = create_ok_reply();
2719 }
2720 }
2721 nc_rpc_free (rpc);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002722
Tomas Cejka09629492014-07-10 15:58:06 +02002723finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002724 CHECK_AND_FREE(target);
2725 CHECK_AND_FREE(url);
2726 return reply;
Tomas Cejka6b886e02013-07-05 09:53:17 +02002727}
2728
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002729json_object *
2730handle_op_query(json_object *request, unsigned int session_key, int idx)
David Kupka8e60a372012-09-04 09:15:20 +02002731{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002732 json_object *reply = NULL, *filters, *obj;
2733 char *filter = NULL;
2734 int load_children = 0;
David Kupka8e60a372012-09-04 09:15:20 +02002735
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002736 DEBUG("Request: query (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002737
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002738 pthread_mutex_lock(&json_lock);
2739 if (json_object_object_get_ex(request, "filters", &filters) == FALSE) {
2740 pthread_mutex_unlock(&json_lock);
2741 reply = create_error_reply("Missing filters parameter.");
2742 goto finalize;
2743 }
2744 obj = json_object_array_get_idx(filters, idx);
2745 if (!obj) {
2746 pthread_mutex_unlock(&json_lock);
2747 reply = create_error_reply("Filters array parameter shorter than sessions.");
2748 goto finalize;
2749 }
2750 filter = strdup(json_object_get_string(obj));
2751 if (json_object_object_get_ex(request, "load_children", &obj) == TRUE) {
2752 load_children = json_object_get_boolean(obj);
2753 }
2754 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02002755
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002756 reply = libyang_query(session_key, filter, load_children);
David Kupka8e60a372012-09-04 09:15:20 +02002757
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002758 CHECK_ERR_SET_REPLY
2759 if (!reply) {
2760 reply = create_error_reply("Query failed.");
2761 }
David Kupka8e60a372012-09-04 09:15:20 +02002762
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002763finalize:
2764 CHECK_AND_FREE(filter);
2765 return reply;
2766}
David Kupka8e60a372012-09-04 09:15:20 +02002767
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002768json_object *
2769handle_op_merge(json_object *request, unsigned int session_key, int idx)
2770{
2771 json_object *reply = NULL, *configs, *obj;
2772 char *config = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02002773
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002774 DEBUG("Request: merge (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002775
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002776 pthread_mutex_lock(&json_lock);
2777 if (json_object_object_get_ex(request, "configurations", &configs) == FALSE) {
2778 pthread_mutex_unlock(&json_lock);
2779 reply = create_error_reply("Missing configurations parameter.");
2780 goto finalize;
2781 }
2782 obj = json_object_array_get_idx(configs, idx);
2783 if (!obj) {
2784 pthread_mutex_unlock(&json_lock);
2785 reply = create_error_reply("Filters array parameter shorter than sessions.");
2786 goto finalize;
2787 }
2788 config = strdup(json_object_get_string(obj));
2789 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02002790
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002791 reply = libyang_merge(session_key, config);
David Kupka8e60a372012-09-04 09:15:20 +02002792
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002793 CHECK_ERR_SET_REPLY
2794 if (!reply) {
2795 reply = create_error_reply("Merge failed.");
2796 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002797
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002798finalize:
2799 CHECK_AND_FREE(config);
2800 return reply;
2801}
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002802
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002803void *
2804thread_routine(void *arg)
2805{
2806 void *retval = NULL;
2807 struct pollfd fds;
2808 json_object *request = NULL, *replies = NULL, *reply, *sessions = NULL;
2809 json_object *js_tmp = NULL;
2810 int operation = (-1), count, i;
2811 int status = 0;
2812 const char *msgtext;
2813 unsigned int session_key = 0;
2814 char *chunked_out_msg = NULL;
2815 int client = ((struct pass_to_thread *)arg)->client;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002816
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002817 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02002818
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002819 /* init thread specific err_reply memory */
2820 create_err_reply_p();
David Kupka8e60a372012-09-04 09:15:20 +02002821
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002822 while (!isterminated) {
2823 fds.fd = client;
2824 fds.events = POLLIN;
2825 fds.revents = 0;
David Kupka8e60a372012-09-04 09:15:20 +02002826
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002827 status = poll(&fds, 1, 1000);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002828
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002829 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
2830 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
2831 continue;
2832 } else if (status < 0) {
2833 /* 0: poll time outed
2834 * close socket and ignore this request from the client, it can try it again
2835 * -1: poll failed
2836 * something wrong happend, close this socket and wait for another request
2837 */
2838 close(client);
2839 break;
2840 }
2841 /* status > 0 */
David Kupka8e60a372012-09-04 09:15:20 +02002842
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002843 /* check the status of the socket */
David Kupka8e60a372012-09-04 09:15:20 +02002844
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002845 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
2846 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
2847 /* close client's socket (it's probably already closed by client */
2848 close(client);
2849 break;
2850 }
Tomas Cejka09629492014-07-10 15:58:06 +02002851
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002852 DEBUG("Get framed message...");
2853 buffer = get_framed_message(client);
Tomas Cejka09629492014-07-10 15:58:06 +02002854
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002855 DEBUG("Check read buffer.");
2856 if (buffer != NULL) {
2857 enum json_tokener_error jerr;
2858 pthread_mutex_lock(&json_lock);
2859 request = json_tokener_parse_verbose(buffer, &jerr);
2860 if (jerr != json_tokener_success) {
2861 ERROR("JSON parsing error");
2862 pthread_mutex_unlock(&json_lock);
2863 continue;
2864 }
2865
2866 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
2867 operation = json_object_get_int(js_tmp);
2868 }
2869 pthread_mutex_unlock(&json_lock);
2870 if (operation == -1) {
2871 replies = create_replies();
2872 add_reply(replies, create_error_reply("Missing operation type from frontend."), 0);
2873 goto send_reply;
2874 }
2875
2876 if ((operation < 4) || ((operation > 19) && (operation < 100)) || (operation > 101)) {
2877 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
2878 replies = create_replies();
2879 add_reply(replies, create_error_reply("Operation not supported."), 0);
2880 goto send_reply;
2881 }
2882
2883 DEBUG("operation %d", operation);
2884
2885 /* null global JSON error-reply */
2886 clean_err_reply();
2887
2888 /* clean replies envelope */
2889 if (replies != NULL) {
2890 pthread_mutex_lock(&json_lock);
2891 json_object_put(replies);
2892 pthread_mutex_unlock(&json_lock);
2893 }
2894 replies = create_replies();
2895
2896 if (operation == MSG_CONNECT) {
2897 count = 1;
2898 } else {
2899 pthread_mutex_lock(&json_lock);
2900 if (json_object_object_get_ex(request, "sessions", &sessions) == FALSE) {
2901 add_reply(replies, create_error_reply("Operation missing \"sessions\" arg"), 0);
2902 goto send_reply;
2903 }
2904 count = json_object_array_length(sessions);
2905 pthread_mutex_unlock(&json_lock);
2906 }
2907
2908 for (i = 0; i < count; ++i) {
2909 if (operation != MSG_CONNECT) {
2910 js_tmp = json_object_array_get_idx(sessions, i);
2911 session_key = json_object_get_int(js_tmp);
2912 }
2913
2914 /* process required operation */
2915 switch (operation) {
2916 case MSG_CONNECT:
2917 reply = handle_op_connect(request);
2918 break;
2919 case MSG_DISCONNECT:
2920 reply = handle_op_disconnect(request, session_key);
2921 break;
2922 case MSG_GET:
2923 reply = handle_op_get(request, session_key);
2924 break;
2925 case MSG_GETCONFIG:
2926 reply = handle_op_getconfig(request, session_key);
2927 break;
2928 case MSG_EDITCONFIG:
2929 reply = handle_op_editconfig(request, session_key, i);
2930 break;
2931 case MSG_COPYCONFIG:
2932 reply = handle_op_copyconfig(request, session_key, i);
2933 break;
2934 case MSG_DELETECONFIG:
2935 reply = handle_op_deleteconfig(request, session_key);
2936 break;
2937 case MSG_LOCK:
2938 reply = handle_op_lock(request, session_key);
2939 break;
2940 case MSG_UNLOCK:
2941 reply = handle_op_unlock(request, session_key);
2942 break;
2943 case MSG_KILL:
2944 reply = handle_op_kill(request, session_key);
2945 break;
2946 case MSG_INFO:
2947 reply = handle_op_info(request, session_key);
2948 break;
2949 case MSG_GENERIC:
2950 reply = handle_op_generic(request, session_key, i);
2951 break;
2952 case MSG_GETSCHEMA:
2953 reply = handle_op_getschema(request, session_key);
2954 break;
2955 case MSG_RELOADHELLO:
2956 reply = handle_op_reloadhello(request, session_key);
2957 break;
2958 case MSG_NTF_GETHISTORY:
2959 reply = handle_op_ntfgethistory(request, session_key);
2960 break;
2961 case MSG_VALIDATE:
2962 reply = handle_op_validate(request, session_key);
2963 break;
2964 case SCH_QUERY:
2965 reply = handle_op_query(request, session_key, i);
2966 break;
2967 case SCH_MERGE:
2968 reply = handle_op_merge(request, session_key, i);
2969 break;
2970 }
2971
2972 add_reply(replies, reply, session_key);
2973 }
2974
2975 /* free parameters */
2976 operation = (-1);
2977
2978 DEBUG("Clean request json object.");
2979 if (request != NULL) {
2980 pthread_mutex_lock(&json_lock);
2981 json_object_put(request);
2982 pthread_mutex_unlock(&json_lock);
2983 }
2984 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02002985
2986send_reply:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002987 /* send reply to caller */
2988 if (replies) {
2989 pthread_mutex_lock(&json_lock);
2990 msgtext = json_object_to_json_string(replies);
2991 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
2992 if (buffer != NULL) {
2993 free(buffer);
2994 buffer = NULL;
2995 }
2996 pthread_mutex_unlock(&json_lock);
2997 break;
2998 }
2999 pthread_mutex_unlock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003000
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003001 DEBUG("Send framed reply json object.");
3002 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
3003 DEBUG("Clean reply json object.");
3004 pthread_mutex_lock(&json_lock);
3005 json_object_put(replies);
3006 replies = NULL;
3007 DEBUG("Clean message buffer.");
3008 CHECK_AND_FREE(chunked_out_msg);
3009 chunked_out_msg = NULL;
3010 if (buffer) {
3011 free(buffer);
3012 buffer = NULL;
3013 }
3014 pthread_mutex_unlock(&json_lock);
3015 clean_err_reply();
3016 } else {
3017 ERROR("Reply is NULL, shouldn't be...");
3018 continue;
3019 }
3020 }
3021 }
3022 free(arg);
3023 free_err_reply();
David Kupka8e60a372012-09-04 09:15:20 +02003024
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003025 return retval;
David Kupka8e60a372012-09-04 09:15:20 +02003026}
3027
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003028/**
3029 * \brief Close all open NETCONF sessions.
3030 *
3031 * During termination of mod_netconf, it is useful to close all remaining
3032 * sessions. This function iterates over the list of sessions and close them
3033 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003034 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003035static void
3036close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003037{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003038 struct session_with_mutex *locked_session, *next_session;
3039 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003040
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003041 /* get exclusive access to sessions_list (conns) */
3042 DEBUG("LOCK wrlock %s", __func__);
3043 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
3044 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3045 return;
3046 }
3047 for (next_session = netconf_sessions_list; next_session;) {
3048 locked_session = next_session;
3049 next_session = locked_session->next;
Tomas Cejka47387fd2013-06-10 20:37:46 +02003050
Michal Vaskoc3146782015-11-04 14:46:41 +01003051 /* close_and_free_session handles locking on its own */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003052 DEBUG("Closing NETCONF session %u (SID %s).", locked_session->session_key, nc_session_get_id(locked_session->session));
3053 close_and_free_session(locked_session);
3054 }
3055 netconf_sessions_list = NULL;
3056
3057 /* get exclusive access to sessions_list (conns) */
3058 DEBUG("UNLOCK wrlock %s", __func__);
3059 if (pthread_rwlock_unlock (&session_lock) != 0) {
3060 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3061 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003062}
3063
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003064static void
3065check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003066{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003067 struct nc_session *ns = NULL;
3068 struct session_with_mutex *locked_session = NULL;
3069 time_t current_time = time(NULL);
3070 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003071
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003072 /* get exclusive access to sessions_list (conns) */
3073 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
3074 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3075 return;
3076 }
3077 for (locked_session = netconf_sessions_list; locked_session; locked_session = locked_session->next) {
3078 ns = locked_session->session;
3079 if (ns == NULL) {
3080 continue;
3081 }
3082 pthread_mutex_lock(&locked_session->lock);
3083 if ((current_time - locked_session->last_activity) > ACTIVITY_TIMEOUT) {
3084 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 +02003085
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003086 /* close_and_free_session handles locking on its own */
3087 close_and_free_session(locked_session);
3088 } else {
3089 pthread_mutex_unlock(&locked_session->lock);
3090 }
3091 }
3092 /* get exclusive access to sessions_list (conns) */
3093 if (pthread_rwlock_unlock(&session_lock) != 0) {
3094 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3095 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003096}
3097
3098
3099/**
Radek Krejcif23850c2012-07-23 16:14:17 +02003100 * This is actually implementation of NETCONF client
3101 * - requests are received from UNIX socket in the predefined format
3102 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01003103 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02003104 *
3105 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003106static void
3107forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02003108{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003109 struct timeval tv;
3110 struct sockaddr_un local, remote;
3111 int lsock, client, ret, i, pthread_count = 0;
3112 unsigned int olds = 0, timediff = 0;
3113 socklen_t len;
3114 struct pass_to_thread *arg;
3115 pthread_t *ptids = calloc(1, sizeof(pthread_t));
3116 struct timespec maxtime;
3117 pthread_rwlockattr_t lock_attrs;
3118 #ifdef WITH_NOTIFICATIONS
3119 char use_notifications = 0;
3120 #endif
David Kupka8e60a372012-09-04 09:15:20 +02003121
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003122 /* wait at most 5 seconds for every thread to terminate */
3123 maxtime.tv_sec = 5;
3124 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02003125
Tomas Cejka04e08f42014-03-27 19:52:34 +01003126#ifdef HAVE_UNIXD_SETUP_CHILD
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003127 /* change uid and gid of process for security reasons */
3128 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01003129#else
3130# ifdef SU_GROUP
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003131 if (strlen(SU_GROUP) > 0) {
3132 struct group *g = getgrnam(SU_GROUP);
3133 if (g == NULL) {
3134 ERROR("GID (%s) was not found.", SU_GROUP);
3135 return;
3136 }
3137 if (setgid(g->gr_gid) != 0) {
3138 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
3139 return;
3140 }
3141 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003142# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003143 DEBUG("no SU_GROUP");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003144# endif
3145# ifdef SU_USER
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003146 if (strlen(SU_USER) > 0) {
3147 struct passwd *p = getpwnam(SU_USER);
3148 if (p == NULL) {
3149 ERROR("UID (%s) was not found.", SU_USER);
3150 return;
3151 }
3152 if (setuid(p->pw_uid) != 0) {
3153 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
3154 return;
3155 }
3156 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003157# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003158 DEBUG("no SU_USER");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003159# endif
3160#endif
Radek Krejci469aab82012-07-22 18:42:20 +02003161
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003162 /* try to remove if exists */
3163 unlink(sockname);
Tomas Cejka04e08f42014-03-27 19:52:34 +01003164
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003165 /* create listening UNIX socket to accept incoming connections */
3166 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
3167 ERROR("Creating socket failed (%s)", strerror(errno));
3168 goto error_exit;
3169 }
Radek Krejci469aab82012-07-22 18:42:20 +02003170
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003171 local.sun_family = AF_UNIX;
3172 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
3173 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
Radek Krejci469aab82012-07-22 18:42:20 +02003174
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003175 if (bind(lsock, (struct sockaddr *)&local, len) == -1) {
3176 if (errno == EADDRINUSE) {
3177 ERROR("mod_netconf socket address already in use");
3178 goto error_exit;
3179 }
3180 ERROR("Binding socket failed (%s)", strerror(errno));
3181 goto error_exit;
3182 }
Radek Krejci469aab82012-07-22 18:42:20 +02003183
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003184 if (listen(lsock, MAX_SOCKET_CL) == -1) {
3185 ERROR("Setting up listen socket failed (%s)", strerror(errno));
3186 goto error_exit;
3187 }
3188 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02003189
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003190 uid_t user = -1;
3191 if (strlen(CHOWN_USER) > 0) {
3192 struct passwd *p = getpwnam(CHOWN_USER);
3193 if (p != NULL) {
3194 user = p->pw_uid;
3195 }
3196 }
3197 gid_t group = -1;
3198 if (strlen(CHOWN_GROUP) > 0) {
3199 struct group *g = getgrnam(CHOWN_GROUP);
3200 if (g != NULL) {
3201 group = g->gr_gid;
3202 }
3203 }
3204 if (chown(sockname, user, group) == -1) {
3205 ERROR("Chown on socket file failed (%s).", strerror(errno));
3206 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003207
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003208 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02003209
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003210 #ifdef WITH_NOTIFICATIONS
3211 if (notification_init() == -1) {
3212 ERROR("libwebsockets initialization failed");
3213 use_notifications = 0;
3214 } else {
3215 use_notifications = 1;
3216 }
3217 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003218
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003219 /* setup libnetconf's callbacks */
3220 nc_verbosity(NC_VERB_DEBUG);
3221 nc_callback_print(clb_print);
3222 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
3223 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
3224 nc_callback_sshauth_password(netconf_callback_sshauth_password);
3225 nc_callback_sshauth_passphrase(netconf_callback_sshauth_passphrase);
3226 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02003227
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003228 /* disable publickey authentication */
3229 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
Radek Krejci469aab82012-07-22 18:42:20 +02003230
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003231 /* create mutex protecting session list */
3232 pthread_rwlockattr_init(&lock_attrs);
3233 /* rwlock is shared only with threads in this process */
3234 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
3235 /* create rw lock */
3236 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
3237 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
3238 goto error_exit;
3239 }
3240 pthread_mutex_init(&ntf_history_lock, NULL);
3241 pthread_mutex_init(&json_lock, NULL);
3242 DEBUG("Initialization of notification history.");
3243 if (pthread_key_create(&notif_history_key, NULL) != 0) {
3244 ERROR("Initialization of notification history failed.");
3245 }
3246 if (pthread_key_create(&err_reply_key, NULL) != 0) {
3247 ERROR("Initialization of reply key failed.");
3248 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02003249
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003250 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
3251 while (isterminated == 0) {
3252 gettimeofday(&tv, NULL);
3253 timediff = (unsigned int)tv.tv_sec - olds;
3254 #ifdef WITH_NOTIFICATIONS
3255 if (use_notifications == 1) {
3256 notification_handle();
3257 }
3258 #endif
3259 if (timediff > ACTIVITY_CHECK_INTERVAL) {
3260 check_timeout_and_close();
3261 }
Radek Krejci469aab82012-07-22 18:42:20 +02003262
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003263 /* open incoming connection if any */
3264 len = sizeof(remote);
3265 client = accept(lsock, (struct sockaddr *) &remote, &len);
3266 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
3267 usleep(SLEEP_TIME * 1000);
3268 continue;
3269 } else if (client == -1 && (errno == EINTR)) {
3270 continue;
3271 } else if (client == -1) {
3272 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
3273 continue;
3274 }
Radek Krejci469aab82012-07-22 18:42:20 +02003275
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003276 /* set client's socket as non-blocking */
3277 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02003278
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003279 arg = malloc(sizeof(struct pass_to_thread));
3280 arg->client = client;
3281 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02003282
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003283 /* start new thread. It will serve this particular request and then terminate */
3284 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void *)arg)) != 0) {
3285 ERROR("Creating POSIX thread failed: %d\n", ret);
3286 } else {
3287 DEBUG("Thread %lu created", ptids[pthread_count]);
3288 pthread_count++;
3289 ptids = realloc (ptids, sizeof(pthread_t) * (pthread_count+1));
3290 ptids[pthread_count] = 0;
3291 }
Radek Krejci469aab82012-07-22 18:42:20 +02003292
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003293 /* check if some thread already terminated, free some resources by joining it */
3294 for (i = 0; i < pthread_count; i++) {
3295 if (pthread_tryjoin_np(ptids[i], (void **)&arg) == 0) {
3296 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
3297 pthread_count--;
3298 if (pthread_count > 0) {
3299 /* place last Thread ID on the place of joined one */
3300 ptids[i] = ptids[pthread_count];
3301 }
3302 }
3303 }
3304 DEBUG("Running %d threads", pthread_count);
3305 }
Radek Krejci469aab82012-07-22 18:42:20 +02003306
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003307 DEBUG("mod_netconf terminating...");
3308 /* join all threads */
3309 for (i = 0; i < pthread_count; i++) {
3310 pthread_timedjoin_np(ptids[i], (void **)&arg, &maxtime);
3311 }
Radek Krejci469aab82012-07-22 18:42:20 +02003312
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003313 #ifdef WITH_NOTIFICATIONS
3314 notification_close();
3315 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003316
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003317 /* close all NETCONF sessions */
3318 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003319
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003320 /* destroy rwlock */
3321 pthread_rwlock_destroy(&session_lock);
3322 pthread_rwlockattr_destroy(&lock_attrs);
David Kupka8e60a372012-09-04 09:15:20 +02003323
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003324 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02003325
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003326 free(ptids);
3327 close(lsock);
3328 exit(0);
3329 return;
3330
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003331error_exit:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003332 close(lsock);
3333 free(ptids);
3334 return;
Radek Krejci469aab82012-07-22 18:42:20 +02003335}
3336
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003337int
3338main(int argc, char **argv)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003339{
Michal Vaskoc3146782015-11-04 14:46:41 +01003340 struct sigaction action;
3341 sigset_t block_mask;
3342
3343 if (argc > 1) {
3344 sockname = argv[1];
3345 } else {
3346 sockname = SOCKET_FILENAME;
3347 }
3348
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003349 sigfillset(&block_mask);
Michal Vaskoc3146782015-11-04 14:46:41 +01003350 action.sa_handler = signal_handler;
3351 action.sa_mask = block_mask;
3352 action.sa_flags = 0;
3353 sigaction(SIGINT, &action, NULL);
3354 sigaction(SIGTERM, &action, NULL);
3355
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003356 forked_proc();
3357 DEBUG("Terminated");
3358 return 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003359}