blob: af50aaed23cc36139475b86555d9c950d8347897 [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>
Tomas Cejka04e08f42014-03-27 19:52:34 +010055#include <pwd.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010056#include <errno.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010057#include <grp.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010058#include <signal.h>
David Kupka8e60a372012-09-04 09:15:20 +020059#include <pthread.h>
60#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020061
Radek Krejci469aab82012-07-22 18:42:20 +020062#include <libnetconf.h>
Tomas Cejkab3cc64f2013-05-03 19:44:54 +020063#include <libnetconf_ssh.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020064
Tomas Cejka04e08f42014-03-27 19:52:34 +010065#include "../config.h"
66
Tomas Cejkad340dbf2013-03-24 20:36:57 +010067#ifdef WITH_NOTIFICATIONS
68#include "notification_module.h"
69#endif
70
Tomas Cejka94da2c52013-01-08 18:20:30 +010071#include "message_type.h"
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020072#include "mod_netconf.h"
Radek Krejci469aab82012-07-22 18:42:20 +020073
74#define MAX_PROCS 5
Tomas Cejka86f0fc12014-09-17 15:09:38 +020075#define SOCKET_FILENAME "/var/run/mod_netconf.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020076#define MAX_SOCKET_CL 10
77#define BUFFER_SIZE 4096
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020078#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
Tomas Cejka04e39952013-04-19 11:49:38 +020079#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020080
81/* sleep in master process for non-blocking socket reading */
82#define SLEEP_TIME 200
83
84#ifndef offsetof
85#define offsetof(type, member) ((size_t) ((type *) 0)->member)
86#endif
87
Tomas Cejka027f3bc2012-11-10 20:28:36 +010088/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020089struct timeval timeout = { 1, 0 };
90
Tomas Cejka5064c232013-01-17 09:30:58 +010091#define NCWITHDEFAULTS NCWD_MODE_NOTSET
92
93
Radek Krejci469aab82012-07-22 18:42:20 +020094#define MSG_OK 0
95#define MSG_OPEN 1
96#define MSG_DATA 2
97#define MSG_CLOSE 3
98#define MSG_ERROR 4
99#define MSG_UNKNOWN 5
100
Tomas Cejka47387fd2013-06-10 20:37:46 +0200101pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200102pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100103pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100104pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
105
Michal Vaskoc3146782015-11-04 14:46:41 +0100106struct session_with_mutex *netconf_sessions_list = NULL;
107
108static const char *sockname;
David Kupka8e60a372012-09-04 09:15:20 +0200109
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200110static pthread_key_t notif_history_key;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200111
Tomas Cejka442258e2014-04-01 18:17:18 +0200112pthread_key_t err_reply_key;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100113
Radek Krejci469aab82012-07-22 18:42:20 +0200114volatile int isterminated = 0;
115
116static char* password;
117
Radek Krejci469aab82012-07-22 18:42:20 +0200118static void signal_handler(int sign)
119{
120 switch (sign) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100121 case SIGINT:
Radek Krejci469aab82012-07-22 18:42:20 +0200122 case SIGTERM:
123 isterminated = 1;
Radek Krejci469aab82012-07-22 18:42:20 +0200124 break;
125 }
126}
127
Michal Vaskoc3146782015-11-04 14:46:41 +0100128int netconf_callback_ssh_hostkey_check(const char* UNUSED(hostname), ssh_session UNUSED(session))
Radek Krejci469aab82012-07-22 18:42:20 +0200129{
130 /* always approve */
131 return (EXIT_SUCCESS);
132}
133
Michal Vaskoc3146782015-11-04 14:46:41 +0100134char *netconf_callback_sshauth_passphrase(const char *UNUSED(username), const char *UNUSED(hostname), const char *UNUSED(priv_key_file))
Radek Krejci469aab82012-07-22 18:42:20 +0200135{
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200136 char *buf;
137 buf = strdup(password);
Radek Krejci469aab82012-07-22 18:42:20 +0200138 return (buf);
139}
140
Michal Vaskoc3146782015-11-04 14:46:41 +0100141char *netconf_callback_sshauth_password(const char* UNUSED(username), const char* UNUSED(hostname))
Radek Krejci469aab82012-07-22 18:42:20 +0200142{
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200143 char *buf;
144 buf = strdup(password);
145 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200146}
147
Michal Vaskoc3146782015-11-04 14:46:41 +0100148char *netconf_callback_sshauth_interactive(const char *UNUSED(name), const char *UNUSED(instruction),
149 const char *UNUSED(prompt), int UNUSED(echo))
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200150{
151 char *buf;
152 buf = strdup(password);
153 return (buf);
154}
155
Michal Vaskoc3146782015-11-04 14:46:41 +0100156void netconf_callback_error_process(const char *UNUSED(tag),
157 const char *UNUSED(type),
158 const char *UNUSED(severity),
159 const char *UNUSED(apptag),
160 const char *UNUSED(path),
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200161 const char *message,
Michal Vaskoc3146782015-11-04 14:46:41 +0100162 const char *UNUSED(attribute),
163 const char *UNUSED(element),
164 const char *UNUSED(ns),
165 const char *UNUSED(sid))
Radek Krejcic11fd862012-07-26 12:41:21 +0200166{
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100167 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
Tomas Cejka442258e2014-04-01 18:17:18 +0200168 if (err_reply_p == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200169 ERROR("Error message was not allocated. %s", __func__);
Tomas Cejka442258e2014-04-01 18:17:18 +0200170 return;
171 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100172 json_object *err_reply = *err_reply_p;
173
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100174 json_object *array = NULL;
175 if (err_reply == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200176 ERROR("error calback: empty error list");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100177 pthread_mutex_lock(&json_lock);
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100178 err_reply = json_object_new_object();
179 array = json_object_new_array();
180 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
181 json_object_object_add(err_reply, "errors", array);
182 if (message != NULL) {
183 json_object_array_add(array, json_object_new_string(message));
184 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100185 pthread_mutex_unlock(&json_lock);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100186 (*err_reply_p) = err_reply;
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100187 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200188 ERROR("error calback: nonempty error list");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100189 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +0200190 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100191 if (message != NULL) {
192 json_object_array_add(array, json_object_new_string(message));
193 }
194 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100195 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100196 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100197 pthread_setspecific(err_reply_key, err_reply_p);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100198 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200199}
200
Tomas Cejka47387fd2013-06-10 20:37:46 +0200201/**
202 * should be used in locked area
203 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100204void prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200205{
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200206 json_object *json_obj = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +0200207 json_object *js_tmp = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100208 char *old_sid = NULL;
209 const char *j_old_sid = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200210 const char *cpbltstr;
211 struct nc_cpblts* cpblts = NULL;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200212
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200213 if (s == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200214 ERROR("No session given.");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200215 return;
216 }
217
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100218 pthread_mutex_lock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200219 if (s->hello_message != NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200220 ERROR("clean previous hello message");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200221 //json_object_put(s->hello_message);
Tomas Cejka09629492014-07-10 15:58:06 +0200222 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
223 j_old_sid = json_object_get_string(js_tmp);
224 if (j_old_sid != NULL) {
225 old_sid = strdup(j_old_sid);
226 }
227 json_object_put(s->hello_message);
228 json_object_put(js_tmp);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100229 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200230 s->hello_message = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200231 }
Tomas Cejkadd5cb452014-03-26 15:22:00 +0100232 s->hello_message = json_object_get(json_object_new_object());
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200233 if (session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100234 if (old_sid != NULL) {
235 /* use previous sid */
236 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
237 free(old_sid);
Tomas Cejkaa3ffdba2014-03-27 15:12:21 +0100238 old_sid = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100239 } else {
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200240 /* we don't have old sid */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100241 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
242 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200243 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
244 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
245 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
246 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
247 cpblts = nc_session_get_cpblts (session);
248 if (cpblts != NULL) {
249 json_obj = json_object_new_array();
250 nc_cpblts_iter_start (cpblts);
251 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
252 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
253 }
254 json_object_object_add(s->hello_message, "capabilities", json_obj);
255 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100256 DEBUG("%s", json_object_to_json_string(s->hello_message));
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200257 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200258 ERROR("Session was not given.");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200259 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
260 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
261 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100262 DEBUG("Status info from hello message prepared");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100263 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200264
265}
266
Tomas Cejka442258e2014-04-01 18:17:18 +0200267void create_err_reply_p()
268{
269 json_object **err_reply = calloc(1, sizeof(json_object **));
270 if (err_reply == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200271 ERROR("Allocation of err_reply storage failed!");
Tomas Cejka442258e2014-04-01 18:17:18 +0200272 return;
273 }
274 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200275 ERROR("cannot set thread-specific value.");
Tomas Cejka442258e2014-04-01 18:17:18 +0200276 }
277}
278
279void clean_err_reply()
280{
281 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
282 if (err_reply != NULL) {
283 if (*err_reply != NULL) {
284 pthread_mutex_lock(&json_lock);
285 json_object_put(*err_reply);
286 pthread_mutex_unlock(&json_lock);
287 }
288 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200289 ERROR("Cannot set thread-specific hash value.");
Tomas Cejka442258e2014-04-01 18:17:18 +0200290 }
291 }
292}
293
294void free_err_reply()
295{
296 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
297 if (err_reply != NULL) {
298 if (*err_reply != NULL) {
299 pthread_mutex_lock(&json_lock);
300 json_object_put(*err_reply);
301 pthread_mutex_unlock(&json_lock);
302 }
303 free(err_reply);
304 err_reply = NULL;
305 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200306 ERROR("Cannot set thread-specific hash value.");
Tomas Cejka442258e2014-04-01 18:17:18 +0200307 }
308 }
309}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200310
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200311/**
312 * \defgroup netconf_operations NETCONF operations
313 * The list of NETCONF operations that mod_netconf supports.
314 * @{
315 */
316
317/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200318 * \brief Send RPC and wait for reply with timeout.
319 *
320 * \param[in] session libnetconf session
321 * \param[in] rpc prepared RPC message
322 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
323 * \param[out] reply reply from the server
324 * \return Value from nc_session_recv_reply() or NC_MSG_UNKNOWN when send_rpc() fails.
325 * On success, it returns NC_MSG_REPLY.
326 */
327NC_MSG_TYPE netconf_send_recv_timed(struct nc_session *session, nc_rpc *rpc,
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200328 int timeout, nc_reply **reply)
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200329{
330 const nc_msgid msgid = NULL;
331 NC_MSG_TYPE ret = NC_MSG_UNKNOWN;
332 msgid = nc_session_send_rpc(session, rpc);
333 if (msgid == NULL) {
334 return ret;
335 }
Tomas Cejka756d1292015-05-06 09:31:46 +0200336 do {
337 ret = nc_session_recv_reply(session, timeout, reply);
338 if (ret == NC_MSG_HELLO) {
339 ERROR("<hello> received instead reply, it will be lost.");
340 nc_reply_free(*reply);
341 }
342 if (ret == NC_MSG_WOULDBLOCK) {
343 ERROR("Timeout for receiving RPC reply expired.");
344 break;
345 }
346 } while (ret == NC_MSG_HELLO || ret == NC_MSG_NOTIFICATION);
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200347 return ret;
348}
349
350/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200351 * \brief Connect to NETCONF server
352 *
353 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
354 */
Michal Vaskoc3146782015-11-04 14:46:41 +0100355static const char *netconf_connect(const char* host, const char* port, const char* user, const char* pass, struct nc_cpblts * cpblts)
Radek Krejci469aab82012-07-22 18:42:20 +0200356{
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200357 struct nc_session* session = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +0100358 struct session_with_mutex * locked_session, *last_session;
Radek Krejci469aab82012-07-22 18:42:20 +0200359
360 /* connect to the requested NETCONF server */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200361 password = (char*)pass;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100362 DEBUG("prepare to connect %s@%s:%s", user, host, port);
David Kupka8e60a372012-09-04 09:15:20 +0200363 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100364 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +0200365
Radek Krejci469aab82012-07-22 18:42:20 +0200366 /* if connected successful, add session to the list */
367 if (session != NULL) {
Tomas Cejka73496bf2014-03-26 15:31:09 +0100368 if ((locked_session = calloc(1, sizeof(struct session_with_mutex))) == NULL || pthread_mutex_init (&locked_session->lock, NULL) != 0) {
David Kupka8e60a372012-09-04 09:15:20 +0200369 nc_session_free(session);
Tomas Cejka73496bf2014-03-26 15:31:09 +0100370 session = NULL;
371 free(locked_session);
372 locked_session = NULL;
Tomas Cejkacf44e522015-04-24 17:29:21 +0200373 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
Michal Vaskoc3146782015-11-04 14:46:41 +0100374 return 0;
David Kupka8e60a372012-09-04 09:15:20 +0200375 }
376 locked_session->session = session;
Michal Vaskoc3146782015-11-04 14:46:41 +0100377 locked_session->last_activity = time(NULL);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200378 locked_session->hello_message = NULL;
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200379 locked_session->closed = 0;
David Kupka8e60a372012-09-04 09:15:20 +0200380 pthread_mutex_init (&locked_session->lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100381 DEBUG("Before session_lock");
David Kupka8e60a372012-09-04 09:15:20 +0200382 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100383 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka866f2282014-09-18 15:20:26 +0200384 if (pthread_rwlock_wrlock(&session_lock) != 0) {
David Kupka8e60a372012-09-04 09:15:20 +0200385 nc_session_free(session);
386 free (locked_session);
Tomas Cejkacf44e522015-04-24 17:29:21 +0200387 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Michal Vaskoc3146782015-11-04 14:46:41 +0100388 return 0;
David Kupka8e60a372012-09-04 09:15:20 +0200389 }
Tomas Cejka654f84e2013-04-19 11:55:01 +0200390 locked_session->ntfc_subscribed = 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100391 DEBUG("Add connection to the list");
Michal Vaskoc3146782015-11-04 14:46:41 +0100392 if (!netconf_sessions_list) {
393 netconf_sessions_list = locked_session;
394 } else {
395 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
396 last_session->next = locked_session;
397 locked_session->prev = last_session;
398 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100399 DEBUG("Before session_unlock");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200400
Tomas Cejka47387fd2013-06-10 20:37:46 +0200401 /* lock session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100402 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200403 pthread_mutex_lock(&locked_session->lock);
404
405 /* unlock session list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100406 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200407 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200408 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +0200409 }
410
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200411 /* store information about session from hello message for future usage */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100412 prepare_status_message(locked_session, session);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200413
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100414 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200415 pthread_mutex_unlock(&locked_session->lock);
416
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100417 DEBUG("NETCONF session established");
Michal Vaskoc3146782015-11-04 14:46:41 +0100418 return nc_session_get_id(session);
Radek Krejci469aab82012-07-22 18:42:20 +0200419 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200420 ERROR("Connection could not be established");
Michal Vaskoc3146782015-11-04 14:46:41 +0100421 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200422 }
423
Radek Krejci469aab82012-07-22 18:42:20 +0200424}
425
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100426static int close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +0200427{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100428 DEBUG("lock private lock.");
429 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200430 if (pthread_mutex_lock(&locked_session->lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200431 ERROR("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200432 }
433 locked_session->ntfc_subscribed = 0;
434 locked_session->closed = 1;
Tomas Cejka73496bf2014-03-26 15:31:09 +0100435 if (locked_session->session != NULL) {
436 nc_session_free(locked_session->session);
437 locked_session->session = NULL;
438 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100439 DEBUG("session closed.");
440 DEBUG("unlock private lock.");
441 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200442 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200443 ERROR("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200444 }
445
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100446 DEBUG("unlock session lock.");
447 DEBUG("closed session, disabled notif(?), wait 2s");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200448 usleep(500000); /* let notification thread stop */
449
450 /* session shouldn't be used by now */
451 /** \todo free all notifications from queue */
Michal Vaskoc3146782015-11-04 14:46:41 +0100452 free(locked_session->notifications);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200453 pthread_mutex_destroy(&locked_session->lock);
454 if (locked_session->hello_message != NULL) {
455 /** \todo free hello_message */
456 //json_object_put(locked_session->hello_message);
457 locked_session->hello_message = NULL;
458 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200459 locked_session->session = NULL;
Tomas Cejkaaecc5f72013-10-01 00:03:50 +0200460 free(locked_session);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200461 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100462 DEBUG("NETCONF session closed, everything cleared.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200463 return (EXIT_SUCCESS);
464}
465
Michal Vaskoc3146782015-11-04 14:46:41 +0100466static int netconf_close(const char *session_id, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +0200467{
Michal Vaskoc3146782015-11-04 14:46:41 +0100468 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +0200469
Michal Vaskoc3146782015-11-04 14:46:41 +0100470 DEBUG("Session to close: %s", session_id);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200471
David Kupka8e60a372012-09-04 09:15:20 +0200472 /* get exclusive (write) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100473 DEBUG("lock session lock.");
474 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200475 if (pthread_rwlock_wrlock (&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200476 ERROR("Error while locking rwlock");
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100477 (*reply) = create_error("Internal: Error while locking.");
David Kupka8e60a372012-09-04 09:15:20 +0200478 return EXIT_FAILURE;
479 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200480 /* remove session from the active sessions list -> nobody new can now work with session */
Michal Vaskoc3146782015-11-04 14:46:41 +0100481 for (locked_session = netconf_sessions_list;
Michal Vasko59ccb362015-11-05 10:21:44 +0100482 locked_session && strcmp(nc_session_get_id(locked_session->session), session_id);
Michal Vaskoc3146782015-11-04 14:46:41 +0100483 locked_session = locked_session->next);
484
485 if (!locked_session) {
486 ERROR("Could not find the session \"%s\" to close.", session_id);
487 (*reply) = create_error("Internal: Error while finding a session.");
488 return EXIT_FAILURE;
489 }
490
491 if (!locked_session->prev) {
492 netconf_sessions_list = netconf_sessions_list->next;
493 netconf_sessions_list->prev = NULL;
494 } else {
495 locked_session->prev->next = locked_session->next;
496 if (locked_session->next) {
497 locked_session->next->prev = locked_session->prev;
498 }
499 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200500
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100501 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200502 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200503 ERROR("Error while unlocking rwlock");
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100504 (*reply) = create_error("Internal: Error while unlocking.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200505 }
506
507 if ((locked_session != NULL) && (locked_session->session != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100508 return close_and_free_session(locked_session);
Radek Krejci469aab82012-07-22 18:42:20 +0200509 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200510 ERROR("Unknown session to close");
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100511 (*reply) = create_error("Internal: Unkown session to close.");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200512 return (EXIT_FAILURE);
Radek Krejci469aab82012-07-22 18:42:20 +0200513 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100514 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +0200515}
516
Tomas Cejkac7929632013-10-24 19:25:15 +0200517/**
518 * Test reply message type and return error message.
519 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200520 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +0100521 * \param[in] session_key session ID, 0 to disable disconnect on error
Tomas Cejkac7929632013-10-24 19:25:15 +0200522 * \param[in] msgt RPC-REPLY message type
523 * \param[out] data
524 * \return NULL on success
525 */
Michal Vaskoc3146782015-11-04 14:46:41 +0100526json_object *netconf_test_reply(struct nc_session *session, const char *session_id, NC_MSG_TYPE msgt, nc_reply *reply, char **data)
Tomas Cejkac7929632013-10-24 19:25:15 +0200527{
528 NC_REPLY_TYPE replyt;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100529 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200530
531 /* process the result of the operation */
532 switch (msgt) {
533 case NC_MSG_UNKNOWN:
534 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200535 ERROR("mod_netconf: receiving rpc-reply failed");
Michal Vaskoc3146782015-11-04 14:46:41 +0100536 if (session_id) {
537 netconf_close(session_id, &err);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100538 }
539 if (err != NULL) {
540 return err;
Tomas Cejkac7929632013-10-24 19:25:15 +0200541 }
542 return create_error("Internal: Receiving RPC-REPLY failed.");
543 }
544 case NC_MSG_NONE:
545 /* there is error handled by callback */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100546 if (data != NULL) {
547 free(*data);
Tomas Cejka7b1e3bd2014-04-08 14:34:28 +0200548 (*data) = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100549 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200550 return NULL;
551 case NC_MSG_REPLY:
552 switch (replyt = nc_reply_get_type(reply)) {
553 case NC_REPLY_OK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100554 if ((data != NULL) && (*data != NULL)) {
555 free(*data);
556 (*data) = NULL;
557 }
558 return create_ok();
Tomas Cejkac7929632013-10-24 19:25:15 +0200559 case NC_REPLY_DATA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100560 if (((*data) = nc_reply_get_data(reply)) == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200561 ERROR("mod_netconf: no data from reply");
Tomas Cejkac7929632013-10-24 19:25:15 +0200562 return create_error("Internal: No data from reply received.");
563 } else {
564 return NULL;
565 }
566 break;
567 case NC_REPLY_ERROR:
Tomas Cejkacf44e522015-04-24 17:29:21 +0200568 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100569 if (data != NULL) {
570 free(*data);
571 (*data) = NULL;
572 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200573 return create_error(nc_reply_get_errormsg(reply));
574 default:
Tomas Cejkacf44e522015-04-24 17:29:21 +0200575 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100576 if (data != NULL) {
577 free(*data);
578 (*data) = NULL;
579 }
Tomas Cejka60885252014-03-26 15:45:47 +0100580 return create_error("Unknown type of NETCONF reply.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200581 }
582 break;
583 default:
Tomas Cejkacf44e522015-04-24 17:29:21 +0200584 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100585 if (data != NULL) {
586 free(*data);
587 (*data) = NULL;
588 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200589 return create_error("Internal: Unexpected RPC-REPLY message type.");
590 }
591}
592
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100593json_object *netconf_unlocked_op(struct nc_session *session, nc_rpc* rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +0200594{
595 nc_reply* reply = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200596 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200597
598 /* check requests */
599 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200600 ERROR("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200601 return create_error("Internal error: RPC is not created");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200602 }
603
604 if (session != NULL) {
605 /* send the request and get the reply */
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200606 msgt = netconf_send_recv_timed(session, rpc, 5000, &reply);
Tomas Cejka6b886e02013-07-05 09:53:17 +0200607 /* process the result of the operation */
Michal Vaskoc3146782015-11-04 14:46:41 +0100608 return netconf_test_reply(session, 0, msgt, reply, NULL);
Tomas Cejka6b886e02013-07-05 09:53:17 +0200609 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200610 ERROR("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200611 return create_error("Internal error: Unknown session to process.");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200612 }
613}
614
Tomas Cejkac7929632013-10-24 19:25:15 +0200615/**
616 * Perform RPC method that returns data.
617 *
Michal Vaskoc3146782015-11-04 14:46:41 +0100618 * \param[in] session_id session identifier
Tomas Cejkac7929632013-10-24 19:25:15 +0200619 * \param[in] rpc RPC message to perform
620 * \param[out] received_data received data string, can be NULL when no data expected, value can be set to NULL if no data received
621 * \return NULL on success, json object with error otherwise
622 */
Michal Vaskoc3146782015-11-04 14:46:41 +0100623static json_object *netconf_op(const char *session_id, nc_rpc* rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +0200624{
625 struct nc_session *session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200626 struct session_with_mutex * locked_session;
Tomas Cejka00635972013-06-03 15:10:52 +0200627 nc_reply* reply = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200628 json_object *res = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100629 char *data = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100630 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200631
Radek Krejci8e4632a2012-07-26 13:40:34 +0200632 /* check requests */
633 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200634 ERROR("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200635 res = create_error("Internal: RPC could not be created.");
636 data = NULL;
637 goto finished;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200638 }
639
David Kupka8e60a372012-09-04 09:15:20 +0200640 /* get non-exclusive (read) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100641 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka866f2282014-09-18 15:20:26 +0200642 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200643 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200644 res = create_error("Internal: Lock failed.");
645 data = NULL;
646 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200647 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200648 /* get session where send the RPC */
Michal Vaskoc3146782015-11-04 14:46:41 +0100649 for (locked_session = netconf_sessions_list;
Michal Vasko59ccb362015-11-05 10:21:44 +0100650 locked_session && strcmp(nc_session_get_id(locked_session->session), session_id);
Michal Vaskoc3146782015-11-04 14:46:41 +0100651 locked_session = locked_session->next);
David Kupka8e60a372012-09-04 09:15:20 +0200652 if (locked_session != NULL) {
653 session = locked_session->session;
654 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200655 if (session != NULL) {
David Kupka8e60a372012-09-04 09:15:20 +0200656 /* get exclusive access to session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100657 DEBUG("LOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200658 if (pthread_mutex_lock(&locked_session->lock) != 0) {
659 /* unlock before returning error */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100660 DEBUG("UNLOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200661 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200662
Tomas Cejkacf44e522015-04-24 17:29:21 +0200663 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200664 res = create_error("Internal: Could not unlock.");
665 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200666 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200667 res = create_error("Internal: Could not unlock.");
David Kupka8e60a372012-09-04 09:15:20 +0200668 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100669 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200670 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200671
Tomas Cejkacf44e522015-04-24 17:29:21 +0200672 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200673 res = create_error("Internal: Could not unlock.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200674 }
675
Michal Vaskoc3146782015-11-04 14:46:41 +0100676 locked_session->last_activity = time(NULL);
Tomas Cejkac7929632013-10-24 19:25:15 +0200677
Radek Krejci035bf4e2012-07-25 10:59:09 +0200678 /* send the request and get the reply */
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200679 msgt = netconf_send_recv_timed(session, rpc, 5000, &reply);
Radek Krejcia332b692012-11-12 16:15:54 +0100680
David Kupka8e60a372012-09-04 09:15:20 +0200681 /* first release exclusive lock for this session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100682 DEBUG("UNLOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200683 pthread_mutex_unlock(&locked_session->lock);
684 /* end of critical section */
Radek Krejci035bf4e2012-07-25 10:59:09 +0200685
Michal Vaskoc3146782015-11-04 14:46:41 +0100686 res = netconf_test_reply(session, session_id, msgt, reply, &data);
Radek Krejci035bf4e2012-07-25 10:59:09 +0200687 } else {
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100688 /* release lock on failure */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100689 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka866f2282014-09-18 15:20:26 +0200690 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200691 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100692 }
Tomas Cejkacf44e522015-04-24 17:29:21 +0200693 ERROR("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200694 res = create_error("Unknown session to process.");
695 data = NULL;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200696 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200697finished:
698 nc_reply_free(reply);
699 if (received_data != NULL) {
700 (*received_data) = data;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100701 } else {
702 if (data != NULL) {
703 free(data);
704 data = NULL;
705 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200706 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200707 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200708}
709
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100710static char* netconf_getconfig(const char* session_key, NC_DATASTORE source, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200711{
712 nc_rpc* rpc;
713 struct nc_filter *f = NULL;
714 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200715 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200716
717 /* create filter if set */
718 if (filter != NULL) {
719 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
720 }
721
722 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100723 rpc = nc_rpc_getconfig (source, f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200724 nc_filter_free(f);
725 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200726 ERROR("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200727 return (NULL);
728 }
729
Tomas Cejka94674662013-09-13 15:55:24 +0200730 /* tell server to show all elements even if they have default values */
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100731#ifdef HAVE_WITHDEFAULTS_TAGGED
Michal Vaskoc3146782015-11-04 14:46:41 +0100732 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100733#else
Michal Vaskoc3146782015-11-04 14:46:41 +0100734 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET))
735 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL))
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100736#endif
Michal Vaskoc3146782015-11-04 14:46:41 +0100737 {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200738 ERROR("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200739 }
740
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100741 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200742 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100743 if (res != NULL) {
744 (*err) = res;
745 } else {
746 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200747 }
748
Radek Krejci8e4632a2012-07-26 13:40:34 +0200749 return (data);
750}
751
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100752static char* netconf_getschema(const char* session_key, const char* identifier, const char* version, const char* format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100753{
754 nc_rpc* rpc;
755 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200756 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100757
758 /* create requests */
Tomas Cejka94da2c52013-01-08 18:20:30 +0100759 rpc = nc_rpc_getschema(identifier, version, format);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100760 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200761 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100762 return (NULL);
763 }
764
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100765 res = netconf_op(session_key, rpc, &data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100766 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100767 if (res != NULL) {
768 (*err) = res;
769 } else {
770 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200771 }
772
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100773 return (data);
774}
775
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100776static char* netconf_get(const char* session_key, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200777{
778 nc_rpc* rpc;
779 struct nc_filter *f = NULL;
780 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200781 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200782
783 /* create filter if set */
784 if (filter != NULL) {
785 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
786 }
787
788 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100789 rpc = nc_rpc_get (f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200790 nc_filter_free(f);
791 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200792 ERROR("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200793 return (NULL);
794 }
795
Tomas Cejka94674662013-09-13 15:55:24 +0200796 /* tell server to show all elements even if they have default values */
Tomas Cejkac0e166b2014-07-08 16:04:58 +0200797 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
798 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
799 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200800 ERROR("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200801 }
802
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100803 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200804 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200805 if (res == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100806 (*err) = res;
807 } else {
808 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200809 }
810
Radek Krejci8e4632a2012-07-26 13:40:34 +0200811 return (data);
812}
813
Tomas Cejkab4d05872014-02-14 22:44:38 +0100814static json_object *netconf_copyconfig(const char* session_key, NC_DATASTORE source, NC_DATASTORE target, const char* config, const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200815{
816 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200817 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200818
819 /* create requests */
Tomas Cejkab4d05872014-02-14 22:44:38 +0100820 if (source == NC_DATASTORE_CONFIG) {
Tomas Cejka5064c232013-01-17 09:30:58 +0100821 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100822 /* config, url */
823 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100824 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100825 /* config, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100826 rpc = nc_rpc_copyconfig(source, target, config);
827 }
Tomas Cejkab4d05872014-02-14 22:44:38 +0100828 } else if (source == NC_DATASTORE_URL) {
829 if (target == NC_DATASTORE_URL) {
830 /* url, url */
831 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
832 } else {
833 /* url, datastore */
834 rpc = nc_rpc_copyconfig(source, target, uri_src);
835 }
Tomas Cejka5064c232013-01-17 09:30:58 +0100836 } else {
837 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100838 /* datastore, url */
839 rpc = nc_rpc_copyconfig(source, target, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100840 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100841 /* datastore, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100842 rpc = nc_rpc_copyconfig(source, target);
843 }
844 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200845 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200846 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200847 return create_error("Internal: Creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200848 }
849
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100850 res = netconf_op(session_key, rpc, NULL);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200851 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200852
853 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200854}
Radek Krejci035bf4e2012-07-25 10:59:09 +0200855
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100856static json_object *netconf_editconfig(const char* session_key, NC_DATASTORE source, NC_DATASTORE target, NC_EDIT_DEFOP_TYPE defop, NC_EDIT_ERROPT_TYPE erropt, NC_EDIT_TESTOPT_TYPE testopt, const char* config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +0200857{
858 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200859 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200860
861 /* create requests */
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100862 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200863 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200864 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200865 return create_error("Internal: Creating rpc request failed");
Radek Krejci62ab34b2012-07-26 13:42:05 +0200866 }
867
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100868 res = netconf_op(session_key, rpc, NULL);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200869 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200870
871 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200872}
873
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100874static json_object *netconf_killsession(const char* session_key, const char* sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200875{
876 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200877 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200878
879 /* create requests */
880 rpc = nc_rpc_killsession(sid);
881 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200882 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200883 return create_error("Internal: Creating rpc request failed");
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200884 }
885
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100886 res = netconf_op(session_key, rpc, NULL);
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200887 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200888 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200889}
890
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100891static json_object *netconf_onlytargetop(const char* session_key, NC_DATASTORE target, nc_rpc* (*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +0200892{
893 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200894 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +0200895
896 /* create requests */
Radek Krejci5cd7d422012-07-26 14:50:29 +0200897 rpc = op_func(target);
Radek Krejci2f318372012-07-26 14:22:35 +0200898 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200899 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200900 return create_error("Internal: Creating rpc request failed");
Radek Krejci2f318372012-07-26 14:22:35 +0200901 }
902
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100903 res = netconf_op(session_key, rpc, NULL);
Radek Krejci2f318372012-07-26 14:22:35 +0200904 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200905 return res;
Radek Krejci2f318372012-07-26 14:22:35 +0200906}
907
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100908static json_object *netconf_deleteconfig(const char* session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200909{
Tomas Cejka404d37e2013-04-13 02:31:35 +0200910 nc_rpc *rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200911 json_object *res = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +0200912 if (target != NC_DATASTORE_URL) {
913 rpc = nc_rpc_deleteconfig(target);
914 } else {
Tomas Cejkac7929632013-10-24 19:25:15 +0200915 rpc = nc_rpc_deleteconfig(target, url);
916 }
917 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200918 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200919 return create_error("Internal: Creating rpc request failed");
Tomas Cejka404d37e2013-04-13 02:31:35 +0200920 }
921
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100922 res = netconf_op(session_key, rpc, NULL);
Tomas Cejkac7929632013-10-24 19:25:15 +0200923 nc_rpc_free (rpc);
924 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +0200925}
926
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100927static json_object *netconf_lock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200928{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100929 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200930}
931
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100932static json_object *netconf_unlock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200933{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100934 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200935}
936
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100937static json_object *netconf_generic(const char* session_key, const char* content, char** data)
Radek Krejci80c10d92012-07-30 08:38:50 +0200938{
Tomas Cejka00635972013-06-03 15:10:52 +0200939 nc_rpc* rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200940 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +0200941
942 /* create requests */
943 rpc = nc_rpc_generic(content);
944 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200945 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200946 return create_error("Internal: Creating rpc request failed");
Radek Krejci80c10d92012-07-30 08:38:50 +0200947 }
948
Radek Krejcia332b692012-11-12 16:15:54 +0100949 if (data != NULL) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200950 // TODO ?free(*data);
951 (*data) = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100952 }
Radek Krejci80c10d92012-07-30 08:38:50 +0200953
954 /* get session where send the RPC */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100955 res = netconf_op(session_key, rpc, data);
Tomas Cejkac7929632013-10-24 19:25:15 +0200956 nc_rpc_free (rpc);
957 return res;
Radek Krejci80c10d92012-07-30 08:38:50 +0200958}
959
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200960/**
961 * @}
962 *//* netconf_operations */
963
Radek Krejci7338bde2012-08-10 12:57:30 +0200964void clb_print(NC_VERB_LEVEL level, const char* msg)
Radek Krejci469aab82012-07-22 18:42:20 +0200965{
Tomas Cejka8a86cc22014-09-18 15:35:07 +0200966#define FOREACH(I) \
Tomas Cejkacf44e522015-04-24 17:29:21 +0200967 I(NC_VERB_ERROR) I(NC_VERB_WARNING)
968
969#define CASE(VAL) case VAL: ERROR("%s: %s", #VAL, msg); \
Tomas Cejka8a86cc22014-09-18 15:35:07 +0200970 break;
971
Radek Krejci7338bde2012-08-10 12:57:30 +0200972 switch (level) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200973 FOREACH(CASE);
974 case NC_VERB_VERBOSE:
975 case NC_VERB_DEBUG:
976 DEBUG("DEBUG: %s", msg);
977 break;
Tomas Cejka8a86cc22014-09-18 15:35:07 +0200978 }
979 if (level == NC_VERB_ERROR) {
980 /* return global error */
981 netconf_callback_error_process(NULL /* tag */, NULL /* type */,
982 NULL /* severity */, NULL /* apptag */,
983 NULL /* path */, msg, NULL /* attribute */,
984 NULL /* element */, NULL /* ns */, NULL /* sid */);
Radek Krejci7338bde2012-08-10 12:57:30 +0200985 }
Radek Krejci469aab82012-07-22 18:42:20 +0200986}
987
Tomas Cejka64b87482013-06-03 16:30:53 +0200988/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +0200989 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +0200990 * Caller should free message memory.
991 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +0200992 * \return pointer to message
993 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100994char *get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +0200995{
996 /* read json in chunked framing */
997 unsigned int buffer_size = 0;
998 ssize_t buffer_len = 0;
999 char *buffer = NULL;
1000 char c;
1001 ssize_t ret;
1002 int i, chunk_len;
1003 char chunk_len_str[12];
1004
1005 while (1) {
1006 /* read chunk length */
1007 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
1008 if (buffer != NULL) {
1009 free (buffer);
1010 buffer = NULL;
1011 }
1012 break;
1013 }
1014 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
1015 if (buffer != NULL) {
1016 free (buffer);
1017 buffer = NULL;
1018 }
1019 break;
1020 }
1021 i=0;
1022 memset (chunk_len_str, 0, 12);
1023 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
1024 if (i==0 && c == '#') {
1025 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
1026 /* end but invalid */
1027 if (buffer != NULL) {
1028 free (buffer);
1029 buffer = NULL;
1030 }
1031 }
1032 /* end of message, double-loop break */
1033 goto msg_complete;
1034 }
1035 chunk_len_str[i++] = c;
1036 if (i==11) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001037 ERROR("Message is too long, buffer for length is not big enought!!!!");
Tomas Cejka64b87482013-06-03 16:30:53 +02001038 break;
1039 }
1040 }
1041 if (c != '\n') {
1042 if (buffer != NULL) {
1043 free (buffer);
1044 buffer = NULL;
1045 }
1046 break;
1047 }
1048 chunk_len_str[i] = 0;
1049 if ((chunk_len = atoi (chunk_len_str)) == 0) {
1050 if (buffer != NULL) {
1051 free (buffer);
1052 buffer = NULL;
1053 }
1054 break;
1055 }
1056 buffer_size += chunk_len+1;
1057 buffer = realloc (buffer, sizeof(char)*buffer_size);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001058 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
Tomas Cejka64b87482013-06-03 16:30:53 +02001059 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
1060 if (buffer != NULL) {
1061 free (buffer);
1062 buffer = NULL;
1063 }
1064 break;
1065 }
1066 buffer_len += ret;
1067 }
1068msg_complete:
1069 return buffer;
1070}
1071
Tomas Cejkad5b53772013-06-08 23:01:07 +02001072NC_DATASTORE parse_datastore(const char *ds)
1073{
1074 if (strcmp(ds, "running") == 0) {
1075 return NC_DATASTORE_RUNNING;
1076 } else if (strcmp(ds, "startup") == 0) {
1077 return NC_DATASTORE_STARTUP;
1078 } else if (strcmp(ds, "candidate") == 0) {
1079 return NC_DATASTORE_CANDIDATE;
Tomas Cejka4003a702013-10-01 00:02:45 +02001080 } else if (strcmp(ds, "url") == 0) {
1081 return NC_DATASTORE_URL;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001082 } else if (strcmp(ds, "config") == 0) {
1083 return NC_DATASTORE_CONFIG;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001084 }
1085 return -1;
1086}
1087
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001088NC_EDIT_TESTOPT_TYPE parse_testopt(const char *t)
1089{
1090 if (strcmp(t, "notset") == 0) {
1091 return NC_EDIT_TESTOPT_NOTSET;
1092 } else if (strcmp(t, "testset") == 0) {
1093 return NC_EDIT_TESTOPT_TESTSET;
1094 } else if (strcmp(t, "set") == 0) {
1095 return NC_EDIT_TESTOPT_SET;
1096 } else if (strcmp(t, "test") == 0) {
1097 return NC_EDIT_TESTOPT_TEST;
1098 }
1099 return NC_EDIT_TESTOPT_ERROR;
1100}
1101
Tomas Cejkad5b53772013-06-08 23:01:07 +02001102json_object *create_error(const char *errmess)
1103{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001104 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001105 json_object *reply = json_object_new_object();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001106 json_object *array = json_object_new_array();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001107 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001108 json_object_array_add(array, json_object_new_string(errmess));
1109 json_object_object_add(reply, "errors", array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001110 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001111 return reply;
1112
1113}
1114
1115json_object *create_data(const char *data)
1116{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001117 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001118 json_object *reply = json_object_new_object();
1119 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1120 json_object_object_add(reply, "data", json_object_new_string(data));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001121 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001122 return reply;
1123}
1124
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001125json_object *create_ok()
1126{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001127 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001128 json_object *reply = json_object_new_object();
1129 reply = json_object_new_object();
1130 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001131 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001132 return reply;
1133}
1134
Tomas Cejka09629492014-07-10 15:58:06 +02001135char *get_param_string(json_object *data, const char *name)
1136{
1137 json_object *js_tmp = NULL;
1138 char *res = NULL;
1139 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
1140 res = strdup(json_object_get_string(js_tmp));
1141 json_object_put(js_tmp);
1142 }
1143 return res;
1144}
1145
Michal Vaskoc3146782015-11-04 14:46:41 +01001146json_object *handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001147{
Tomas Cejka09629492014-07-10 15:58:06 +02001148 char *host = NULL;
1149 char *port = NULL;
1150 char *user = NULL;
1151 char *pass = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001152 json_object *capabilities = NULL;
1153 json_object *reply = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +01001154 const char *session_id = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001155 struct nc_cpblts* cpblts = NULL;
1156 unsigned int len, i;
1157
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001158 DEBUG("Request: Connect");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001159 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001160
Tomas Cejka09629492014-07-10 15:58:06 +02001161 host = get_param_string(request, "host");
1162 port = get_param_string(request, "port");
1163 user = get_param_string(request, "user");
1164 pass = get_param_string(request, "pass");
1165
1166 if (json_object_object_get_ex(request, "capabilities", &capabilities) == TRUE) {
1167 if ((capabilities != NULL) && ((len = json_object_array_length(capabilities)) > 0)) {
1168 cpblts = nc_cpblts_new(NULL);
1169 for (i=0; i<len; i++) {
1170 nc_cpblts_add(cpblts, json_object_get_string(json_object_array_get_idx(capabilities, i)));
1171 }
1172 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001173 ERROR("no capabilities specified");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001174 }
Tomas Cejka09629492014-07-10 15:58:06 +02001175 json_object_put(capabilities);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001176 }
Tomas Cejka09629492014-07-10 15:58:06 +02001177
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001178 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001179
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001180 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001181 if ((host == NULL) || (user == NULL)) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001182 ERROR("Cannot connect - insufficient input.");
Michal Vaskoc3146782015-11-04 14:46:41 +01001183 session_id = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001184 } else {
Michal Vaskoc3146782015-11-04 14:46:41 +01001185 session_id = netconf_connect(host, port, user, pass, cpblts);
1186 DEBUG("SID: %s", session_id);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001187 }
1188 if (cpblts != NULL) {
1189 nc_cpblts_free(cpblts);
1190 }
1191
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001192 GETSPEC_ERR_REPLY
1193
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001194 pthread_mutex_lock(&json_lock);
Michal Vaskoc3146782015-11-04 14:46:41 +01001195 if (session_id == NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001196 /* negative reply */
1197 if (err_reply == NULL) {
1198 reply = json_object_new_object();
1199 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1200 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
Tomas Cejkacf44e522015-04-24 17:29:21 +02001201 ERROR("Connection failed.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001202 } else {
1203 /* use filled err_reply from libnetconf's callback */
1204 reply = err_reply;
Tomas Cejkacf44e522015-04-24 17:29:21 +02001205 ERROR("Connect - error from libnetconf's callback.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001206 }
1207 } else {
1208 /* positive reply */
1209 reply = json_object_new_object();
1210 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Michal Vaskoc3146782015-11-04 14:46:41 +01001211 json_object_object_add(reply, "session", json_object_new_string(session_id));
Tomas Cejkad5b53772013-06-08 23:01:07 +02001212 }
Tomas Cejka09629492014-07-10 15:58:06 +02001213 memset(pass, 0, strlen(pass));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001214 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001215 CHECK_AND_FREE(host);
1216 CHECK_AND_FREE(user);
1217 CHECK_AND_FREE(port);
1218 CHECK_AND_FREE(pass);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001219 return reply;
1220}
1221
Michal Vaskoc3146782015-11-04 14:46:41 +01001222json_object *handle_op_get(json_object *request, const char *session_id)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001223{
Tomas Cejka09629492014-07-10 15:58:06 +02001224 char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001225 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001226 json_object *reply = NULL;
1227
Michal Vaskoc3146782015-11-04 14:46:41 +01001228 DEBUG("Request: get (session %s)", session_id);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001229
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001230 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001231 filter = get_param_string(request, "filter");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001232 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001233
Michal Vaskoc3146782015-11-04 14:46:41 +01001234 if ((data = netconf_get(session_id, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001235 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001236 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001237 reply = create_data(data);
1238 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001239 }
1240 return reply;
1241}
1242
Michal Vaskoc3146782015-11-04 14:46:41 +01001243json_object *handle_op_getconfig(json_object *request, const char *session_id)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001244{
1245 NC_DATASTORE ds_type_s = -1;
Tomas Cejka09629492014-07-10 15:58:06 +02001246 char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001247 char *data = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001248 char *source = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001249 json_object *reply = NULL;
1250
Michal Vaskoc3146782015-11-04 14:46:41 +01001251 DEBUG("Request: get-config (session %s)", session_id);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001252
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001253 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001254 filter = get_param_string(request, "filter");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001255
Tomas Cejka09629492014-07-10 15:58:06 +02001256 source = get_param_string(request, "source");
1257 if (source != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001258 ds_type_s = parse_datastore(source);
1259 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001260 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001261
Michal Vaskoc3146782015-11-04 14:46:41 +01001262 if ((int)ds_type_s == -1) {
Tomas Cejka09629492014-07-10 15:58:06 +02001263 reply = create_error("Invalid source repository type requested.");
1264 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001265 }
1266
Michal Vaskoc3146782015-11-04 14:46:41 +01001267 if ((data = netconf_getconfig(session_id, ds_type_s, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001268 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001269 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001270 reply = create_data(data);
1271 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001272 }
Tomas Cejka09629492014-07-10 15:58:06 +02001273finalize:
1274 CHECK_AND_FREE(filter);
1275 CHECK_AND_FREE(source);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001276 return reply;
1277}
1278
Michal Vaskoc3146782015-11-04 14:46:41 +01001279json_object *handle_op_getschema(json_object *request, const char *session_id)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001280{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001281 char *data = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001282 char *identifier = NULL;
1283 char *version = NULL;
1284 char *format = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001285 json_object *reply = NULL;
1286
Michal Vaskoc3146782015-11-04 14:46:41 +01001287 DEBUG("Request: get-schema (session %s)", session_id);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001288 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001289 identifier = get_param_string(request, "identifier");
1290 version = get_param_string(request, "version");
1291 format = get_param_string(request, "format");
Tomas Cejkab9e7efe2014-03-28 21:09:04 +01001292 pthread_mutex_unlock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001293
Tomas Cejkad5b53772013-06-08 23:01:07 +02001294 if (identifier == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001295 reply = create_error("No identifier for get-schema supplied.");
1296 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001297 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001298
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001299 DEBUG("get-schema(version: %s, format: %s)", version, format);
Michal Vaskoc3146782015-11-04 14:46:41 +01001300 if ((data = netconf_getschema(session_id, identifier, version, format, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001301 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001302 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001303 reply = create_data(data);
1304 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001305 }
Tomas Cejka09629492014-07-10 15:58:06 +02001306finalize:
1307 CHECK_AND_FREE(identifier);
1308 CHECK_AND_FREE(version);
1309 CHECK_AND_FREE(format);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001310 return reply;
1311}
1312
Michal Vaskoc3146782015-11-04 14:46:41 +01001313json_object *handle_op_editconfig(json_object *request, const char *session_id)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001314{
1315 NC_DATASTORE ds_type_s = -1;
1316 NC_DATASTORE ds_type_t = -1;
1317 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
1318 NC_EDIT_ERROPT_TYPE erropt_type = 0;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001319 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
Tomas Cejka09629492014-07-10 15:58:06 +02001320 char *defop = NULL;
1321 char *erropt = NULL;
1322 char *config = NULL;
1323 char *source = NULL;
1324 char *target = NULL;
1325 char *testopt = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001326 json_object *reply = NULL;
1327
Michal Vaskoc3146782015-11-04 14:46:41 +01001328 DEBUG("Request: edit-config (session %s)", session_id);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001329
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001330 pthread_mutex_lock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001331 /* get parameters */
Tomas Cejka09629492014-07-10 15:58:06 +02001332 defop = get_param_string(request, "default-operation");
1333 erropt = get_param_string(request, "error-option");
1334 target = get_param_string(request, "target");
1335 source = get_param_string(request, "source");
1336 config = get_param_string(request, "config");
1337 testopt = get_param_string(request, "test-option");
1338 pthread_mutex_unlock(&json_lock);
1339
1340 if (target != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001341 ds_type_t = parse_datastore(target);
1342 }
Tomas Cejka09629492014-07-10 15:58:06 +02001343 if (source != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001344 ds_type_s = parse_datastore(source);
1345 } else {
1346 /* source is optional, default value is config */
1347 ds_type_s = NC_DATASTORE_CONFIG;
1348 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001349
Tomas Cejkad5b53772013-06-08 23:01:07 +02001350 if (defop != NULL) {
1351 if (strcmp(defop, "merge") == 0) {
1352 defop_type = NC_EDIT_DEFOP_MERGE;
1353 } else if (strcmp(defop, "replace") == 0) {
1354 defop_type = NC_EDIT_DEFOP_REPLACE;
1355 } else if (strcmp(defop, "none") == 0) {
1356 defop_type = NC_EDIT_DEFOP_NONE;
1357 } else {
Tomas Cejka09629492014-07-10 15:58:06 +02001358 reply = create_error("Invalid default-operation parameter.");
1359 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001360 }
1361 } else {
1362 defop_type = NC_EDIT_DEFOP_NOTSET;
1363 }
1364
Tomas Cejkad5b53772013-06-08 23:01:07 +02001365 if (erropt != NULL) {
1366 if (strcmp(erropt, "continue-on-error") == 0) {
1367 erropt_type = NC_EDIT_ERROPT_CONT;
1368 } else if (strcmp(erropt, "stop-on-error") == 0) {
1369 erropt_type = NC_EDIT_ERROPT_STOP;
1370 } else if (strcmp(erropt, "rollback-on-error") == 0) {
1371 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
1372 } else {
Tomas Cejka09629492014-07-10 15:58:06 +02001373 reply = create_error("Invalid error-option parameter.");
1374 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001375 }
1376 } else {
1377 erropt_type = 0;
1378 }
1379
Michal Vaskoc3146782015-11-04 14:46:41 +01001380 if ((int)ds_type_t == -1) {
Tomas Cejka09629492014-07-10 15:58:06 +02001381 reply = create_error("Invalid target repository type requested.");
1382 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001383 }
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001384 if (ds_type_s == NC_DATASTORE_CONFIG) {
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001385 if (config == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001386 reply = create_error("Invalid config data parameter.");
1387 goto finalize;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001388 }
1389 } else if (ds_type_s == NC_DATASTORE_URL){
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001390 if (config == NULL) {
1391 config = "";
1392 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001393 }
1394
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001395 if (testopt != NULL) {
1396 testopt_type = parse_testopt(testopt);
1397 } else {
1398 testopt_type = NC_EDIT_TESTOPT_TESTSET;
1399 }
1400
Michal Vaskoc3146782015-11-04 14:46:41 +01001401 reply = netconf_editconfig(session_id, ds_type_s, ds_type_t, defop_type, erropt_type, testopt_type, config);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001402
1403 CHECK_ERR_SET_REPLY
Tomas Cejka09629492014-07-10 15:58:06 +02001404finalize:
1405 CHECK_AND_FREE(defop);
1406 CHECK_AND_FREE(erropt);
1407 CHECK_AND_FREE(config);
1408 CHECK_AND_FREE(source);
1409 CHECK_AND_FREE(target);
1410 CHECK_AND_FREE(testopt);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001411 return reply;
1412}
1413
Michal Vaskoc3146782015-11-04 14:46:41 +01001414json_object *handle_op_copyconfig(json_object *request, const char *session_id)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001415{
1416 NC_DATASTORE ds_type_s = -1;
1417 NC_DATASTORE ds_type_t = -1;
Tomas Cejka09629492014-07-10 15:58:06 +02001418 char *config = NULL;
1419 char *target = NULL;
1420 char *source = NULL;
1421 char *uri_src = NULL;
1422 char *uri_trg = NULL;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001423
Tomas Cejkad5b53772013-06-08 23:01:07 +02001424 json_object *reply = NULL;
1425
Michal Vaskoc3146782015-11-04 14:46:41 +01001426 DEBUG("Request: copy-config (session %s)", session_id);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001427
1428 /* get parameters */
Tomas Cejka09629492014-07-10 15:58:06 +02001429 pthread_mutex_lock(&json_lock);
1430 target = get_param_string(request, "target");
1431 source = get_param_string(request, "source");
1432 config = get_param_string(request, "config");
1433 uri_src = get_param_string(request, "uri-source");
1434 uri_trg = get_param_string(request, "uri-target");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001435 pthread_mutex_unlock(&json_lock);
1436
Tomas Cejka09629492014-07-10 15:58:06 +02001437 if (target != NULL) {
1438 ds_type_t = parse_datastore(target);
1439 }
1440 if (source != NULL) {
1441 ds_type_s = parse_datastore(source);
1442 } else {
1443 /* source == NULL *//* no explicit source specified -> use config data */
Tomas Cejkad5b53772013-06-08 23:01:07 +02001444 ds_type_s = NC_DATASTORE_CONFIG;
Tomas Cejka09629492014-07-10 15:58:06 +02001445 }
Michal Vaskoc3146782015-11-04 14:46:41 +01001446 if ((int)ds_type_s == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001447 /* source datastore specified, but it is invalid */
Tomas Cejka09629492014-07-10 15:58:06 +02001448 reply = create_error("Invalid source repository type requested.");
1449 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001450 }
1451
Michal Vaskoc3146782015-11-04 14:46:41 +01001452 if ((int)ds_type_t == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001453 /* invalid target datastore specified */
Tomas Cejka09629492014-07-10 15:58:06 +02001454 reply = create_error("Invalid target repository type requested.");
1455 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001456 }
1457
Tomas Cejka55c6df52014-02-20 12:59:33 +01001458 /* source can be missing when config is given */
1459 if (source == NULL && config == NULL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001460 reply = create_error("invalid input parameters - source and config is required.");
Tomas Cejka09629492014-07-10 15:58:06 +02001461 goto finalize;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001462 }
1463
1464 if (ds_type_s == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001465 if (uri_src == NULL) {
1466 uri_src = "";
1467 }
1468 }
1469 if (ds_type_t == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001470 if (uri_trg == NULL) {
1471 uri_trg = "";
1472 }
1473 }
Michal Vaskoc3146782015-11-04 14:46:41 +01001474 reply = netconf_copyconfig(session_id, ds_type_s, ds_type_t, config, uri_src, uri_trg);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001475
1476 CHECK_ERR_SET_REPLY
1477
Tomas Cejka09629492014-07-10 15:58:06 +02001478finalize:
1479 CHECK_AND_FREE(config);
1480 CHECK_AND_FREE(target);
1481 CHECK_AND_FREE(source);
1482 CHECK_AND_FREE(uri_src);
1483 CHECK_AND_FREE(uri_trg);
1484
Tomas Cejkad5b53772013-06-08 23:01:07 +02001485 return reply;
1486}
1487
Michal Vaskoc3146782015-11-04 14:46:41 +01001488json_object *handle_op_generic(json_object *request, const char *session_id)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001489{
1490 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001491 char *config = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001492 char *data = NULL;
1493
Michal Vaskoc3146782015-11-04 14:46:41 +01001494 DEBUG("Request: generic request for session %s", session_id);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001495
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001496 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001497 config = get_param_string(request, "content");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001498 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001499
Michal Vaskoc3146782015-11-04 14:46:41 +01001500 reply = netconf_generic(session_id, config, &data);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001501 if (reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001502 GETSPEC_ERR_REPLY
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001503 if (err_reply != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001504 /* use filled err_reply from libnetconf's callback */
1505 reply = err_reply;
1506 }
1507 } else {
1508 if (data == NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001509 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001510 reply = json_object_new_object();
1511 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001512 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001513 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001514 reply = create_data(data);
1515 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001516 }
1517 }
Tomas Cejka09629492014-07-10 15:58:06 +02001518 CHECK_AND_FREE(config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001519 return reply;
1520}
1521
Michal Vaskoc3146782015-11-04 14:46:41 +01001522json_object *handle_op_disconnect(json_object *UNUSED(request), const char *session_id)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001523{
1524 json_object *reply = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +01001525 DEBUG("Request: Disconnect session %s", session_id);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001526
Michal Vaskoc3146782015-11-04 14:46:41 +01001527 if (netconf_close(session_id, &reply) != EXIT_SUCCESS) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001528 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001529 } else {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001530 reply = create_ok();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001531 }
1532 return reply;
1533}
1534
Michal Vaskoc3146782015-11-04 14:46:41 +01001535json_object *handle_op_kill(json_object *request, const char *session_id)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001536{
1537 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001538 char *sid = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001539
Michal Vaskoc3146782015-11-04 14:46:41 +01001540 DEBUG("Request: kill-session, session %s", session_id);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001541
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001542 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001543 sid = get_param_string(request, "session-id");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001544 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001545
1546 if (sid == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001547 reply = create_error("Missing session-id parameter.");
1548 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001549 }
1550
Michal Vaskoc3146782015-11-04 14:46:41 +01001551 reply = netconf_killsession(session_id, sid);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001552
1553 CHECK_ERR_SET_REPLY
1554
Tomas Cejka09629492014-07-10 15:58:06 +02001555finalize:
1556 CHECK_AND_FREE(sid);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001557 return reply;
1558}
1559
Michal Vaskoc3146782015-11-04 14:46:41 +01001560json_object *handle_op_reloadhello(json_object *UNUSED(request), const char *session_id)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001561{
Tomas Cejka47387fd2013-06-10 20:37:46 +02001562 struct nc_session *temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001563 struct session_with_mutex * locked_session = NULL;
1564 json_object *reply = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +01001565 DEBUG("Request: get info about session %s", session_id);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001566
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001567 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001568 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001569 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001570 return NULL;
1571 }
1572
Michal Vaskoc3146782015-11-04 14:46:41 +01001573 for (locked_session = netconf_sessions_list;
Michal Vasko59ccb362015-11-05 10:21:44 +01001574 locked_session && strcmp(nc_session_get_id(locked_session->session), session_id);
Michal Vaskoc3146782015-11-04 14:46:41 +01001575 locked_session = locked_session->next);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001576 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001577 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001578 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001579 DEBUG("creating temporal NC session.");
Tomas Cejka47387fd2013-06-10 20:37:46 +02001580 temp_session = nc_session_connect_channel(locked_session->session, NULL);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001581 if (temp_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001582 prepare_status_message(locked_session, temp_session);
1583 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001584 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001585 temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001586 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001587 DEBUG("Reload hello failed due to channel establishment");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001588 reply = create_error("Reload was unsuccessful, connection failed.");
1589 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001590 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001591 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001592 DEBUG("UNLOCK wrlock %s", __func__);
1593 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001594 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka93b0e492014-03-26 15:47:45 +01001595 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001596 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001597 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001598 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001599 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001600 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001601 reply = create_error("Invalid session identifier.");
1602 }
1603
1604 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
1605 reply = locked_session->hello_message;
1606 }
1607 return reply;
1608}
1609
Michal Vaskoc3146782015-11-04 14:46:41 +01001610json_object *handle_op_info(json_object *UNUSED(request), const char *session_id)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001611{
1612 json_object *reply = NULL;
Tomas Cejka47387fd2013-06-10 20:37:46 +02001613 struct session_with_mutex * locked_session = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +01001614 DEBUG("Request: get info about session %s", session_id);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001615
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001616 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001617 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001618 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001619 }
1620
Michal Vaskoc3146782015-11-04 14:46:41 +01001621 for (locked_session = netconf_sessions_list;
Michal Vasko59ccb362015-11-05 10:21:44 +01001622 locked_session && strcmp(nc_session_get_id(locked_session->session), session_id);
Michal Vaskoc3146782015-11-04 14:46:41 +01001623 locked_session = locked_session->next);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001624 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001625 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001626 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001627 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001628 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001629 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001630 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001631 if (locked_session->hello_message != NULL) {
1632 reply = locked_session->hello_message;
1633 } else {
1634 reply = create_error("Invalid session identifier.");
1635 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001636 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001637 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001638 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001639 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001640 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001641 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001642 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001643 reply = create_error("Invalid session identifier.");
1644 }
1645
Tomas Cejka47387fd2013-06-10 20:37:46 +02001646
Tomas Cejkad5b53772013-06-08 23:01:07 +02001647 return reply;
1648}
1649
Tomas Cejka6b886e02013-07-05 09:53:17 +02001650void notification_history(time_t eventtime, const char *content)
1651{
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001652 json_object *notif_history_array = (json_object *) pthread_getspecific(notif_history_key);
1653 if (notif_history_array == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001654 ERROR("No list of notification history found.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001655 return;
1656 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001657 DEBUG("Got notification from history %lu.", (long unsigned) eventtime);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001658 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001659 json_object *notif = json_object_new_object();
1660 if (notif == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001661 ERROR("Could not allocate memory for notification (json).");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001662 goto failed;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001663 }
1664 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
1665 json_object_object_add(notif, "content", json_object_new_string(content));
1666 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001667failed:
1668 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001669}
1670
Michal Vaskoc3146782015-11-04 14:46:41 +01001671json_object *handle_op_ntfgethistory(json_object *request, const char *session_id)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001672{
1673 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001674 json_object *js_tmp = NULL;
1675 char *sid = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001676 struct session_with_mutex *locked_session = NULL;
1677 struct nc_session *temp_session = NULL;
1678 nc_rpc *rpc = NULL;
1679 time_t start = 0;
1680 time_t stop = 0;
Tomas Cejka09629492014-07-10 15:58:06 +02001681 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001682
Michal Vaskoc3146782015-11-04 14:46:41 +01001683 DEBUG("Request: get notification history, session %s", session_id);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001684
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001685 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001686 sid = get_param_string(request, "session");
1687
1688 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
1689 from = json_object_get_int64(js_tmp);
1690 json_object_put(js_tmp);
1691 }
1692 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
1693 to = json_object_get_int64(js_tmp);
1694 json_object_put(js_tmp);
1695 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001696 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001697
1698 start = time(NULL) + from;
1699 stop = time(NULL) + to;
1700
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001701 DEBUG("notification history interval %li %li", (long int) from, (long int) to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001702
1703 if (sid == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001704 reply = create_error("Missing session parameter.");
1705 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001706 }
1707
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001708 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001709 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001710 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka09629492014-07-10 15:58:06 +02001711 reply = create_error("Internal lock failed.");
1712 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001713 }
1714
Michal Vaskoc3146782015-11-04 14:46:41 +01001715 for (locked_session = netconf_sessions_list;
Michal Vasko59ccb362015-11-05 10:21:44 +01001716 locked_session && strcmp(nc_session_get_id(locked_session->session), session_id);
Michal Vaskoc3146782015-11-04 14:46:41 +01001717 locked_session = locked_session->next);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001718 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001719 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001720 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001721 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001722 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001723 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001724 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001725 DEBUG("creating temporal NC session.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001726 temp_session = nc_session_connect_channel(locked_session->session, NULL);
1727 if (temp_session != NULL) {
1728 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
1729 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001730 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001731 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001732 DEBUG("notifications: creating an rpc request failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001733 reply = create_error("notifications: creating an rpc request failed.");
1734 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001735 }
1736
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001737 DEBUG("Send NC subscribe.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001738 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001739 json_object *res = netconf_unlocked_op(temp_session, rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001740 if (res != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001741 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001742 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001743 DEBUG("Subscription RPC failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001744 reply = res;
1745 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001746 }
1747 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
1748
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001749 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001750 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001751 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001752 pthread_mutex_lock(&ntf_history_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001753 pthread_mutex_lock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001754 json_object *notif_history_array = json_object_new_array();
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001755 pthread_mutex_unlock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001756 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001757 ERROR("notif_history: cannot set thread-specific hash value.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001758 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001759
1760 ncntf_dispatch_receive(temp_session, notification_history);
1761
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001762 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001763 reply = json_object_new_object();
1764 json_object_object_add(reply, "notifications", notif_history_array);
1765 //json_object_put(notif_history_array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001766 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001767
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001768 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001769 pthread_mutex_unlock(&ntf_history_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001770 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001771 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001772 temp_session = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001773 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001774 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001775 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001776 DEBUG("Get history of notification failed due to channel establishment");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001777 reply = create_error("Get history of notification was unsuccessful, connection failed.");
1778 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001779 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001780 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001781 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001782 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001783 }
1784 reply = create_error("Invalid session identifier.");
1785 }
1786
Tomas Cejka09629492014-07-10 15:58:06 +02001787finalize:
1788 CHECK_AND_FREE(sid);
Tomas Cejka4003a702013-10-01 00:02:45 +02001789 return reply;
1790}
1791
Michal Vaskoc3146782015-11-04 14:46:41 +01001792json_object *handle_op_validate(json_object *request, const char *session_id)
Tomas Cejka4003a702013-10-01 00:02:45 +02001793{
1794 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001795 char *sid = NULL;
1796 char *target = NULL;
1797 char *url = NULL;
Tomas Cejka4003a702013-10-01 00:02:45 +02001798 nc_rpc *rpc = NULL;
1799 NC_DATASTORE target_ds;
1800
Michal Vaskoc3146782015-11-04 14:46:41 +01001801 DEBUG("Request: validate datastore, session %s", session_id);
Tomas Cejka4003a702013-10-01 00:02:45 +02001802
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001803 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001804 sid = get_param_string(request, "session");
1805 target = get_param_string(request, "target");
1806 url = get_param_string(request, "url");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001807 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02001808
1809
1810 if ((sid == NULL) || (target == NULL)) {
Tomas Cejka09629492014-07-10 15:58:06 +02001811 reply = create_error("Missing session parameter.");
1812 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001813 }
Tomas Cejka4003a702013-10-01 00:02:45 +02001814
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001815 /* validation */
1816 target_ds = parse_datastore(target);
1817 if (target_ds == NC_DATASTORE_URL) {
1818 if (url != NULL) {
1819 rpc = nc_rpc_validate(target_ds, url);
Tomas Cejka4003a702013-10-01 00:02:45 +02001820 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001821 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
Tomas Cejka4003a702013-10-01 00:02:45 +02001822 || (target_ds == NC_DATASTORE_CANDIDATE)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001823 rpc = nc_rpc_validate(target_ds);
1824 }
1825 if (rpc == NULL) {
1826 DEBUG("mod_netconf: creating rpc request failed");
1827 reply = create_error("Creation of RPC request failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001828 goto finalize;
Tomas Cejka4003a702013-10-01 00:02:45 +02001829 }
1830
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001831 DEBUG("Request: validate datastore");
Michal Vaskoc3146782015-11-04 14:46:41 +01001832 if ((reply = netconf_op(session_id, rpc, NULL)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001833
1834 CHECK_ERR_SET_REPLY
1835
1836 if (reply == NULL) {
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001837 DEBUG("Request: validation ok.");
1838 reply = create_ok();
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001839 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001840 }
1841 nc_rpc_free (rpc);
Tomas Cejka09629492014-07-10 15:58:06 +02001842finalize:
1843 CHECK_AND_FREE(sid);
1844 CHECK_AND_FREE(target);
1845 CHECK_AND_FREE(url);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001846 return reply;
1847}
1848
David Kupka8e60a372012-09-04 09:15:20 +02001849void * thread_routine (void * arg)
1850{
1851 void * retval = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001852 struct pollfd fds;
Tomas Cejka00635972013-06-03 15:10:52 +02001853 json_object *request = NULL;
1854 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001855 json_object *js_tmp = NULL;
1856 int operation = (-1);
1857 NC_DATASTORE ds_type_t = -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001858 int status = 0;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001859 const char *msgtext;
Michal Vaskoc3146782015-11-04 14:46:41 +01001860 char *session_id = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001861 char *target = NULL;
1862 char *url = NULL;
Tomas Cejka64b87482013-06-03 16:30:53 +02001863 char *chunked_out_msg = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001864 //server_rec * server = ((struct pass_to_thread*)arg)->server;
David Kupka8e60a372012-09-04 09:15:20 +02001865 int client = ((struct pass_to_thread*)arg)->client;
1866
Tomas Cejka00635972013-06-03 15:10:52 +02001867 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001868
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001869 /* init thread specific err_reply memory */
Tomas Cejka442258e2014-04-01 18:17:18 +02001870 create_err_reply_p();
1871
David Kupka8e60a372012-09-04 09:15:20 +02001872 while (!isterminated) {
1873 fds.fd = client;
1874 fds.events = POLLIN;
1875 fds.revents = 0;
1876
1877 status = poll(&fds, 1, 1000);
1878
1879 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
1880 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
David Kupka8e60a372012-09-04 09:15:20 +02001881 continue;
1882 } else if (status < 0) {
1883 /* 0: poll time outed
1884 * close socket and ignore this request from the client, it can try it again
1885 * -1: poll failed
1886 * something wrong happend, close this socket and wait for another request
1887 */
David Kupka8e60a372012-09-04 09:15:20 +02001888 close(client);
1889 break;
1890 }
1891 /* status > 0 */
1892
1893 /* check the status of the socket */
1894
1895 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
1896 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
1897 /* close client's socket (it's probably already closed by client */
David Kupka8e60a372012-09-04 09:15:20 +02001898 close(client);
1899 break;
1900 }
1901
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001902 DEBUG("Get framed message...");
1903 buffer = get_framed_message(client);
David Kupka8e60a372012-09-04 09:15:20 +02001904
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001905 DEBUG("Check read buffer.");
David Kupka8e60a372012-09-04 09:15:20 +02001906 if (buffer != NULL) {
Tomas Cejka00635972013-06-03 15:10:52 +02001907 enum json_tokener_error jerr;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001908 pthread_mutex_lock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001909 request = json_tokener_parse_verbose(buffer, &jerr);
1910 if (jerr != json_tokener_success) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001911 ERROR("JSON parsing error");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001912 pthread_mutex_unlock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001913 continue;
1914 }
David Kupka8e60a372012-09-04 09:15:20 +02001915
Michal Vaskoc3146782015-11-04 14:46:41 +01001916 session_id = get_param_string(request, "session");
Tomas Cejka09629492014-07-10 15:58:06 +02001917 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
1918 operation = json_object_get_int(js_tmp);
1919 json_object_put(js_tmp);
1920 js_tmp = NULL;
1921 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001922 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001923 if (operation == -1) {
1924 reply = create_error("Missing operation type form frontend.");
1925 goto send_reply;
1926 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001927
Michal Vaskoc3146782015-11-04 14:46:41 +01001928 DEBUG("operation %d session_id %s.", operation, session_id);
David Kupka8e60a372012-09-04 09:15:20 +02001929 /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
Michal Vaskoc3146782015-11-04 14:46:41 +01001930 if (operation != MSG_CONNECT && session_id == NULL) {
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001931 reply = create_error("Missing session specification.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001932 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001933 msgtext = json_object_to_json_string(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001934 pthread_mutex_unlock(&json_lock);
1935
David Kupka8e60a372012-09-04 09:15:20 +02001936 send(client, msgtext, strlen(msgtext) + 1, 0);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001937
1938 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001939 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001940 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001941 /* there is some stupid client, so close the connection to give a chance to some other client */
1942 close(client);
1943 break;
1944 }
1945
David Kupka8e60a372012-09-04 09:15:20 +02001946 /* null global JSON error-reply */
Tomas Cejka442258e2014-04-01 18:17:18 +02001947 clean_err_reply();
David Kupka8e60a372012-09-04 09:15:20 +02001948
1949 /* prepare reply envelope */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001950 if (reply != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001951 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001952 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001953 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001954 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001955 reply = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001956
1957 /* process required operation */
1958 switch (operation) {
1959 case MSG_CONNECT:
Michal Vaskoc3146782015-11-04 14:46:41 +01001960 reply = handle_op_connect(request);
David Kupka8e60a372012-09-04 09:15:20 +02001961 break;
1962 case MSG_GET:
Michal Vaskoc3146782015-11-04 14:46:41 +01001963 reply = handle_op_get(request, session_id);
David Kupka8e60a372012-09-04 09:15:20 +02001964 break;
1965 case MSG_GETCONFIG:
Michal Vaskoc3146782015-11-04 14:46:41 +01001966 reply = handle_op_getconfig(request, session_id);
David Kupka8e60a372012-09-04 09:15:20 +02001967 break;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001968 case MSG_GETSCHEMA:
Michal Vaskoc3146782015-11-04 14:46:41 +01001969 reply = handle_op_getschema(request, session_id);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001970 break;
David Kupka8e60a372012-09-04 09:15:20 +02001971 case MSG_EDITCONFIG:
Michal Vaskoc3146782015-11-04 14:46:41 +01001972 reply = handle_op_editconfig(request, session_id);
David Kupka8e60a372012-09-04 09:15:20 +02001973 break;
1974 case MSG_COPYCONFIG:
Michal Vaskoc3146782015-11-04 14:46:41 +01001975 reply = handle_op_copyconfig(request, session_id);
David Kupka8e60a372012-09-04 09:15:20 +02001976 break;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001977
David Kupka8e60a372012-09-04 09:15:20 +02001978 case MSG_DELETECONFIG:
David Kupka8e60a372012-09-04 09:15:20 +02001979 case MSG_LOCK:
David Kupka8e60a372012-09-04 09:15:20 +02001980 case MSG_UNLOCK:
Tomas Cejkad5b53772013-06-08 23:01:07 +02001981 /* get parameters */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001982 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001983 target = get_param_string(request, "target");
1984 pthread_mutex_unlock(&json_lock);
1985 if (target != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001986 ds_type_t = parse_datastore(target);
1987 }
David Kupka8e60a372012-09-04 09:15:20 +02001988
Michal Vaskoc3146782015-11-04 14:46:41 +01001989 if ((int)ds_type_t == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001990 reply = create_error("Invalid target repository type requested.");
David Kupka8e60a372012-09-04 09:15:20 +02001991 break;
1992 }
David Kupka8e60a372012-09-04 09:15:20 +02001993 switch(operation) {
1994 case MSG_DELETECONFIG:
Michal Vaskoc3146782015-11-04 14:46:41 +01001995 DEBUG("Request: delete-config (session %s)", session_id);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001996 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001997 url = get_param_string(request, "url");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001998 pthread_mutex_unlock(&json_lock);
Michal Vaskoc3146782015-11-04 14:46:41 +01001999 reply = netconf_deleteconfig(session_id, ds_type_t, url);
David Kupka8e60a372012-09-04 09:15:20 +02002000 break;
2001 case MSG_LOCK:
Michal Vaskoc3146782015-11-04 14:46:41 +01002002 DEBUG("Request: lock (session %s)", session_id);
2003 reply = netconf_lock(session_id, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02002004 break;
2005 case MSG_UNLOCK:
Michal Vaskoc3146782015-11-04 14:46:41 +01002006 DEBUG("Request: unlock (session %s)", session_id);
2007 reply = netconf_unlock(session_id, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02002008 break;
2009 default:
Tomas Cejkac7929632013-10-24 19:25:15 +02002010 reply = create_error("Internal: Unknown request type.");
David Kupka8e60a372012-09-04 09:15:20 +02002011 break;
2012 }
2013
Tomas Cejka442258e2014-04-01 18:17:18 +02002014 CHECK_ERR_SET_REPLY
Tomas Cejkac7929632013-10-24 19:25:15 +02002015 if (reply == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02002016 reply = create_ok();
David Kupka8e60a372012-09-04 09:15:20 +02002017 }
2018 break;
2019 case MSG_KILL:
Michal Vaskoc3146782015-11-04 14:46:41 +01002020 reply = handle_op_kill(request, session_id);
David Kupka8e60a372012-09-04 09:15:20 +02002021 break;
2022 case MSG_DISCONNECT:
Michal Vaskoc3146782015-11-04 14:46:41 +01002023 reply = handle_op_disconnect(request, session_id);
David Kupka8e60a372012-09-04 09:15:20 +02002024 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02002025 case MSG_RELOADHELLO:
Michal Vaskoc3146782015-11-04 14:46:41 +01002026 reply = handle_op_reloadhello(request, session_id);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002027 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02002028 case MSG_INFO:
Michal Vaskoc3146782015-11-04 14:46:41 +01002029 reply = handle_op_info(request, session_id);
David Kupka8e60a372012-09-04 09:15:20 +02002030 break;
2031 case MSG_GENERIC:
Michal Vaskoc3146782015-11-04 14:46:41 +01002032 reply = handle_op_generic(request, session_id);
David Kupka8e60a372012-09-04 09:15:20 +02002033 break;
Tomas Cejka6b886e02013-07-05 09:53:17 +02002034 case MSG_NTF_GETHISTORY:
Michal Vaskoc3146782015-11-04 14:46:41 +01002035 reply = handle_op_ntfgethistory(request, session_id);
Tomas Cejka6b886e02013-07-05 09:53:17 +02002036 break;
Tomas Cejka4003a702013-10-01 00:02:45 +02002037 case MSG_VALIDATE:
Michal Vaskoc3146782015-11-04 14:46:41 +01002038 reply = handle_op_validate(request, session_id);
Tomas Cejka4003a702013-10-01 00:02:45 +02002039 break;
David Kupka8e60a372012-09-04 09:15:20 +02002040 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002041 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002042 reply = create_error("Operation not supported.");
David Kupka8e60a372012-09-04 09:15:20 +02002043 break;
2044 }
Tomas Cejka09629492014-07-10 15:58:06 +02002045 /* free parameters */
Tomas Cejka09629492014-07-10 15:58:06 +02002046 CHECK_AND_FREE(url);
2047 CHECK_AND_FREE(target);
2048 request = NULL;
2049 operation = (-1);
2050 ds_type_t = (-1);
2051
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002052 DEBUG("Clean request json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02002053 if (request != NULL) {
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002054 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002055 json_object_put(request);
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002056 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002057 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002058 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02002059
2060
2061send_reply:
David Kupka1e3e4c82012-09-04 09:32:15 +02002062 /* send reply to caller */
2063 if (reply != NULL) {
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002064 pthread_mutex_lock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02002065 msgtext = json_object_to_json_string(reply);
Tomas Cejka866f2282014-09-18 15:20:26 +02002066 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int) strlen(msgtext), msgtext) == -1) {
Tomas Cejka00635972013-06-03 15:10:52 +02002067 if (buffer != NULL) {
2068 free(buffer);
2069 buffer = NULL;
2070 }
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002071 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02002072 break;
2073 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002074 pthread_mutex_unlock(&json_lock);
2075
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002076 DEBUG("Send framed reply json object.");
Tomas Cejka64b87482013-06-03 16:30:53 +02002077 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002078 DEBUG("Clean reply json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002079 pthread_mutex_lock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02002080 json_object_put(reply);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002081 reply = NULL;
2082 DEBUG("Clean message buffer.");
Tomas Cejka09629492014-07-10 15:58:06 +02002083 CHECK_AND_FREE(chunked_out_msg);
Tomas Cejka64b87482013-06-03 16:30:53 +02002084 chunked_out_msg = NULL;
Tomas Cejka00635972013-06-03 15:10:52 +02002085 if (buffer != NULL) {
2086 free(buffer);
2087 buffer = NULL;
2088 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002089 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02002090 clean_err_reply();
David Kupka1e3e4c82012-09-04 09:32:15 +02002091 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002092 ERROR("Reply is NULL, shouldn't be...");
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002093 continue;
David Kupka8e60a372012-09-04 09:15:20 +02002094 }
2095 }
2096 }
David Kupka8e60a372012-09-04 09:15:20 +02002097 free (arg);
2098
Tomas Cejka442258e2014-04-01 18:17:18 +02002099 free_err_reply();
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002100
David Kupka8e60a372012-09-04 09:15:20 +02002101 return retval;
2102}
2103
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002104/**
2105 * \brief Close all open NETCONF sessions.
2106 *
2107 * During termination of mod_netconf, it is useful to close all remaining
2108 * sessions. This function iterates over the list of sessions and close them
2109 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002110 */
Michal Vaskoc3146782015-11-04 14:46:41 +01002111static void close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002112{
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002113 struct session_with_mutex *swm = NULL;
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002114 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002115
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002116 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002117 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002118 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002119 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002120 return;
2121 }
Michal Vaskoc3146782015-11-04 14:46:41 +01002122 for (swm = netconf_sessions_list; swm; swm = swm->next) {
2123 DEBUG("Closing NETCONF session (%s).", nc_session_get_id(swm->session));
Tomas Cejka47387fd2013-06-10 20:37:46 +02002124
Michal Vaskoc3146782015-11-04 14:46:41 +01002125 /* close_and_free_session handles locking on its own */
2126 close_and_free_session(swm);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002127 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002128 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002129 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002130 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002131 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002132 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002133}
2134
Michal Vaskoc3146782015-11-04 14:46:41 +01002135static void check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002136{
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002137 struct nc_session *ns = NULL;
2138 struct session_with_mutex *swm = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +01002139 time_t current_time = time(NULL);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002140 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002141
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002142 /* get exclusive access to sessions_list (conns) */
Tomas Cejka866f2282014-09-18 15:20:26 +02002143 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002144 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002145 return;
2146 }
Michal Vaskoc3146782015-11-04 14:46:41 +01002147 for (swm = netconf_sessions_list; swm; swm = swm->next) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002148 ns = swm->session;
2149 if (ns == NULL) {
2150 continue;
2151 }
2152 pthread_mutex_lock(&swm->lock);
Michal Vaskoc3146782015-11-04 14:46:41 +01002153 if ((current_time - swm->last_activity) > ACTIVITY_TIMEOUT) {
2154 DEBUG("Closing NETCONF session (%s).", nc_session_get_id(swm->session));
Tomas Cejka47387fd2013-06-10 20:37:46 +02002155
2156 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002157 close_and_free_session(swm);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002158 } else {
2159 pthread_mutex_unlock(&swm->lock);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002160 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002161 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002162 /* get exclusive access to sessions_list (conns) */
Tomas Cejka866f2282014-09-18 15:20:26 +02002163 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002164 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002165 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002166}
2167
2168
2169/**
Radek Krejcif23850c2012-07-23 16:14:17 +02002170 * This is actually implementation of NETCONF client
2171 * - requests are received from UNIX socket in the predefined format
2172 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01002173 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02002174 *
2175 */
Michal Vaskoc3146782015-11-04 14:46:41 +01002176static void forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02002177{
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002178 struct timeval tv;
Radek Krejci469aab82012-07-22 18:42:20 +02002179 struct sockaddr_un local, remote;
David Kupka8e60a372012-09-04 09:15:20 +02002180 int lsock, client, ret, i, pthread_count = 0;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002181 unsigned int olds = 0, timediff = 0;
David Kupka8e60a372012-09-04 09:15:20 +02002182 socklen_t len;
David Kupka8e60a372012-09-04 09:15:20 +02002183 struct pass_to_thread * arg;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002184 pthread_t * ptids = calloc(1, sizeof(pthread_t));
David Kupka8e60a372012-09-04 09:15:20 +02002185 struct timespec maxtime;
2186 pthread_rwlockattr_t lock_attrs;
Tomas Cejka404d37e2013-04-13 02:31:35 +02002187 #ifdef WITH_NOTIFICATIONS
2188 char use_notifications = 0;
2189 #endif
David Kupka8e60a372012-09-04 09:15:20 +02002190
Tomas Cejka404d37e2013-04-13 02:31:35 +02002191 /* wait at most 5 seconds for every thread to terminate */
David Kupka8e60a372012-09-04 09:15:20 +02002192 maxtime.tv_sec = 5;
2193 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02002194
Tomas Cejka04e08f42014-03-27 19:52:34 +01002195#ifdef HAVE_UNIXD_SETUP_CHILD
Radek Krejcif23850c2012-07-23 16:14:17 +02002196 /* change uid and gid of process for security reasons */
Radek Krejci469aab82012-07-22 18:42:20 +02002197 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01002198#else
2199# ifdef SU_GROUP
2200 if (strlen(SU_GROUP) > 0) {
2201 struct group *g = getgrnam(SU_GROUP);
2202 if (g == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002203 ERROR("GID (%s) was not found.", SU_GROUP);
Tomas Cejka04e08f42014-03-27 19:52:34 +01002204 return;
2205 }
2206 if (setgid(g->gr_gid) != 0) {
2207
Tomas Cejkacf44e522015-04-24 17:29:21 +02002208 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
Tomas Cejka04e08f42014-03-27 19:52:34 +01002209 return;
2210 }
2211 }
2212# else
2213 DEBUG("no SU_GROUP");
2214# endif
2215# ifdef SU_USER
2216 if (strlen(SU_USER) > 0) {
2217 struct passwd *p = getpwnam(SU_USER);
2218 if (p == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002219 ERROR("UID (%s) was not found.", SU_USER);
Tomas Cejka04e08f42014-03-27 19:52:34 +01002220 return;
2221 }
2222 if (setuid(p->pw_uid) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002223 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
Tomas Cejka04e08f42014-03-27 19:52:34 +01002224 return;
2225 }
2226 }
2227# else
2228 DEBUG("no SU_USER");
2229# endif
2230#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002231
Tomas Cejka04e08f42014-03-27 19:52:34 +01002232 /* try to remove if exists */
2233 unlink(sockname);
2234
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002235 /* create listening UNIX socket to accept incoming connections */
Radek Krejci469aab82012-07-22 18:42:20 +02002236 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002237 ERROR("Creating socket failed (%s)", strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002238 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002239 }
2240
2241 local.sun_family = AF_UNIX;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002242 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
Radek Krejci469aab82012-07-22 18:42:20 +02002243 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
2244
2245 if (bind(lsock, (struct sockaddr *) &local, len) == -1) {
2246 if (errno == EADDRINUSE) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002247 ERROR("mod_netconf socket address already in use");
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002248 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002249 }
Tomas Cejkacf44e522015-04-24 17:29:21 +02002250 ERROR("Binding socket failed (%s)", strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002251 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002252 }
2253
2254 if (listen(lsock, MAX_SOCKET_CL) == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002255 ERROR("Setting up listen socket failed (%s)", strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002256 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002257 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002258 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02002259
Tomas Cejka04e08f42014-03-27 19:52:34 +01002260 uid_t user = -1;
2261 if (strlen(CHOWN_USER) > 0) {
2262 struct passwd *p = getpwnam(CHOWN_USER);
2263 if (p != NULL) {
2264 user = p->pw_uid;
2265 }
2266 }
2267 gid_t group = -1;
2268 if (strlen(CHOWN_GROUP) > 0) {
2269 struct group *g = getgrnam(CHOWN_GROUP);
2270 if (g != NULL) {
2271 group = g->gr_gid;
2272 }
2273 }
2274 if (chown(sockname, user, group) == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002275 ERROR("Chown on socket file failed (%s).", strerror(errno));
Tomas Cejka04e08f42014-03-27 19:52:34 +01002276 }
2277
Tomas Cejkaba21b382013-04-13 02:37:32 +02002278 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02002279
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002280 #ifdef WITH_NOTIFICATIONS
Michal Vaskoc3146782015-11-04 14:46:41 +01002281 if (notification_init() == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002282 ERROR("libwebsockets initialization failed");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002283 use_notifications = 0;
2284 } else {
2285 use_notifications = 1;
2286 }
2287 #endif
2288
Radek Krejci469aab82012-07-22 18:42:20 +02002289 /* setup libnetconf's callbacks */
2290 nc_verbosity(NC_VERB_DEBUG);
Radek Krejci469aab82012-07-22 18:42:20 +02002291 nc_callback_print(clb_print);
2292 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
2293 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
2294 nc_callback_sshauth_password(netconf_callback_sshauth_password);
Tomas Cejkab34b7b12015-06-21 22:54:11 +02002295 nc_callback_sshauth_passphrase(netconf_callback_sshauth_passphrase);
Radek Krejcic11fd862012-07-26 12:41:21 +02002296 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02002297
2298 /* disable publickey authentication */
2299 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
2300
David Kupka8e60a372012-09-04 09:15:20 +02002301 /* create mutex protecting session list */
2302 pthread_rwlockattr_init(&lock_attrs);
2303 /* rwlock is shared only with threads in this process */
2304 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
2305 /* create rw lock */
2306 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002307 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002308 goto error_exit;
David Kupka8e60a372012-09-04 09:15:20 +02002309 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002310 pthread_mutex_init(&ntf_history_lock, NULL);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002311 pthread_mutex_init(&json_lock, NULL);
Tomas Cejkacf44e522015-04-24 17:29:21 +02002312 DEBUG("Initialization of notification history.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002313 if (pthread_key_create(&notif_history_key, NULL) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002314 ERROR("Initialization of notification history failed.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002315 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002316 if (pthread_key_create(&err_reply_key, NULL) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002317 ERROR("Initialization of reply key failed.");
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002318 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02002319
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002320 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002321 while (isterminated == 0) {
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002322 gettimeofday(&tv, NULL);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002323 timediff = (unsigned int)tv.tv_sec - olds;
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002324 #ifdef WITH_NOTIFICATIONS
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002325 if (use_notifications == 1) {
2326 notification_handle();
2327 }
2328 #endif
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002329 if (timediff > ACTIVITY_CHECK_INTERVAL) {
Michal Vaskoc3146782015-11-04 14:46:41 +01002330 check_timeout_and_close();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002331 }
Radek Krejci469aab82012-07-22 18:42:20 +02002332
2333 /* open incoming connection if any */
David Kupka8e60a372012-09-04 09:15:20 +02002334 len = sizeof(remote);
2335 client = accept(lsock, (struct sockaddr *) &remote, &len);
Radek Krejci469aab82012-07-22 18:42:20 +02002336 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
Michal Vaskoc3146782015-11-04 14:46:41 +01002337 sleep(SLEEP_TIME);
Radek Krejci469aab82012-07-22 18:42:20 +02002338 continue;
2339 } else if (client == -1 && (errno == EINTR)) {
2340 continue;
2341 } else if (client == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002342 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
Radek Krejci469aab82012-07-22 18:42:20 +02002343 continue;
2344 }
Radek Krejci469aab82012-07-22 18:42:20 +02002345
2346 /* set client's socket as non-blocking */
Radek Krejcif23850c2012-07-23 16:14:17 +02002347 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002348
Michal Vaskoc3146782015-11-04 14:46:41 +01002349 arg = malloc(sizeof(struct pass_to_thread));
David Kupka8e60a372012-09-04 09:15:20 +02002350 arg->client = client;
David Kupka8e60a372012-09-04 09:15:20 +02002351 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02002352
David Kupka8e60a372012-09-04 09:15:20 +02002353 /* start new thread. It will serve this particular request and then terminate */
2354 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void*)arg)) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002355 ERROR("Creating POSIX thread failed: %d\n", ret);
David Kupka8e60a372012-09-04 09:15:20 +02002356 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002357 DEBUG("Thread %lu created", ptids[pthread_count]);
David Kupka8e60a372012-09-04 09:15:20 +02002358 pthread_count++;
2359 ptids = realloc (ptids, sizeof(pthread_t)*(pthread_count+1));
2360 ptids[pthread_count] = 0;
2361 }
Radek Krejci469aab82012-07-22 18:42:20 +02002362
David Kupka8e60a372012-09-04 09:15:20 +02002363 /* check if some thread already terminated, free some resources by joining it */
2364 for (i=0; i<pthread_count; i++) {
2365 if (pthread_tryjoin_np (ptids[i], (void**)&arg) == 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002366 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
David Kupka8e60a372012-09-04 09:15:20 +02002367 pthread_count--;
2368 if (pthread_count > 0) {
2369 /* place last Thread ID on the place of joined one */
2370 ptids[i] = ptids[pthread_count];
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002371 }
Radek Krejci469aab82012-07-22 18:42:20 +02002372 }
2373 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002374 DEBUG("Running %d threads", pthread_count);
Radek Krejci469aab82012-07-22 18:42:20 +02002375 }
2376
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002377 DEBUG("mod_netconf terminating...");
David Kupka8e60a372012-09-04 09:15:20 +02002378 /* join all threads */
2379 for (i=0; i<pthread_count; i++) {
2380 pthread_timedjoin_np (ptids[i], (void**)&arg, &maxtime);
2381 }
Radek Krejci469aab82012-07-22 18:42:20 +02002382
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002383 #ifdef WITH_NOTIFICATIONS
2384 notification_close();
2385 #endif
2386
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002387 /* close all NETCONF sessions */
Michal Vaskoc3146782015-11-04 14:46:41 +01002388 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002389
David Kupka8e60a372012-09-04 09:15:20 +02002390 /* destroy rwlock */
2391 pthread_rwlock_destroy(&session_lock);
2392 pthread_rwlockattr_destroy(&lock_attrs);
2393
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002394 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02002395
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002396 free(ptids);
2397 close(lsock);
Michal Vaskoc3146782015-11-04 14:46:41 +01002398 exit(0);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002399 return;
2400error_exit:
2401 close(lsock);
2402 free(ptids);
2403 return;
Radek Krejci469aab82012-07-22 18:42:20 +02002404}
2405
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002406int main(int argc, char **argv)
2407{
Michal Vaskoc3146782015-11-04 14:46:41 +01002408 struct sigaction action;
2409 sigset_t block_mask;
2410
2411 if (argc > 1) {
2412 sockname = argv[1];
2413 } else {
2414 sockname = SOCKET_FILENAME;
2415 }
2416
2417 sigfillset (&block_mask);
2418 action.sa_handler = signal_handler;
2419 action.sa_mask = block_mask;
2420 action.sa_flags = 0;
2421 sigaction(SIGINT, &action, NULL);
2422 sigaction(SIGTERM, &action, NULL);
2423
2424 forked_proc();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002425 DEBUG("Terminated");
2426 return 0;
2427}