blob: 7a71e1716b3915c8923ed5103c2beba250c23a6c [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 */
46
Radek Krejci7b4ddd02012-07-30 08:09:58 +020047#include <unistd.h>
48#include <poll.h>
Radek Krejci469aab82012-07-22 18:42:20 +020049#include <sys/types.h>
50#include <sys/socket.h>
51#include <sys/un.h>
Tomas Cejkad340dbf2013-03-24 20:36:57 +010052#include <sys/fcntl.h>
David Kupka8e60a372012-09-04 09:15:20 +020053#include <pthread.h>
54#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020055
56#include <unixd.h>
57#include <httpd.h>
58#include <http_log.h>
59#include <http_config.h>
60
61#include <apr_sha1.h>
62#include <apr_hash.h>
63#include <apr_signal.h>
64#include <apr_strings.h>
Radek Krejci469aab82012-07-22 18:42:20 +020065
Radek Krejci8fd1f5e2012-07-24 17:33:36 +020066#include <json/json.h>
67
Radek Krejci469aab82012-07-22 18:42:20 +020068#include <libnetconf.h>
Tomas Cejkab3cc64f2013-05-03 19:44:54 +020069#include <libnetconf_ssh.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020070
Tomas Cejkad340dbf2013-03-24 20:36:57 +010071#ifdef WITH_NOTIFICATIONS
72#include "notification_module.h"
73#endif
74
Tomas Cejka94da2c52013-01-08 18:20:30 +010075#include "message_type.h"
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020076#include "mod_netconf.h"
Radek Krejci469aab82012-07-22 18:42:20 +020077
78#define MAX_PROCS 5
Radek Krejci6cb08982012-07-25 18:01:06 +020079#define SOCKET_FILENAME "/tmp/mod_netconf.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020080#define MAX_SOCKET_CL 10
81#define BUFFER_SIZE 4096
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020082#define NOTIFICATION_QUEUE_SIZE 10
83#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
Tomas Cejka04e39952013-04-19 11:49:38 +020084#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020085
86/* sleep in master process for non-blocking socket reading */
87#define SLEEP_TIME 200
88
89#ifndef offsetof
90#define offsetof(type, member) ((size_t) ((type *) 0)->member)
91#endif
92
Tomas Cejkaef531ee2013-11-12 16:07:00 +010093server_rec *http_server = NULL;
94
Tomas Cejka027f3bc2012-11-10 20:28:36 +010095/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020096struct timeval timeout = { 1, 0 };
97
Tomas Cejka5064c232013-01-17 09:30:58 +010098#define NCWITHDEFAULTS NCWD_MODE_NOTSET
99
100
Radek Krejci469aab82012-07-22 18:42:20 +0200101#define MSG_OK 0
102#define MSG_OPEN 1
103#define MSG_DATA 2
104#define MSG_CLOSE 3
105#define MSG_ERROR 4
106#define MSG_UNKNOWN 5
107
Radek Krejci469aab82012-07-22 18:42:20 +0200108module AP_MODULE_DECLARE_DATA netconf_module;
109
Tomas Cejka47387fd2013-06-10 20:37:46 +0200110pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200111pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100112pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100113pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
114
Tomas Cejka47387fd2013-06-10 20:37:46 +0200115apr_hash_t *netconf_sessions_list = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200116
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200117static pthread_key_t notif_history_key;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200118
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100119static pthread_key_t err_reply_key;
120
121#define GETSPEC_ERR_REPLY json_object *err_reply = *((json_object **) pthread_getspecific(err_reply_key));
122
123#define CHECK_ERR_SET_REPLY \
124if (reply == NULL) { \
125 GETSPEC_ERR_REPLY \
126 if (err_reply != NULL) { \
127 /* use filled err_reply from libnetconf's callback */ \
128 reply = err_reply; \
129 } \
130}
131
132#define CHECK_ERR_SET_REPLY_ERR(errmsg) \
133if (reply == NULL) { \
134 GETSPEC_ERR_REPLY \
135 if (err_reply == NULL) { \
136 reply = create_error(errmsg); \
137 } else { \
138 /* use filled err_reply from libnetconf's callback */ \
139 reply = err_reply; \
140 } \
141}
142
Radek Krejci469aab82012-07-22 18:42:20 +0200143volatile int isterminated = 0;
144
145static char* password;
146
Radek Krejci469aab82012-07-22 18:42:20 +0200147static void signal_handler(int sign)
148{
149 switch (sign) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100150 case SIGINT:
Radek Krejci469aab82012-07-22 18:42:20 +0200151 case SIGTERM:
152 isterminated = 1;
Radek Krejci469aab82012-07-22 18:42:20 +0200153 break;
154 }
155}
156
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200157static char* gen_ncsession_hash( const char* hostname, const char* port, const char* sid)
Radek Krejci469aab82012-07-22 18:42:20 +0200158{
Radek Krejcif23850c2012-07-23 16:14:17 +0200159 unsigned char hash_raw[APR_SHA1_DIGESTSIZE];
160 int i;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200161 char* hash;
Radek Krejcif23850c2012-07-23 16:14:17 +0200162
Radek Krejci469aab82012-07-22 18:42:20 +0200163 apr_sha1_ctx_t sha1_ctx;
164 apr_sha1_init(&sha1_ctx);
165 apr_sha1_update(&sha1_ctx, hostname, strlen(hostname));
166 apr_sha1_update(&sha1_ctx, port, strlen(port));
167 apr_sha1_update(&sha1_ctx, sid, strlen(sid));
Radek Krejcif23850c2012-07-23 16:14:17 +0200168 apr_sha1_final(hash_raw, &sha1_ctx);
Radek Krejci469aab82012-07-22 18:42:20 +0200169
Radek Krejcif23850c2012-07-23 16:14:17 +0200170 /* convert binary hash into hex string, which is printable */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200171 hash = malloc(sizeof(char) * ((2*APR_SHA1_DIGESTSIZE)+1));
Radek Krejcif23850c2012-07-23 16:14:17 +0200172 for (i = 0; i < APR_SHA1_DIGESTSIZE; i++) {
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200173 snprintf(hash + (2*i), 3, "%02x", hash_raw[i]);
Radek Krejcif23850c2012-07-23 16:14:17 +0200174 }
175 //hash[2*APR_SHA1_DIGESTSIZE] = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200176
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200177 return (hash);
Radek Krejci469aab82012-07-22 18:42:20 +0200178}
179
180int netconf_callback_ssh_hostkey_check (const char* hostname, int keytype, const char* fingerprint)
181{
182 /* always approve */
183 return (EXIT_SUCCESS);
184}
185
186char* netconf_callback_sshauth_password (const char* username, const char* hostname)
187{
188 char* buf;
189
190 buf = malloc ((strlen(password) + 1) * sizeof(char));
191 apr_cpystrn(buf, password, strlen(password) + 1);
192
193 return (buf);
194}
195
196void netconf_callback_sshauth_interactive (const char* name,
197 int name_len,
198 const char* instruction,
199 int instruction_len,
200 int num_prompts,
201 const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts,
202 LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses,
203 void** abstract)
204{
205 int i;
206
207 for (i=0; i<num_prompts; i++) {
208 responses[i].text = malloc ((strlen(password) + 1) * sizeof(char));
209 apr_cpystrn(responses[i].text, password, strlen(password) + 1);
210 responses[i].length = strlen(responses[i].text) + 1;
211 }
212
213 return;
214}
215
Radek Krejcic11fd862012-07-26 12:41:21 +0200216void netconf_callback_error_process(const char* tag,
217 const char* type,
218 const char* severity,
219 const char* apptag,
220 const char* path,
221 const char* message,
222 const char* attribute,
223 const char* element,
224 const char* ns,
225 const char* sid)
226{
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100227 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
228 json_object *err_reply = *err_reply_p;
229
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100230 json_object *array = NULL;
231 if (err_reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100232 DEBUG("error calback: empty error list");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100233 pthread_mutex_lock(&json_lock);
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100234 err_reply = json_object_new_object();
235 array = json_object_new_array();
236 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
237 json_object_object_add(err_reply, "errors", array);
238 if (message != NULL) {
239 json_object_array_add(array, json_object_new_string(message));
240 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100241 pthread_mutex_unlock(&json_lock);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100242 (*err_reply_p) = err_reply;
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100243 } else {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100244 DEBUG("error calback: nonempty error list");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100245 pthread_mutex_lock(&json_lock);
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100246 array = json_object_object_get(err_reply, "errors");
247 if (array != NULL) {
248 if (message != NULL) {
249 json_object_array_add(array, json_object_new_string(message));
250 }
251 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100252 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100253 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100254 pthread_setspecific(err_reply_key, err_reply_p);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100255 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200256}
257
Tomas Cejka47387fd2013-06-10 20:37:46 +0200258/**
259 * should be used in locked area
260 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100261void prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200262{
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200263 json_object *json_obj = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100264 char *old_sid = NULL;
265 const char *j_old_sid = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200266 const char *cpbltstr;
267 struct nc_cpblts* cpblts = NULL;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200268
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200269 if (s == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100270 DEBUG("No session given.");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200271 return;
272 }
273
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100274 pthread_mutex_lock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200275 if (s->hello_message != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100276 DEBUG("clean previous hello message");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200277 //json_object_put(s->hello_message);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100278 j_old_sid = json_object_get_string(json_object_object_get(s->hello_message, "sid"));
279 if (j_old_sid != NULL) {
280 old_sid = strdup(j_old_sid);
281 }
Tomas Cejkadd5cb452014-03-26 15:22:00 +0100282 json_object_put(s->hello_message);
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200283 s->hello_message = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200284 }
Tomas Cejkadd5cb452014-03-26 15:22:00 +0100285 s->hello_message = json_object_get(json_object_new_object());
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200286 if (session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100287 if (old_sid != NULL) {
288 /* use previous sid */
289 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
290 free(old_sid);
Tomas Cejkaa3ffdba2014-03-27 15:12:21 +0100291 old_sid = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100292 } else {
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200293 /* we don't have old sid */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100294 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
295 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200296 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
297 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
298 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
299 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
300 cpblts = nc_session_get_cpblts (session);
301 if (cpblts != NULL) {
302 json_obj = json_object_new_array();
303 nc_cpblts_iter_start (cpblts);
304 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
305 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
306 }
307 json_object_object_add(s->hello_message, "capabilities", json_obj);
308 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100309 DEBUG("%s", json_object_to_json_string(s->hello_message));
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200310 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100311 DEBUG("Session was not given.");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200312 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
313 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
314 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100315 DEBUG("Status info from hello message prepared");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100316 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200317
318}
319
320
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200321/**
322 * \defgroup netconf_operations NETCONF operations
323 * The list of NETCONF operations that mod_netconf supports.
324 * @{
325 */
326
327/**
328 * \brief Connect to NETCONF server
329 *
330 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
331 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100332static char* netconf_connect(apr_pool_t* pool, const char* host, const char* port, const char* user, const char* pass, struct nc_cpblts * cpblts)
Radek Krejci469aab82012-07-22 18:42:20 +0200333{
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200334 struct nc_session* session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200335 struct session_with_mutex * locked_session;
Radek Krejcif23850c2012-07-23 16:14:17 +0200336 char *session_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200337
338 /* connect to the requested NETCONF server */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200339 password = (char*)pass;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100340 DEBUG("prepare to connect %s@%s:%s", user, host, port);
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200341 nc_verbosity(NC_VERB_DEBUG);
David Kupka8e60a372012-09-04 09:15:20 +0200342 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100343 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +0200344
Radek Krejci469aab82012-07-22 18:42:20 +0200345 /* if connected successful, add session to the list */
346 if (session != NULL) {
347 /* generate hash for the session */
Radek Krejcic11fd862012-07-26 12:41:21 +0200348 session_key = gen_ncsession_hash(
349 (host==NULL) ? "localhost" : host,
350 (port==NULL) ? "830" : port,
Radek Krejcia282bed2012-07-27 14:43:45 +0200351 nc_session_get_id(session));
David Kupka8e60a372012-09-04 09:15:20 +0200352
Tomas Cejkaba21b382013-04-13 02:37:32 +0200353 /** \todo allocate from apr_pool */
Tomas Cejka73496bf2014-03-26 15:31:09 +0100354 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 +0200355 nc_session_free(session);
Tomas Cejka73496bf2014-03-26 15:31:09 +0100356 session = NULL;
357 free(locked_session);
358 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100359 DEBUG("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200360 return NULL;
361 }
362 locked_session->session = session;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200363 locked_session->last_activity = apr_time_now();
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200364 locked_session->hello_message = NULL;
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200365 locked_session->closed = 0;
David Kupka8e60a372012-09-04 09:15:20 +0200366 pthread_mutex_init (&locked_session->lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100367 DEBUG("Before session_lock");
David Kupka8e60a372012-09-04 09:15:20 +0200368 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100369 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200370 if (pthread_rwlock_wrlock (&session_lock) != 0) {
371 nc_session_free(session);
372 free (locked_session);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100373 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200374 return NULL;
375 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200376 locked_session->notifications = apr_array_make(pool, NOTIFICATION_QUEUE_SIZE, sizeof(notification_t));
Tomas Cejka654f84e2013-04-19 11:55:01 +0200377 locked_session->ntfc_subscribed = 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100378 DEBUG("Add connection to the list");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200379 apr_hash_set(netconf_sessions_list, apr_pstrdup(pool, session_key), APR_HASH_KEY_STRING, (void *) locked_session);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100380 DEBUG("Before session_unlock");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200381
Tomas Cejka47387fd2013-06-10 20:37:46 +0200382 /* lock session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100383 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200384 pthread_mutex_lock(&locked_session->lock);
385
386 /* unlock session list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100387 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200388 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100389 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +0200390 }
391
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200392 /* store information about session from hello message for future usage */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100393 prepare_status_message(locked_session, session);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200394
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100395 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200396 pthread_mutex_unlock(&locked_session->lock);
397
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100398 DEBUG("NETCONF session established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200399 return (session_key);
Radek Krejci469aab82012-07-22 18:42:20 +0200400 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100401 DEBUG("Connection could not be established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200402 return (NULL);
Radek Krejci469aab82012-07-22 18:42:20 +0200403 }
404
Radek Krejci469aab82012-07-22 18:42:20 +0200405}
406
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100407static int close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +0200408{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100409 DEBUG("lock private lock.");
410 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200411 if (pthread_mutex_lock(&locked_session->lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100412 DEBUG("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200413 }
414 locked_session->ntfc_subscribed = 0;
415 locked_session->closed = 1;
Tomas Cejka73496bf2014-03-26 15:31:09 +0100416 if (locked_session->session != NULL) {
417 nc_session_free(locked_session->session);
418 locked_session->session = NULL;
419 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100420 DEBUG("session closed.");
421 DEBUG("unlock private lock.");
422 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200423 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100424 DEBUG("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200425 }
426
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100427 DEBUG("unlock session lock.");
428 DEBUG("closed session, disabled notif(?), wait 2s");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200429 usleep(500000); /* let notification thread stop */
430
431 /* session shouldn't be used by now */
432 /** \todo free all notifications from queue */
433 apr_array_clear(locked_session->notifications);
434 pthread_mutex_destroy(&locked_session->lock);
435 if (locked_session->hello_message != NULL) {
436 /** \todo free hello_message */
437 //json_object_put(locked_session->hello_message);
438 locked_session->hello_message = NULL;
439 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200440 locked_session->session = NULL;
Tomas Cejkaaecc5f72013-10-01 00:03:50 +0200441 free(locked_session);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200442 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100443 DEBUG("NETCONF session closed, everything cleared.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200444 return (EXIT_SUCCESS);
445}
446
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100447static int netconf_close(const char* session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +0200448{
David Kupka8e60a372012-09-04 09:15:20 +0200449 struct session_with_mutex * locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +0200450
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100451 DEBUG("Key in hash to close: %s", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200452
David Kupka8e60a372012-09-04 09:15:20 +0200453 /* get exclusive (write) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100454 DEBUG("lock session lock.");
455 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200456 if (pthread_rwlock_wrlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100457 DEBUG("Error while locking rwlock");
458 (*reply) = create_error("Internal: Error while locking.");
David Kupka8e60a372012-09-04 09:15:20 +0200459 return EXIT_FAILURE;
460 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200461 /* get session to close */
462 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
463 /* remove session from the active sessions list -> nobody new can now work with session */
464 apr_hash_set(netconf_sessions_list, session_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200465
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100466 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200467 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100468 DEBUG("Error while unlocking rwlock");
469 (*reply) = create_error("Internal: Error while unlocking.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200470 }
471
472 if ((locked_session != NULL) && (locked_session->session != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100473 return close_and_free_session(locked_session);
Radek Krejci469aab82012-07-22 18:42:20 +0200474 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100475 DEBUG("Unknown session to close");
476 (*reply) = create_error("Internal: Unkown session to close.");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200477 return (EXIT_FAILURE);
Radek Krejci469aab82012-07-22 18:42:20 +0200478 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100479 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +0200480}
481
Tomas Cejkac7929632013-10-24 19:25:15 +0200482/**
483 * Test reply message type and return error message.
484 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200485 * \param[in] session nc_session internal struct
486 * \param[in] session_key session key, NULL to disable disconnect on error
487 * \param[in] msgt RPC-REPLY message type
488 * \param[out] data
489 * \return NULL on success
490 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100491json_object *netconf_test_reply(struct nc_session *session, const char *session_key, NC_MSG_TYPE msgt, nc_reply *reply, char **data)
Tomas Cejkac7929632013-10-24 19:25:15 +0200492{
493 NC_REPLY_TYPE replyt;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100494 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200495
496 /* process the result of the operation */
497 switch (msgt) {
498 case NC_MSG_UNKNOWN:
499 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100500 DEBUG("mod_netconf: receiving rpc-reply failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200501 if (session_key != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100502 netconf_close(session_key, &err);
503 }
504 if (err != NULL) {
505 return err;
Tomas Cejkac7929632013-10-24 19:25:15 +0200506 }
507 return create_error("Internal: Receiving RPC-REPLY failed.");
508 }
509 case NC_MSG_NONE:
510 /* there is error handled by callback */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100511 if (data != NULL) {
512 free(*data);
513 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200514 (*data) = NULL;
515 return NULL;
516 case NC_MSG_REPLY:
517 switch (replyt = nc_reply_get_type(reply)) {
518 case NC_REPLY_OK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100519 if ((data != NULL) && (*data != NULL)) {
520 free(*data);
521 (*data) = NULL;
522 }
523 return create_ok();
Tomas Cejkac7929632013-10-24 19:25:15 +0200524 case NC_REPLY_DATA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100525 if (((*data) = nc_reply_get_data(reply)) == NULL) {
526 DEBUG("mod_netconf: no data from reply");
Tomas Cejkac7929632013-10-24 19:25:15 +0200527 return create_error("Internal: No data from reply received.");
528 } else {
529 return NULL;
530 }
531 break;
532 case NC_REPLY_ERROR:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100533 DEBUG("mod_netconf: unexpected rpc-reply (%d)", replyt);
534 if (data != NULL) {
535 free(*data);
536 (*data) = NULL;
537 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200538 return create_error(nc_reply_get_errormsg(reply));
539 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100540 DEBUG("mod_netconf: unexpected rpc-reply (%d)", replyt);
541 if (data != NULL) {
542 free(*data);
543 (*data) = NULL;
544 }
Tomas Cejka60885252014-03-26 15:45:47 +0100545 return create_error("Unknown type of NETCONF reply.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200546 }
547 break;
548 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100549 DEBUG("mod_netconf: unexpected reply message received (%d)", msgt);
550 if (data != NULL) {
551 free(*data);
552 (*data) = NULL;
553 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200554 return create_error("Internal: Unexpected RPC-REPLY message type.");
555 }
556}
557
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100558json_object *netconf_unlocked_op(struct nc_session *session, nc_rpc* rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +0200559{
560 nc_reply* reply = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200561 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200562
563 /* check requests */
564 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100565 DEBUG("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200566 return create_error("Internal error: RPC is not created");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200567 }
568
569 if (session != NULL) {
570 /* send the request and get the reply */
571 msgt = nc_session_send_recv(session, rpc, &reply);
572 /* process the result of the operation */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100573 return netconf_test_reply(session, NULL, msgt, reply, NULL);
Tomas Cejka6b886e02013-07-05 09:53:17 +0200574 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100575 DEBUG("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200576 return create_error("Internal error: Unknown session to process.");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200577 }
578}
579
Tomas Cejkac7929632013-10-24 19:25:15 +0200580/**
581 * Perform RPC method that returns data.
582 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200583 * \param[in] session_key session identifier
584 * \param[in] rpc RPC message to perform
585 * \param[out] received_data received data string, can be NULL when no data expected, value can be set to NULL if no data received
586 * \return NULL on success, json object with error otherwise
587 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100588static json_object *netconf_op(const char* session_key, nc_rpc* rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +0200589{
590 struct nc_session *session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200591 struct session_with_mutex * locked_session;
Tomas Cejka00635972013-06-03 15:10:52 +0200592 nc_reply* reply = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200593 json_object *res = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100594 char *data = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100595 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200596
Radek Krejci8e4632a2012-07-26 13:40:34 +0200597 /* check requests */
598 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100599 DEBUG("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200600 res = create_error("Internal: RPC could not be created.");
601 data = NULL;
602 goto finished;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200603 }
604
David Kupka8e60a372012-09-04 09:15:20 +0200605 /* get non-exclusive (read) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100606 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200607 if (pthread_rwlock_rdlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100608 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200609 res = create_error("Internal: Lock failed.");
610 data = NULL;
611 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200612 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200613 /* get session where send the RPC */
Tomas Cejka47387fd2013-06-10 20:37:46 +0200614 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
David Kupka8e60a372012-09-04 09:15:20 +0200615 if (locked_session != NULL) {
616 session = locked_session->session;
617 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200618 if (session != NULL) {
David Kupka8e60a372012-09-04 09:15:20 +0200619 /* get exclusive access to session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100620 DEBUG("LOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200621 if (pthread_mutex_lock(&locked_session->lock) != 0) {
622 /* unlock before returning error */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100623 DEBUG("UNLOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200624 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200625
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100626 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200627 res = create_error("Internal: Could not unlock.");
628 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200629 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200630 res = create_error("Internal: Could not unlock.");
David Kupka8e60a372012-09-04 09:15:20 +0200631 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100632 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200633 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200634
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100635 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200636 res = create_error("Internal: Could not unlock.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200637 }
638
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200639 locked_session->last_activity = apr_time_now();
Tomas Cejkac7929632013-10-24 19:25:15 +0200640
Radek Krejci035bf4e2012-07-25 10:59:09 +0200641 /* send the request and get the reply */
Radek Krejcia332b692012-11-12 16:15:54 +0100642 msgt = nc_session_send_recv(session, rpc, &reply);
643
David Kupka8e60a372012-09-04 09:15:20 +0200644 /* first release exclusive lock for this session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100645 DEBUG("UNLOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200646 pthread_mutex_unlock(&locked_session->lock);
647 /* end of critical section */
Radek Krejci035bf4e2012-07-25 10:59:09 +0200648
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100649 res = netconf_test_reply(session, session_key, msgt, reply, &data);
Radek Krejci035bf4e2012-07-25 10:59:09 +0200650 } else {
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100651 /* release lock on failure */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100652 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100653 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100654 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100655 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100656 DEBUG("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200657 res = create_error("Unknown session to process.");
658 data = NULL;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200659 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200660finished:
661 nc_reply_free(reply);
662 if (received_data != NULL) {
663 (*received_data) = data;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100664 } else {
665 if (data != NULL) {
666 free(data);
667 data = NULL;
668 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200669 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200670 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200671}
672
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100673static char* netconf_getconfig(const char* session_key, NC_DATASTORE source, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200674{
675 nc_rpc* rpc;
676 struct nc_filter *f = NULL;
677 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200678 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200679
680 /* create filter if set */
681 if (filter != NULL) {
682 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
683 }
684
685 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100686 rpc = nc_rpc_getconfig (source, f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200687 nc_filter_free(f);
688 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100689 DEBUG("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200690 return (NULL);
691 }
692
Tomas Cejka94674662013-09-13 15:55:24 +0200693 /* tell server to show all elements even if they have default values */
Tomas Cejka1c3840d2014-01-29 21:08:23 +0100694 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100695 DEBUG("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200696 }
697
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100698 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200699 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100700 if (res != NULL) {
701 (*err) = res;
702 } else {
703 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200704 }
705
Radek Krejci8e4632a2012-07-26 13:40:34 +0200706 return (data);
707}
708
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100709static 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 +0100710{
711 nc_rpc* rpc;
712 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200713 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100714
715 /* create requests */
Tomas Cejka94da2c52013-01-08 18:20:30 +0100716 rpc = nc_rpc_getschema(identifier, version, format);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100717 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100718 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100719 return (NULL);
720 }
721
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100722 res = netconf_op(session_key, rpc, &data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100723 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100724 if (res != NULL) {
725 (*err) = res;
726 } else {
727 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200728 }
729
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100730 return (data);
731}
732
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100733static char* netconf_get(const char* session_key, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200734{
735 nc_rpc* rpc;
736 struct nc_filter *f = NULL;
737 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200738 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200739
740 /* create filter if set */
741 if (filter != NULL) {
742 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
743 }
744
745 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100746 rpc = nc_rpc_get (f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200747 nc_filter_free(f);
748 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100749 DEBUG("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200750 return (NULL);
751 }
752
Tomas Cejka94674662013-09-13 15:55:24 +0200753 /* tell server to show all elements even if they have default values */
Tomas Cejka1c3840d2014-01-29 21:08:23 +0100754 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100755 DEBUG("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200756 }
757
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100758 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200759 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200760 if (res == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100761 (*err) = res;
762 } else {
763 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200764 }
765
Radek Krejci8e4632a2012-07-26 13:40:34 +0200766 return (data);
767}
768
Tomas Cejkab4d05872014-02-14 22:44:38 +0100769static 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 +0200770{
771 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200772 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200773
774 /* create requests */
Tomas Cejkab4d05872014-02-14 22:44:38 +0100775 if (source == NC_DATASTORE_CONFIG) {
Tomas Cejka5064c232013-01-17 09:30:58 +0100776 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100777 /* config, url */
778 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100779 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100780 /* config, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100781 rpc = nc_rpc_copyconfig(source, target, config);
782 }
Tomas Cejkab4d05872014-02-14 22:44:38 +0100783 } else if (source == NC_DATASTORE_URL) {
784 if (target == NC_DATASTORE_URL) {
785 /* url, url */
786 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
787 } else {
788 /* url, datastore */
789 rpc = nc_rpc_copyconfig(source, target, uri_src);
790 }
Tomas Cejka5064c232013-01-17 09:30:58 +0100791 } else {
792 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100793 /* datastore, url */
794 rpc = nc_rpc_copyconfig(source, target, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100795 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100796 /* datastore, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100797 rpc = nc_rpc_copyconfig(source, target);
798 }
799 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200800 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100801 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200802 return create_error("Internal: Creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200803 }
804
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100805 res = netconf_op(session_key, rpc, NULL);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200806 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200807
808 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200809}
Radek Krejci035bf4e2012-07-25 10:59:09 +0200810
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100811static 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 +0200812{
813 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200814 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200815
816 /* create requests */
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100817 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200818 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100819 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200820 return create_error("Internal: Creating rpc request failed");
Radek Krejci62ab34b2012-07-26 13:42:05 +0200821 }
822
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100823 res = netconf_op(session_key, rpc, NULL);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200824 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200825
826 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200827}
828
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100829static json_object *netconf_killsession(const char* session_key, const char* sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200830{
831 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200832 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200833
834 /* create requests */
835 rpc = nc_rpc_killsession(sid);
836 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100837 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200838 return create_error("Internal: Creating rpc request failed");
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200839 }
840
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100841 res = netconf_op(session_key, rpc, NULL);
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200842 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200843 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200844}
845
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100846static json_object *netconf_onlytargetop(const char* session_key, NC_DATASTORE target, nc_rpc* (*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +0200847{
848 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200849 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +0200850
851 /* create requests */
Radek Krejci5cd7d422012-07-26 14:50:29 +0200852 rpc = op_func(target);
Radek Krejci2f318372012-07-26 14:22:35 +0200853 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100854 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200855 return create_error("Internal: Creating rpc request failed");
Radek Krejci2f318372012-07-26 14:22:35 +0200856 }
857
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100858 res = netconf_op(session_key, rpc, NULL);
Radek Krejci2f318372012-07-26 14:22:35 +0200859 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200860 return res;
Radek Krejci2f318372012-07-26 14:22:35 +0200861}
862
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100863static json_object *netconf_deleteconfig(const char* session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200864{
Tomas Cejka404d37e2013-04-13 02:31:35 +0200865 nc_rpc *rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200866 json_object *res = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +0200867 if (target != NC_DATASTORE_URL) {
868 rpc = nc_rpc_deleteconfig(target);
869 } else {
Tomas Cejkac7929632013-10-24 19:25:15 +0200870 rpc = nc_rpc_deleteconfig(target, url);
871 }
872 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100873 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200874 return create_error("Internal: Creating rpc request failed");
Tomas Cejka404d37e2013-04-13 02:31:35 +0200875 }
876
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100877 res = netconf_op(session_key, rpc, NULL);
Tomas Cejkac7929632013-10-24 19:25:15 +0200878 nc_rpc_free (rpc);
879 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +0200880}
881
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100882static json_object *netconf_lock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200883{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100884 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200885}
886
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100887static json_object *netconf_unlock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200888{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100889 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200890}
891
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100892static json_object *netconf_generic(const char* session_key, const char* content, char** data)
Radek Krejci80c10d92012-07-30 08:38:50 +0200893{
Tomas Cejka00635972013-06-03 15:10:52 +0200894 nc_rpc* rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200895 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +0200896
897 /* create requests */
898 rpc = nc_rpc_generic(content);
899 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100900 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200901 return create_error("Internal: Creating rpc request failed");
Radek Krejci80c10d92012-07-30 08:38:50 +0200902 }
903
Radek Krejcia332b692012-11-12 16:15:54 +0100904 if (data != NULL) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200905 // TODO ?free(*data);
906 (*data) = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100907 }
Radek Krejci80c10d92012-07-30 08:38:50 +0200908
909 /* get session where send the RPC */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100910 res = netconf_op(session_key, rpc, data);
Tomas Cejkac7929632013-10-24 19:25:15 +0200911 nc_rpc_free (rpc);
912 return res;
Radek Krejci80c10d92012-07-30 08:38:50 +0200913}
914
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200915/**
916 * @}
917 *//* netconf_operations */
918
Radek Krejci7338bde2012-08-10 12:57:30 +0200919void clb_print(NC_VERB_LEVEL level, const char* msg)
Radek Krejci469aab82012-07-22 18:42:20 +0200920{
Radek Krejci7338bde2012-08-10 12:57:30 +0200921 switch (level) {
922 case NC_VERB_ERROR:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100923 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200924 break;
925 case NC_VERB_WARNING:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100926 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200927 break;
928 case NC_VERB_VERBOSE:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100929 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200930 break;
931 case NC_VERB_DEBUG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100932 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200933 break;
934 }
Radek Krejci469aab82012-07-22 18:42:20 +0200935}
936
Tomas Cejka64b87482013-06-03 16:30:53 +0200937/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +0200938 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +0200939 * Caller should free message memory.
940 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +0200941 * \return pointer to message
942 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100943char *get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +0200944{
945 /* read json in chunked framing */
946 unsigned int buffer_size = 0;
947 ssize_t buffer_len = 0;
948 char *buffer = NULL;
949 char c;
950 ssize_t ret;
951 int i, chunk_len;
952 char chunk_len_str[12];
953
954 while (1) {
955 /* read chunk length */
956 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
957 if (buffer != NULL) {
958 free (buffer);
959 buffer = NULL;
960 }
961 break;
962 }
963 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
964 if (buffer != NULL) {
965 free (buffer);
966 buffer = NULL;
967 }
968 break;
969 }
970 i=0;
971 memset (chunk_len_str, 0, 12);
972 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
973 if (i==0 && c == '#') {
974 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
975 /* end but invalid */
976 if (buffer != NULL) {
977 free (buffer);
978 buffer = NULL;
979 }
980 }
981 /* end of message, double-loop break */
982 goto msg_complete;
983 }
984 chunk_len_str[i++] = c;
985 if (i==11) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100986 DEBUG("Message is too long, buffer for length is not big enought!!!!");
Tomas Cejka64b87482013-06-03 16:30:53 +0200987 break;
988 }
989 }
990 if (c != '\n') {
991 if (buffer != NULL) {
992 free (buffer);
993 buffer = NULL;
994 }
995 break;
996 }
997 chunk_len_str[i] = 0;
998 if ((chunk_len = atoi (chunk_len_str)) == 0) {
999 if (buffer != NULL) {
1000 free (buffer);
1001 buffer = NULL;
1002 }
1003 break;
1004 }
1005 buffer_size += chunk_len+1;
1006 buffer = realloc (buffer, sizeof(char)*buffer_size);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001007 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
Tomas Cejka64b87482013-06-03 16:30:53 +02001008 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
1009 if (buffer != NULL) {
1010 free (buffer);
1011 buffer = NULL;
1012 }
1013 break;
1014 }
1015 buffer_len += ret;
1016 }
1017msg_complete:
1018 return buffer;
1019}
1020
Tomas Cejkad5b53772013-06-08 23:01:07 +02001021NC_DATASTORE parse_datastore(const char *ds)
1022{
1023 if (strcmp(ds, "running") == 0) {
1024 return NC_DATASTORE_RUNNING;
1025 } else if (strcmp(ds, "startup") == 0) {
1026 return NC_DATASTORE_STARTUP;
1027 } else if (strcmp(ds, "candidate") == 0) {
1028 return NC_DATASTORE_CANDIDATE;
Tomas Cejka4003a702013-10-01 00:02:45 +02001029 } else if (strcmp(ds, "url") == 0) {
1030 return NC_DATASTORE_URL;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001031 } else if (strcmp(ds, "config") == 0) {
1032 return NC_DATASTORE_CONFIG;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001033 }
1034 return -1;
1035}
1036
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001037NC_EDIT_TESTOPT_TYPE parse_testopt(const char *t)
1038{
1039 if (strcmp(t, "notset") == 0) {
1040 return NC_EDIT_TESTOPT_NOTSET;
1041 } else if (strcmp(t, "testset") == 0) {
1042 return NC_EDIT_TESTOPT_TESTSET;
1043 } else if (strcmp(t, "set") == 0) {
1044 return NC_EDIT_TESTOPT_SET;
1045 } else if (strcmp(t, "test") == 0) {
1046 return NC_EDIT_TESTOPT_TEST;
1047 }
1048 return NC_EDIT_TESTOPT_ERROR;
1049}
1050
Tomas Cejkad5b53772013-06-08 23:01:07 +02001051json_object *create_error(const char *errmess)
1052{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001053 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001054 json_object *reply = json_object_new_object();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001055 json_object *array = json_object_new_array();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001056 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001057 json_object_array_add(array, json_object_new_string(errmess));
1058 json_object_object_add(reply, "errors", array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001059 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001060 return reply;
1061
1062}
1063
1064json_object *create_data(const char *data)
1065{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001066 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001067 json_object *reply = json_object_new_object();
1068 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1069 json_object_object_add(reply, "data", json_object_new_string(data));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001070 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001071 return reply;
1072}
1073
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001074json_object *create_ok()
1075{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001076 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001077 json_object *reply = json_object_new_object();
1078 reply = json_object_new_object();
1079 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001080 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001081 return reply;
1082}
1083
1084json_object *handle_op_connect(apr_pool_t *pool, json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001085{
1086 const char *host = NULL;
1087 const char *port = NULL;
1088 const char *user = NULL;
1089 const char *pass = NULL;
1090 json_object *capabilities = NULL;
1091 json_object *reply = NULL;
1092 char *session_key_hash = NULL;
1093 struct nc_cpblts* cpblts = NULL;
1094 unsigned int len, i;
1095
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001096 DEBUG("Request: Connect");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001097 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001098 host = json_object_get_string(json_object_object_get((json_object *) request, "host"));
1099 port = json_object_get_string(json_object_object_get((json_object *) request, "port"));
1100 user = json_object_get_string(json_object_object_get((json_object *) request, "user"));
1101 pass = json_object_get_string(json_object_object_get((json_object *) request, "pass"));
1102
1103 capabilities = json_object_object_get((json_object *) request, "capabilities");
1104 if ((capabilities != NULL) && ((len = json_object_array_length(capabilities)) > 0)) {
1105 cpblts = nc_cpblts_new(NULL);
1106 for (i=0; i<len; i++) {
1107 nc_cpblts_add(cpblts, json_object_get_string(json_object_array_get_idx(capabilities, i)));
1108 }
1109 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001110 DEBUG("no capabilities specified");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001111 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001112 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001113
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001114 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001115 if ((host == NULL) || (user == NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001116 DEBUG("Cannot connect - insufficient input.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001117 session_key_hash = NULL;
1118 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001119 session_key_hash = netconf_connect(pool, host, port, user, pass, cpblts);
1120 DEBUG("hash: %s", session_key_hash);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001121 }
1122 if (cpblts != NULL) {
1123 nc_cpblts_free(cpblts);
1124 }
1125
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001126 GETSPEC_ERR_REPLY
1127
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001128 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001129 if (session_key_hash == NULL) {
1130 /* negative reply */
1131 if (err_reply == NULL) {
1132 reply = json_object_new_object();
1133 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1134 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001135 DEBUG("Connection failed.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001136 } else {
1137 /* use filled err_reply from libnetconf's callback */
1138 reply = err_reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001139 DEBUG("Connect - error from libnetconf's callback.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001140 }
1141 } else {
1142 /* positive reply */
1143 reply = json_object_new_object();
1144 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1145 json_object_object_add(reply, "session", json_object_new_string(session_key_hash));
1146
1147 free(session_key_hash);
1148 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001149 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001150 return reply;
1151}
1152
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001153json_object *handle_op_get(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001154{
1155 const char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001156 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001157 json_object *reply = NULL;
1158
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001159 DEBUG("Request: get (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001160
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001161 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001162 filter = json_object_get_string(json_object_object_get(request, "filter"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001163 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001164
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001165 if ((data = netconf_get(session_key, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001166 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001167 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001168 reply = create_data(data);
1169 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001170 }
1171 return reply;
1172}
1173
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001174json_object *handle_op_getconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001175{
1176 NC_DATASTORE ds_type_s = -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001177 const char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001178 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001179 const char *source = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001180 json_object *reply = NULL;
1181
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001182 DEBUG("Request: get-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001183
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001184 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001185 filter = json_object_get_string(json_object_object_get(request, "filter"));
1186
Tomas Cejkad5b53772013-06-08 23:01:07 +02001187 if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
1188 ds_type_s = parse_datastore(source);
1189 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001190 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001191 if (ds_type_s == -1) {
1192 return create_error("Invalid source repository type requested.");
1193 }
1194
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001195 if ((data = netconf_getconfig(session_key, ds_type_s, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001196 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001197 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001198 reply = create_data(data);
1199 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001200 }
1201 return reply;
1202}
1203
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001204json_object *handle_op_getschema(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001205{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001206 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001207 const char *identifier = NULL;
1208 const char *version = NULL;
1209 const char *format = NULL;
1210 json_object *reply = NULL;
1211
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001212 DEBUG("Request: get-schema (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001213 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001214 identifier = json_object_get_string(json_object_object_get(request, "identifier"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001215 version = json_object_get_string(json_object_object_get(request, "version"));
1216 format = json_object_get_string(json_object_object_get(request, "format"));
1217 pthread_mutex_lock(&json_lock);
1218
Tomas Cejkad5b53772013-06-08 23:01:07 +02001219 if (identifier == NULL) {
1220 return create_error("No identifier for get-schema supplied.");
1221 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001222
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001223 DEBUG("get-schema(version: %s, format: %s)", version, format);
1224 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001225 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001226 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001227 reply = create_data(data);
1228 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001229 }
1230 return reply;
1231}
1232
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001233json_object *handle_op_editconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001234{
1235 NC_DATASTORE ds_type_s = -1;
1236 NC_DATASTORE ds_type_t = -1;
1237 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
1238 NC_EDIT_ERROPT_TYPE erropt_type = 0;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001239 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001240 const char *defop = NULL;
1241 const char *erropt = NULL;
1242 const char *config = NULL;
1243 const char *source = NULL;
1244 const char *target = NULL;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001245 const char *testopt = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001246 json_object *reply = NULL;
1247
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001248 DEBUG("Request: edit-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001249
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001250 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001251 defop = json_object_get_string(json_object_object_get(request, "default-operation"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001252 erropt = json_object_get_string(json_object_object_get(request, "error-option"));
1253 /* get parameters */
1254 if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
1255 ds_type_t = parse_datastore(target);
1256 }
1257 if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
1258 ds_type_s = parse_datastore(source);
1259 } else {
1260 /* source is optional, default value is config */
1261 ds_type_s = NC_DATASTORE_CONFIG;
1262 }
1263 config = json_object_get_string(json_object_object_get(request, "config"));
1264 testopt = json_object_get_string(json_object_object_get(request, "test-option"));
1265 pthread_mutex_unlock(&json_lock);
1266
Tomas Cejkad5b53772013-06-08 23:01:07 +02001267 if (defop != NULL) {
1268 if (strcmp(defop, "merge") == 0) {
1269 defop_type = NC_EDIT_DEFOP_MERGE;
1270 } else if (strcmp(defop, "replace") == 0) {
1271 defop_type = NC_EDIT_DEFOP_REPLACE;
1272 } else if (strcmp(defop, "none") == 0) {
1273 defop_type = NC_EDIT_DEFOP_NONE;
1274 } else {
1275 return create_error("Invalid default-operation parameter.");
1276 }
1277 } else {
1278 defop_type = NC_EDIT_DEFOP_NOTSET;
1279 }
1280
Tomas Cejkad5b53772013-06-08 23:01:07 +02001281 if (erropt != NULL) {
1282 if (strcmp(erropt, "continue-on-error") == 0) {
1283 erropt_type = NC_EDIT_ERROPT_CONT;
1284 } else if (strcmp(erropt, "stop-on-error") == 0) {
1285 erropt_type = NC_EDIT_ERROPT_STOP;
1286 } else if (strcmp(erropt, "rollback-on-error") == 0) {
1287 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
1288 } else {
1289 return create_error("Invalid error-option parameter.");
1290 }
1291 } else {
1292 erropt_type = 0;
1293 }
1294
Tomas Cejkad5b53772013-06-08 23:01:07 +02001295 if (ds_type_t == -1) {
1296 return create_error("Invalid target repository type requested.");
1297 }
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001298 if (ds_type_s == NC_DATASTORE_CONFIG) {
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001299 if (config == NULL) {
1300 return create_error("Invalid config data parameter.");
1301 }
1302 } else if (ds_type_s == NC_DATASTORE_URL){
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001303 if (config == NULL) {
1304 config = "";
1305 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001306 }
1307
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001308 if (testopt != NULL) {
1309 testopt_type = parse_testopt(testopt);
1310 } else {
1311 testopt_type = NC_EDIT_TESTOPT_TESTSET;
1312 }
1313
1314 reply = netconf_editconfig(session_key, ds_type_s, ds_type_t, defop_type, erropt_type, testopt_type, config);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001315
1316 CHECK_ERR_SET_REPLY
1317
Tomas Cejkad5b53772013-06-08 23:01:07 +02001318 return reply;
1319}
1320
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001321json_object *handle_op_copyconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001322{
1323 NC_DATASTORE ds_type_s = -1;
1324 NC_DATASTORE ds_type_t = -1;
1325 const char *config = NULL;
1326 const char *target = NULL;
1327 const char *source = NULL;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001328 const char *uri_src = NULL;
1329 const char *uri_trg = NULL;
1330
Tomas Cejkad5b53772013-06-08 23:01:07 +02001331 json_object *reply = NULL;
1332
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001333 DEBUG("Request: copy-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001334
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001335 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001336 /* get parameters */
1337 if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
1338 ds_type_t = parse_datastore(target);
1339 }
1340 if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
1341 ds_type_s = parse_datastore(source);
1342 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001343 config = json_object_get_string(json_object_object_get(request, "config"));
1344 uri_src = json_object_get_string(json_object_object_get(request, "uri-source"));
1345 uri_trg = json_object_get_string(json_object_object_get(request, "uri-target"));
1346 pthread_mutex_unlock(&json_lock);
1347
Tomas Cejkad5b53772013-06-08 23:01:07 +02001348 if (source == NULL) {
1349 /* no explicit source specified -> use config data */
1350 ds_type_s = NC_DATASTORE_CONFIG;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001351 } else if (ds_type_s == -1) {
1352 /* source datastore specified, but it is invalid */
1353 return create_error("Invalid source repository type requested.");
1354 }
1355
1356 if (ds_type_t == -1) {
1357 /* invalid target datastore specified */
1358 return create_error("Invalid target repository type requested.");
1359 }
1360
Tomas Cejka55c6df52014-02-20 12:59:33 +01001361 /* source can be missing when config is given */
1362 if (source == NULL && config == NULL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001363 reply = create_error("invalid input parameters - source and config is required.");
1364 return reply;
1365 }
1366
1367 if (ds_type_s == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001368 if (uri_src == NULL) {
1369 uri_src = "";
1370 }
1371 }
1372 if (ds_type_t == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001373 if (uri_trg == NULL) {
1374 uri_trg = "";
1375 }
1376 }
1377 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001378
1379 CHECK_ERR_SET_REPLY
1380
Tomas Cejkad5b53772013-06-08 23:01:07 +02001381 return reply;
1382}
1383
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001384json_object *handle_op_generic(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001385{
1386 json_object *reply = NULL;
1387 const char *config = NULL;
1388 char *data = NULL;
1389
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001390 DEBUG("Request: generic request for session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001391
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001392 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001393 config = json_object_get_string(json_object_object_get(request, "content"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001394 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001395
1396 if (config == NULL) {
1397 return create_error("Missing content parameter.");
1398 }
1399
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001400 /* TODO */
1401 reply = netconf_generic(session_key, config, &data);
1402 if (reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001403 GETSPEC_ERR_REPLY
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001404 if (err_reply != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001405 /* use filled err_reply from libnetconf's callback */
1406 reply = err_reply;
1407 }
1408 } else {
1409 if (data == NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001410 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001411 reply = json_object_new_object();
1412 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001413 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001414 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001415 reply = create_data(data);
1416 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001417 }
1418 }
1419 return reply;
1420}
1421
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001422json_object *handle_op_disconnect(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001423{
1424 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001425 DEBUG("Request: Disconnect session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001426
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001427 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001428 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001429 } else {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001430 reply = create_ok();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001431 }
1432 return reply;
1433}
1434
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001435json_object *handle_op_kill(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001436{
1437 json_object *reply = NULL;
1438 const char *sid = NULL;
1439
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001440 DEBUG("Request: kill-session, session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001441
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001442 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001443 sid = json_object_get_string(json_object_object_get(request, "session-id"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001444 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001445
1446 if (sid == NULL) {
1447 return create_error("Missing session-id parameter.");
1448 }
1449
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001450 reply = netconf_killsession(session_key, sid);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001451
1452 CHECK_ERR_SET_REPLY
1453
Tomas Cejkad5b53772013-06-08 23:01:07 +02001454 return reply;
1455}
1456
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001457json_object *handle_op_reloadhello(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001458{
Tomas Cejka47387fd2013-06-10 20:37:46 +02001459 struct nc_session *temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001460 struct session_with_mutex * locked_session = NULL;
1461 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001462 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001463
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001464 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001465 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001466 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001467 return NULL;
1468 }
1469
Tomas Cejkad5b53772013-06-08 23:01:07 +02001470 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1471 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001472 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001473 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001474 DEBUG("creating temporal NC session.");
Tomas Cejka47387fd2013-06-10 20:37:46 +02001475 temp_session = nc_session_connect_channel(locked_session->session, NULL);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001476 if (temp_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001477 prepare_status_message(locked_session, temp_session);
1478 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001479 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001480 temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001481 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001482 DEBUG("Reload hello failed due to channel establishment");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001483 reply = create_error("Reload was unsuccessful, connection failed.");
1484 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001485 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001486 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001487 DEBUG("UNLOCK wrlock %s", __func__);
1488 if (pthread_rwlock_unlock(&session_lock) != 0) {
1489 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1490 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001491 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001492 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001493 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001494 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001495 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001496 reply = create_error("Invalid session identifier.");
1497 }
1498
1499 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
1500 reply = locked_session->hello_message;
1501 }
1502 return reply;
1503}
1504
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001505json_object *handle_op_info(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001506{
1507 json_object *reply = NULL;
Tomas Cejka47387fd2013-06-10 20:37:46 +02001508 struct session_with_mutex * locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001509 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001510
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001511 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001512 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001513 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001514 }
1515
Tomas Cejkad5b53772013-06-08 23:01:07 +02001516 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1517 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001518 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001519 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001520 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001521 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001522 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001523 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001524 if (locked_session->hello_message != NULL) {
1525 reply = locked_session->hello_message;
1526 } else {
1527 reply = create_error("Invalid session identifier.");
1528 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001529 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001530 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001531 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001532 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001533 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001534 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001535 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001536 reply = create_error("Invalid session identifier.");
1537 }
1538
Tomas Cejka47387fd2013-06-10 20:37:46 +02001539
Tomas Cejkad5b53772013-06-08 23:01:07 +02001540 return reply;
1541}
1542
Tomas Cejka6b886e02013-07-05 09:53:17 +02001543void notification_history(time_t eventtime, const char *content)
1544{
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001545 json_object *notif_history_array = (json_object *) pthread_getspecific(notif_history_key);
1546 if (notif_history_array == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001547 DEBUG("No list of notification history found.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001548 return;
1549 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001550 DEBUG("Got notification from history %lu.", (long unsigned) eventtime);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001551 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001552 json_object *notif = json_object_new_object();
1553 if (notif == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001554 DEBUG("Could not allocate memory for notification (json).");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001555 goto failed;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001556 }
1557 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
1558 json_object_object_add(notif, "content", json_object_new_string(content));
1559 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001560failed:
1561 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001562}
1563
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001564json_object *handle_op_ntfgethistory(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001565{
1566 json_object *reply = NULL;
1567 const char *sid = NULL;
1568 struct session_with_mutex *locked_session = NULL;
1569 struct nc_session *temp_session = NULL;
1570 nc_rpc *rpc = NULL;
1571 time_t start = 0;
1572 time_t stop = 0;
1573 int64_t from, to;
1574
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001575 DEBUG("Request: get notification history, session %s", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001576
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001577 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001578 sid = json_object_get_string(json_object_object_get(request, "session"));
1579 from = json_object_get_int64(json_object_object_get(request, "from"));
1580 to = json_object_get_int64(json_object_object_get(request, "to"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001581 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001582
1583 start = time(NULL) + from;
1584 stop = time(NULL) + to;
1585
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001586 DEBUG("notification history interval %li %li", (long int) from, (long int) to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001587
1588 if (sid == NULL) {
1589 return create_error("Missing session parameter.");
1590 }
1591
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001592 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001593 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001594 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001595 return NULL;
1596 }
1597
1598 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1599 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001600 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001601 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001602 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001603 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001604 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001605 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001606 DEBUG("creating temporal NC session.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001607 temp_session = nc_session_connect_channel(locked_session->session, NULL);
1608 if (temp_session != NULL) {
1609 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
1610 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001611 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001612 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001613 DEBUG("notifications: creating an rpc request failed.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001614 return create_error("notifications: creating an rpc request failed.");
1615 }
1616
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001617 DEBUG("Send NC subscribe.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001618 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001619 json_object *res = netconf_unlocked_op(temp_session, rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001620 if (res != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001621 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001622 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001623 DEBUG("Subscription RPC failed.");
Tomas Cejkac7929632013-10-24 19:25:15 +02001624 return res;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001625 }
1626 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
1627
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001628 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001629 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001630 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001631 pthread_mutex_lock(&ntf_history_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001632 pthread_mutex_lock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001633 json_object *notif_history_array = json_object_new_array();
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001634 pthread_mutex_unlock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001635 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001636 DEBUG("notif_history: cannot set thread-specific hash value.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001637 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001638
1639 ncntf_dispatch_receive(temp_session, notification_history);
1640
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001641 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001642 reply = json_object_new_object();
1643 json_object_object_add(reply, "notifications", notif_history_array);
1644 //json_object_put(notif_history_array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001645 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001646
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001647 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001648 pthread_mutex_unlock(&ntf_history_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001649 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001650 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001651 temp_session = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001652 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001653 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001654 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001655 DEBUG("Get history of notification failed due to channel establishment");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001656 reply = create_error("Get history of notification was unsuccessful, connection failed.");
1657 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001658 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001659 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001660 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001661 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001662 }
1663 reply = create_error("Invalid session identifier.");
1664 }
1665
Tomas Cejka4003a702013-10-01 00:02:45 +02001666 return reply;
1667}
1668
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001669json_object *handle_op_validate(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02001670{
1671 json_object *reply = NULL;
1672 const char *sid = NULL;
1673 const char *target = NULL;
1674 const char *url = NULL;
1675 struct session_with_mutex *locked_session = NULL;
1676 nc_rpc *rpc = NULL;
1677 NC_DATASTORE target_ds;
1678
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001679 DEBUG("Request: validate datastore, session %s", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02001680
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001681 pthread_mutex_lock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02001682 sid = json_object_get_string(json_object_object_get(request, "session"));
1683 target = json_object_get_string(json_object_object_get(request, "target"));
1684 url = json_object_get_string(json_object_object_get(request, "url"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001685 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02001686
1687
1688 if ((sid == NULL) || (target == NULL)) {
1689 return create_error("Missing session parameter.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001690 }
Tomas Cejka4003a702013-10-01 00:02:45 +02001691
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001692 /* validation */
1693 target_ds = parse_datastore(target);
1694 if (target_ds == NC_DATASTORE_URL) {
1695 if (url != NULL) {
1696 rpc = nc_rpc_validate(target_ds, url);
Tomas Cejka4003a702013-10-01 00:02:45 +02001697 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001698 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
Tomas Cejka4003a702013-10-01 00:02:45 +02001699 || (target_ds == NC_DATASTORE_CANDIDATE)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001700 rpc = nc_rpc_validate(target_ds);
1701 }
1702 if (rpc == NULL) {
1703 DEBUG("mod_netconf: creating rpc request failed");
1704 reply = create_error("Creation of RPC request failed.");
1705 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka4003a702013-10-01 00:02:45 +02001706 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001707 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02001708 }
1709
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001710 DEBUG("Request: validate datastore");
1711 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001712
1713 CHECK_ERR_SET_REPLY
1714
1715 if (reply == NULL) {
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001716 DEBUG("Request: validation ok.");
1717 reply = create_ok();
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001718 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001719 }
1720 nc_rpc_free (rpc);
1721
Tomas Cejka6b886e02013-07-05 09:53:17 +02001722 return reply;
1723}
1724
David Kupka8e60a372012-09-04 09:15:20 +02001725void * thread_routine (void * arg)
1726{
1727 void * retval = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001728 struct pollfd fds;
Tomas Cejka00635972013-06-03 15:10:52 +02001729 json_object *request = NULL;
1730 json_object *reply = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001731 int operation;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001732 int status = 0;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001733 const char *msgtext;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001734 const char *session_key;
1735 const char *target = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001736 const char *url = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001737 NC_DATASTORE ds_type_t = -1;
Tomas Cejka64b87482013-06-03 16:30:53 +02001738 char *chunked_out_msg = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001739 apr_pool_t * pool = ((struct pass_to_thread*)arg)->pool;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001740 //server_rec * server = ((struct pass_to_thread*)arg)->server;
David Kupka8e60a372012-09-04 09:15:20 +02001741 int client = ((struct pass_to_thread*)arg)->client;
1742
Tomas Cejka00635972013-06-03 15:10:52 +02001743 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001744
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001745 /* init thread specific err_reply memory */
1746 json_object **err_reply = calloc(1, sizeof(json_object **));
1747 if (err_reply == NULL) {
1748 DEBUG("Allocation of err_reply storage failed!");
1749 isterminated = 1;
1750 }
1751 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
1752 DEBUG("notif_history: cannot set thread-specific hash value.");
1753 }
David Kupka8e60a372012-09-04 09:15:20 +02001754 while (!isterminated) {
1755 fds.fd = client;
1756 fds.events = POLLIN;
1757 fds.revents = 0;
1758
1759 status = poll(&fds, 1, 1000);
1760
1761 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
1762 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001763 //DEBUG("poll interrupted");
David Kupka8e60a372012-09-04 09:15:20 +02001764 continue;
1765 } else if (status < 0) {
1766 /* 0: poll time outed
1767 * close socket and ignore this request from the client, it can try it again
1768 * -1: poll failed
1769 * something wrong happend, close this socket and wait for another request
1770 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001771 //DEBUG("poll failed, status %d(%d: %s)", status, errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +02001772 close(client);
1773 break;
1774 }
1775 /* status > 0 */
1776
1777 /* check the status of the socket */
1778
1779 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
1780 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
1781 /* close client's socket (it's probably already closed by client */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001782 //DEBUG("socket error (%d)", fds.revents);
David Kupka8e60a372012-09-04 09:15:20 +02001783 close(client);
1784 break;
1785 }
1786
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001787 DEBUG("Get framed message...");
1788 buffer = get_framed_message(client);
David Kupka8e60a372012-09-04 09:15:20 +02001789
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001790 DEBUG("Check read buffer.");
David Kupka8e60a372012-09-04 09:15:20 +02001791 if (buffer != NULL) {
Tomas Cejka00635972013-06-03 15:10:52 +02001792 enum json_tokener_error jerr;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001793 pthread_mutex_lock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001794 request = json_tokener_parse_verbose(buffer, &jerr);
1795 if (jerr != json_tokener_success) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001796 DEBUG("JSON parsing error");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001797 pthread_mutex_unlock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001798 continue;
1799 }
David Kupka8e60a372012-09-04 09:15:20 +02001800
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001801 operation = json_object_get_int(json_object_object_get(request, "type"));
Tomas Cejka64b87482013-06-03 16:30:53 +02001802 session_key = json_object_get_string(json_object_object_get(request, "session"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001803 pthread_mutex_unlock(&json_lock);
1804
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001805 DEBUG("operation %d session_key %s.", operation, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001806 /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
1807 if (operation != MSG_CONNECT && session_key == NULL) {
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001808 reply = create_error("Missing session specification.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001809 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001810 msgtext = json_object_to_json_string(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001811 pthread_mutex_unlock(&json_lock);
1812
David Kupka8e60a372012-09-04 09:15:20 +02001813 send(client, msgtext, strlen(msgtext) + 1, 0);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001814
1815 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001816 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001817 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001818 /* there is some stupid client, so close the connection to give a chance to some other client */
1819 close(client);
1820 break;
1821 }
1822
David Kupka8e60a372012-09-04 09:15:20 +02001823 /* null global JSON error-reply */
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001824 (*err_reply) = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001825
1826 /* prepare reply envelope */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001827 if (reply != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001828 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001829 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001830 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001831 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001832 reply = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001833
1834 /* process required operation */
1835 switch (operation) {
1836 case MSG_CONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001837 reply = handle_op_connect(pool, request);
David Kupka8e60a372012-09-04 09:15:20 +02001838 break;
1839 case MSG_GET:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001840 reply = handle_op_get(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001841 break;
1842 case MSG_GETCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001843 reply = handle_op_getconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001844 break;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001845 case MSG_GETSCHEMA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001846 reply = handle_op_getschema(pool, request, session_key);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001847 break;
David Kupka8e60a372012-09-04 09:15:20 +02001848 case MSG_EDITCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001849 reply = handle_op_editconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001850 break;
1851 case MSG_COPYCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001852 reply = handle_op_copyconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001853 break;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001854
David Kupka8e60a372012-09-04 09:15:20 +02001855 case MSG_DELETECONFIG:
David Kupka8e60a372012-09-04 09:15:20 +02001856 case MSG_LOCK:
David Kupka8e60a372012-09-04 09:15:20 +02001857 case MSG_UNLOCK:
Tomas Cejkad5b53772013-06-08 23:01:07 +02001858 /* get parameters */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001859 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001860 if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
1861 ds_type_t = parse_datastore(target);
1862 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001863 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001864
1865 if (ds_type_t == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001866 reply = create_error("Invalid target repository type requested.");
David Kupka8e60a372012-09-04 09:15:20 +02001867 break;
1868 }
David Kupka8e60a372012-09-04 09:15:20 +02001869 switch(operation) {
1870 case MSG_DELETECONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001871 DEBUG("Request: delete-config (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001872 pthread_mutex_lock(&json_lock);
Tomas Cejkac7929632013-10-24 19:25:15 +02001873 url = json_object_get_string(json_object_object_get(request, "url"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001874 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001875 reply = netconf_deleteconfig(session_key, ds_type_t, url);
David Kupka8e60a372012-09-04 09:15:20 +02001876 break;
1877 case MSG_LOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001878 DEBUG("Request: lock (session %s)", session_key);
1879 reply = netconf_lock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001880 break;
1881 case MSG_UNLOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001882 DEBUG("Request: unlock (session %s)", session_key);
1883 reply = netconf_unlock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001884 break;
1885 default:
Tomas Cejkac7929632013-10-24 19:25:15 +02001886 reply = create_error("Internal: Unknown request type.");
David Kupka8e60a372012-09-04 09:15:20 +02001887 break;
1888 }
1889
Tomas Cejkac7929632013-10-24 19:25:15 +02001890 if (reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001891 if (*err_reply == NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001892 /** \todo more clever error message wanted */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001893 pthread_mutex_lock(&json_lock);
Tomas Cejkac7929632013-10-24 19:25:15 +02001894 reply = json_object_new_object();
1895 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001896 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001897 } else {
1898 /* use filled err_reply from libnetconf's callback */
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001899 reply = *err_reply;
David Kupka8e60a372012-09-04 09:15:20 +02001900 }
David Kupka8e60a372012-09-04 09:15:20 +02001901 }
1902 break;
1903 case MSG_KILL:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001904 reply = handle_op_kill(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001905 break;
1906 case MSG_DISCONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001907 reply = handle_op_disconnect(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001908 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001909 case MSG_RELOADHELLO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001910 reply = handle_op_reloadhello(pool, request, session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001911 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001912 case MSG_INFO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001913 reply = handle_op_info(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001914 break;
1915 case MSG_GENERIC:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001916 reply = handle_op_generic(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001917 break;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001918 case MSG_NTF_GETHISTORY:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001919 reply = handle_op_ntfgethistory(pool, request, session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001920 break;
Tomas Cejka4003a702013-10-01 00:02:45 +02001921 case MSG_VALIDATE:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001922 reply = handle_op_validate(pool, request, session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02001923 break;
David Kupka8e60a372012-09-04 09:15:20 +02001924 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001925 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001926 reply = create_error("Operation not supported.");
David Kupka8e60a372012-09-04 09:15:20 +02001927 break;
1928 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001929 DEBUG("Clean request json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001930 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001931 json_object_put(request);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001932 DEBUG("Send reply json object.");
David Kupka1e3e4c82012-09-04 09:32:15 +02001933 /* send reply to caller */
1934 if (reply != NULL) {
1935 msgtext = json_object_to_json_string(reply);
Tomas Cejka64b87482013-06-03 16:30:53 +02001936 if (asprintf (&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
Tomas Cejka00635972013-06-03 15:10:52 +02001937 if (buffer != NULL) {
1938 free(buffer);
1939 buffer = NULL;
1940 }
David Kupka8e60a372012-09-04 09:15:20 +02001941 break;
1942 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001943 pthread_mutex_unlock(&json_lock);
1944
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001945 DEBUG("Send framed reply json object.");
Tomas Cejka64b87482013-06-03 16:30:53 +02001946 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001947 DEBUG("Clean reply json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001948 pthread_mutex_lock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02001949 json_object_put(reply);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001950 reply = NULL;
1951 DEBUG("Clean message buffer.");
Tomas Cejka64b87482013-06-03 16:30:53 +02001952 free(chunked_out_msg);
1953 chunked_out_msg = NULL;
Tomas Cejka00635972013-06-03 15:10:52 +02001954 if (buffer != NULL) {
1955 free(buffer);
1956 buffer = NULL;
1957 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001958 if ((err_reply != NULL) && (*err_reply != NULL)) {
1959 json_object_put(*err_reply);
1960 *err_reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001961 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001962 pthread_mutex_unlock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02001963 } else {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001964 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001965 DEBUG("Reply is NULL, shouldn't be...");
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001966 if (*err_reply == NULL) {
1967 DEBUG("No error was set - really strange situation");
1968 } else {
1969 DEBUG("Error was set but it was not sent.");
1970 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001971 continue;
David Kupka8e60a372012-09-04 09:15:20 +02001972 }
1973 }
1974 }
David Kupka8e60a372012-09-04 09:15:20 +02001975 free (arg);
1976
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001977 err_reply = (json_object **) pthread_getspecific(err_reply_key);
1978 if (err_reply != NULL) {
1979 if (*err_reply != NULL) {
1980 pthread_mutex_lock(&json_lock);
1981 json_object_put(*err_reply);
1982 pthread_mutex_unlock(&json_lock);
1983 }
1984 free(err_reply);
1985 err_reply = NULL;
1986 }
1987
David Kupka8e60a372012-09-04 09:15:20 +02001988 return retval;
1989}
1990
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001991/**
1992 * \brief Close all open NETCONF sessions.
1993 *
1994 * During termination of mod_netconf, it is useful to close all remaining
1995 * sessions. This function iterates over the list of sessions and close them
1996 * all.
1997 *
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001998 * \param[in] p apr pool needed for hash table iterating
1999 * \param[in] ht hash table of session_with_mutex structs
2000 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002001static void close_all_nc_sessions(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002002{
2003 apr_hash_index_t *hi;
2004 void *val = NULL;
2005 struct session_with_mutex *swm = NULL;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002006 const char *hashed_key = NULL;
2007 apr_ssize_t hashed_key_length;
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002008 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002009
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002010 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002011 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002012 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002013 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002014 return;
2015 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002016 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002017 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002018 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002019 swm = (struct session_with_mutex *) val;
2020 if (swm != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002021 DEBUG("LOCK mutex %s", __func__);
2022 pthread_mutex_lock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002023 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002024 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002025 pthread_mutex_unlock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002026
2027 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002028 close_and_free_session(swm);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002029 }
2030 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002031 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002032 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002033 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002034 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002035 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002036}
2037
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002038static void check_timeout_and_close(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002039{
2040 apr_hash_index_t *hi;
2041 void *val = NULL;
2042 struct nc_session *ns = NULL;
2043 struct session_with_mutex *swm = NULL;
2044 const char *hashed_key = NULL;
2045 apr_ssize_t hashed_key_length;
2046 apr_time_t current_time = apr_time_now();
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002047 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002048
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002049 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002050//DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002051 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002052 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002053 return;
2054 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002055 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002056 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
2057 swm = (struct session_with_mutex *) val;
2058 if (swm == NULL) {
2059 continue;
2060 }
2061 ns = swm->session;
2062 if (ns == NULL) {
2063 continue;
2064 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002065//DEBUG("LOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002066 pthread_mutex_lock(&swm->lock);
2067 if ((current_time - swm->last_activity) > apr_time_from_sec(ACTIVITY_TIMEOUT)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002068 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002069 /* remove session from the active sessions list */
Tomas Cejka47387fd2013-06-10 20:37:46 +02002070 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002071//DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002072 pthread_mutex_unlock(&swm->lock);
2073
2074 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002075 close_and_free_session(swm);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002076 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002077//DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002078 pthread_mutex_unlock(&swm->lock);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002079 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002080 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002081 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002082//DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002083 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002084 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002085 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002086}
2087
2088
2089/**
Radek Krejcif23850c2012-07-23 16:14:17 +02002090 * This is actually implementation of NETCONF client
2091 * - requests are received from UNIX socket in the predefined format
2092 * - results are replied through the same way
2093 * - the daemon run as a separate process, but it is started and stopped
2094 * automatically by Apache.
2095 *
2096 */
Radek Krejci469aab82012-07-22 18:42:20 +02002097static void forked_proc(apr_pool_t * pool, server_rec * server)
2098{
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002099 struct timeval tv;
Radek Krejci469aab82012-07-22 18:42:20 +02002100 struct sockaddr_un local, remote;
David Kupka8e60a372012-09-04 09:15:20 +02002101 int lsock, client, ret, i, pthread_count = 0;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002102 unsigned int olds = 0, timediff = 0;
David Kupka8e60a372012-09-04 09:15:20 +02002103 socklen_t len;
Radek Krejciae021c12012-07-25 18:03:52 +02002104 mod_netconf_cfg *cfg;
David Kupka8e60a372012-09-04 09:15:20 +02002105 struct pass_to_thread * arg;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002106 pthread_t * ptids = calloc(1, sizeof(pthread_t));
David Kupka8e60a372012-09-04 09:15:20 +02002107 struct timespec maxtime;
2108 pthread_rwlockattr_t lock_attrs;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002109 char *sockname = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +02002110 #ifdef WITH_NOTIFICATIONS
2111 char use_notifications = 0;
2112 #endif
David Kupka8e60a372012-09-04 09:15:20 +02002113
Tomas Cejka6b886e02013-07-05 09:53:17 +02002114 http_server = server;
2115
Tomas Cejka404d37e2013-04-13 02:31:35 +02002116 /* wait at most 5 seconds for every thread to terminate */
David Kupka8e60a372012-09-04 09:15:20 +02002117 maxtime.tv_sec = 5;
2118 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02002119
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002120 #ifndef HTTPD_INDEPENDENT
Radek Krejcif23850c2012-07-23 16:14:17 +02002121 /* change uid and gid of process for security reasons */
Radek Krejci469aab82012-07-22 18:42:20 +02002122 unixd_setup_child();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002123 #endif
Radek Krejci469aab82012-07-22 18:42:20 +02002124
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002125 if (server != NULL) {
2126 cfg = ap_get_module_config(server->module_config, &netconf_module);
2127 if (cfg == NULL) {
2128 DEBUG("Getting mod_netconf configuration failed");
2129 return;
2130 }
2131 sockname = cfg->sockname;
2132 } else {
2133 sockname = SOCKET_FILENAME;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002134 }
Radek Krejci469aab82012-07-22 18:42:20 +02002135
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002136 /* create listening UNIX socket to accept incoming connections */
Radek Krejci469aab82012-07-22 18:42:20 +02002137 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002138 DEBUG("Creating socket failed (%s)", strerror(errno));
2139 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002140 }
2141
2142 local.sun_family = AF_UNIX;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002143 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
Radek Krejci469aab82012-07-22 18:42:20 +02002144 unlink(local.sun_path);
2145 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
2146
2147 if (bind(lsock, (struct sockaddr *) &local, len) == -1) {
2148 if (errno == EADDRINUSE) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002149 DEBUG("mod_netconf socket address already in use");
2150 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002151 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002152 DEBUG("Binding socket failed (%s)", strerror(errno));
2153 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002154 }
2155
2156 if (listen(lsock, MAX_SOCKET_CL) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002157 DEBUG("Setting up listen socket failed (%s)", strerror(errno));
2158 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002159 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002160 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02002161
Tomas Cejkaba21b382013-04-13 02:37:32 +02002162 /* prepare internal lists */
2163 netconf_sessions_list = apr_hash_make(pool);
2164
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002165 #ifdef WITH_NOTIFICATIONS
Tomas Cejka47387fd2013-06-10 20:37:46 +02002166 if (notification_init(pool, server) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002167 DEBUG("libwebsockets initialization failed");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002168 use_notifications = 0;
2169 } else {
2170 use_notifications = 1;
2171 }
2172 #endif
2173
Radek Krejci469aab82012-07-22 18:42:20 +02002174 /* setup libnetconf's callbacks */
2175 nc_verbosity(NC_VERB_DEBUG);
Radek Krejci469aab82012-07-22 18:42:20 +02002176 nc_callback_print(clb_print);
2177 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
2178 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
2179 nc_callback_sshauth_password(netconf_callback_sshauth_password);
Radek Krejcic11fd862012-07-26 12:41:21 +02002180 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02002181
2182 /* disable publickey authentication */
2183 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
2184
David Kupka8e60a372012-09-04 09:15:20 +02002185 /* create mutex protecting session list */
2186 pthread_rwlockattr_init(&lock_attrs);
2187 /* rwlock is shared only with threads in this process */
2188 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
2189 /* create rw lock */
2190 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002191 DEBUG("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
2192 goto error_exit;
David Kupka8e60a372012-09-04 09:15:20 +02002193 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002194 pthread_mutex_init(&ntf_history_lock, NULL);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002195 pthread_mutex_init(&json_lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002196 DEBUG("init of notif_history_key.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002197 if (pthread_key_create(&notif_history_key, NULL) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002198 DEBUG("init of notif_history_key failed");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002199 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002200 DEBUG("init of err_reply_key.");
2201 if (pthread_key_create(&err_reply_key, NULL) != 0) {
2202 DEBUG("init of err_reply_key failed");
2203 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02002204
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002205 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002206 while (isterminated == 0) {
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002207 gettimeofday(&tv, NULL);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002208 timediff = (unsigned int)tv.tv_sec - olds;
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002209 #ifdef WITH_NOTIFICATIONS
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002210 if (timediff > 60) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002211 DEBUG("handling notifications");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002212 }
2213 if (use_notifications == 1) {
2214 notification_handle();
2215 }
2216 #endif
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002217 if (timediff > ACTIVITY_CHECK_INTERVAL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002218 check_timeout_and_close(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002219 }
Radek Krejci469aab82012-07-22 18:42:20 +02002220
2221 /* open incoming connection if any */
David Kupka8e60a372012-09-04 09:15:20 +02002222 len = sizeof(remote);
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002223 if (((unsigned int)tv.tv_sec - olds) > 60) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002224 DEBUG("accepting another client");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002225 olds = tv.tv_sec;
2226 }
David Kupka8e60a372012-09-04 09:15:20 +02002227 client = accept(lsock, (struct sockaddr *) &remote, &len);
Radek Krejci469aab82012-07-22 18:42:20 +02002228 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
2229 apr_sleep(SLEEP_TIME);
2230 continue;
2231 } else if (client == -1 && (errno == EINTR)) {
2232 continue;
2233 } else if (client == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002234 DEBUG("Accepting mod_netconf client connection failed (%s)", strerror(errno));
Radek Krejci469aab82012-07-22 18:42:20 +02002235 continue;
2236 }
Radek Krejci469aab82012-07-22 18:42:20 +02002237
2238 /* set client's socket as non-blocking */
Radek Krejcif23850c2012-07-23 16:14:17 +02002239 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002240
David Kupka8e60a372012-09-04 09:15:20 +02002241 arg = malloc (sizeof(struct pass_to_thread));
2242 arg->client = client;
2243 arg->pool = pool;
2244 arg->server = server;
2245 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02002246
David Kupka8e60a372012-09-04 09:15:20 +02002247 /* start new thread. It will serve this particular request and then terminate */
2248 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void*)arg)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002249 DEBUG("Creating POSIX thread failed: %d\n", ret);
David Kupka8e60a372012-09-04 09:15:20 +02002250 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002251 DEBUG("Thread %lu created", ptids[pthread_count]);
David Kupka8e60a372012-09-04 09:15:20 +02002252 pthread_count++;
2253 ptids = realloc (ptids, sizeof(pthread_t)*(pthread_count+1));
2254 ptids[pthread_count] = 0;
2255 }
Radek Krejci469aab82012-07-22 18:42:20 +02002256
David Kupka8e60a372012-09-04 09:15:20 +02002257 /* check if some thread already terminated, free some resources by joining it */
2258 for (i=0; i<pthread_count; i++) {
2259 if (pthread_tryjoin_np (ptids[i], (void**)&arg) == 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002260 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
David Kupka8e60a372012-09-04 09:15:20 +02002261 pthread_count--;
2262 if (pthread_count > 0) {
2263 /* place last Thread ID on the place of joined one */
2264 ptids[i] = ptids[pthread_count];
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002265 }
Radek Krejci469aab82012-07-22 18:42:20 +02002266 }
2267 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002268 DEBUG("Running %d threads", pthread_count);
Radek Krejci469aab82012-07-22 18:42:20 +02002269 }
2270
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002271 DEBUG("mod_netconf terminating...");
David Kupka8e60a372012-09-04 09:15:20 +02002272 /* join all threads */
2273 for (i=0; i<pthread_count; i++) {
2274 pthread_timedjoin_np (ptids[i], (void**)&arg, &maxtime);
2275 }
Radek Krejci469aab82012-07-22 18:42:20 +02002276
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002277 #ifdef WITH_NOTIFICATIONS
2278 notification_close();
2279 #endif
2280
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002281 /* close all NETCONF sessions */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002282 close_all_nc_sessions(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002283
David Kupka8e60a372012-09-04 09:15:20 +02002284 /* destroy rwlock */
2285 pthread_rwlock_destroy(&session_lock);
2286 pthread_rwlockattr_destroy(&lock_attrs);
2287
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002288 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02002289
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002290 free(ptids);
2291 close(lsock);
Radek Krejci469aab82012-07-22 18:42:20 +02002292 exit(APR_SUCCESS);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002293 return;
2294error_exit:
2295 close(lsock);
2296 free(ptids);
2297 return;
Radek Krejci469aab82012-07-22 18:42:20 +02002298}
2299
Radek Krejcif23850c2012-07-23 16:14:17 +02002300static void *mod_netconf_create_conf(apr_pool_t * pool, server_rec * s)
Radek Krejci469aab82012-07-22 18:42:20 +02002301{
Radek Krejcif23850c2012-07-23 16:14:17 +02002302 mod_netconf_cfg *config = apr_pcalloc(pool, sizeof(mod_netconf_cfg));
2303 apr_pool_create(&config->pool, pool);
2304 config->forkproc = NULL;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002305 config->sockname = SOCKET_FILENAME;
Radek Krejcif23850c2012-07-23 16:14:17 +02002306
2307 return (void *)config;
Radek Krejci469aab82012-07-22 18:42:20 +02002308}
2309
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002310#ifndef HTTPD_INDEPENDENT
Radek Krejci469aab82012-07-22 18:42:20 +02002311static int mod_netconf_master_init(apr_pool_t * pconf, apr_pool_t * ptemp,
2312 apr_pool_t * plog, server_rec * s)
2313{
Radek Krejcif23850c2012-07-23 16:14:17 +02002314 mod_netconf_cfg *config;
2315 apr_status_t res;
2316
Radek Krejci469aab82012-07-22 18:42:20 +02002317 /* These two help ensure that we only init once. */
Radek Krejcia332b692012-11-12 16:15:54 +01002318 void *data = NULL;
Radek Krejcif23850c2012-07-23 16:14:17 +02002319 const char *userdata_key = "netconf_ipc_init";
Radek Krejci469aab82012-07-22 18:42:20 +02002320
2321 /*
2322 * The following checks if this routine has been called before.
2323 * This is necessary because the parent process gets initialized
2324 * a couple of times as the server starts up.
2325 */
2326 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2327 if (!data) {
2328 apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
2329 return (OK);
2330 }
2331
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002332 DEBUG("creating mod_netconf daemon");
Radek Krejcif23850c2012-07-23 16:14:17 +02002333 config = ap_get_module_config(s->module_config, &netconf_module);
Radek Krejcidfaa6ea2012-07-23 09:04:43 +02002334
Radek Krejcif23850c2012-07-23 16:14:17 +02002335 if (config && config->forkproc == NULL) {
2336 config->forkproc = apr_pcalloc(config->pool, sizeof(apr_proc_t));
2337 res = apr_proc_fork(config->forkproc, config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002338 switch (res) {
2339 case APR_INCHILD:
2340 /* set signal handler */
Radek Krejcif23850c2012-07-23 16:14:17 +02002341 apr_signal_init(config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002342 apr_signal(SIGTERM, signal_handler);
2343
2344 /* log start of the separated NETCONF communication process */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002345 DEBUG("mod_netconf daemon started (PID %d)", getpid());
Radek Krejci469aab82012-07-22 18:42:20 +02002346
2347 /* start main loop providing NETCONF communication */
Radek Krejcif23850c2012-07-23 16:14:17 +02002348 forked_proc(config->pool, s);
Radek Krejci469aab82012-07-22 18:42:20 +02002349
Radek Krejcif23850c2012-07-23 16:14:17 +02002350 /* I never should be here, wtf?!? */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002351 DEBUG("mod_netconf daemon unexpectedly stopped");
Radek Krejci469aab82012-07-22 18:42:20 +02002352 exit(APR_EGENERAL);
2353 break;
2354 case APR_INPARENT:
2355 /* register child to be killed (SIGTERM) when the module config's pool dies */
Radek Krejcif23850c2012-07-23 16:14:17 +02002356 apr_pool_note_subprocess(config->pool, config->forkproc, APR_KILL_AFTER_TIMEOUT);
Radek Krejci469aab82012-07-22 18:42:20 +02002357 break;
2358 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002359 DEBUG("apr_proc_fork() failed");
Radek Krejci469aab82012-07-22 18:42:20 +02002360 break;
2361 }
Radek Krejcif23850c2012-07-23 16:14:17 +02002362 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002363 DEBUG("mod_netconf misses configuration structure");
Radek Krejci469aab82012-07-22 18:42:20 +02002364 }
2365
2366 return OK;
2367}
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002368#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002369
Radek Krejci469aab82012-07-22 18:42:20 +02002370/**
2371 * Register module hooks
2372 */
2373static void mod_netconf_register_hooks(apr_pool_t * p)
2374{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002375#ifndef HTTPD_INDEPENDENT
Radek Krejcif23850c2012-07-23 16:14:17 +02002376 ap_hook_post_config(mod_netconf_master_init, NULL, NULL, APR_HOOK_LAST);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002377#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002378}
2379
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002380static const char* cfg_set_socket_path(cmd_parms* cmd, void* cfg, const char* arg)
2381{
2382 ((mod_netconf_cfg*)cfg)->sockname = apr_pstrdup(cmd->pool, arg);
2383 return NULL;
2384}
2385
2386static const command_rec netconf_cmds[] = {
2387 AP_INIT_TAKE1("NetconfSocket", cfg_set_socket_path, NULL, OR_ALL, "UNIX socket path for mod_netconf communication."),
2388 {NULL}
2389};
2390
Radek Krejci469aab82012-07-22 18:42:20 +02002391/* Dispatch list for API hooks */
2392module AP_MODULE_DECLARE_DATA netconf_module = {
2393 STANDARD20_MODULE_STUFF,
2394 NULL, /* create per-dir config structures */
2395 NULL, /* merge per-dir config structures */
Radek Krejcif23850c2012-07-23 16:14:17 +02002396 mod_netconf_create_conf, /* create per-server config structures */
Radek Krejci469aab82012-07-22 18:42:20 +02002397 NULL, /* merge per-server config structures */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002398 netconf_cmds, /* table of config file commands */
Radek Krejci469aab82012-07-22 18:42:20 +02002399 mod_netconf_register_hooks /* register hooks */
2400};
Radek Krejcia332b692012-11-12 16:15:54 +01002401
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002402int main(int argc, char **argv)
2403{
2404 apr_pool_t *pool;
2405 apr_app_initialize(&argc, (char const *const **) &argv, NULL);
2406 apr_signal(SIGTERM, signal_handler);
2407 apr_signal(SIGINT, signal_handler);
2408 apr_pool_create(&pool, NULL);
2409 forked_proc(pool, NULL);
2410 apr_pool_destroy(pool);
2411 apr_terminate();
2412 DEBUG("Terminated");
2413 return 0;
2414}