blob: 1cc502bc537c63d4044ed0bbc96cfda0437f307d [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);
291 } else {
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200292 /* we don't have old sid */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100293 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
294 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200295 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
296 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
297 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
298 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
299 cpblts = nc_session_get_cpblts (session);
300 if (cpblts != NULL) {
301 json_obj = json_object_new_array();
302 nc_cpblts_iter_start (cpblts);
303 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
304 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
305 }
306 json_object_object_add(s->hello_message, "capabilities", json_obj);
307 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100308 DEBUG("%s", json_object_to_json_string(s->hello_message));
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200309 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100310 DEBUG("Session was not given.");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200311 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
312 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
313 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100314 DEBUG("Status info from hello message prepared");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100315 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200316
317}
318
319
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200320/**
321 * \defgroup netconf_operations NETCONF operations
322 * The list of NETCONF operations that mod_netconf supports.
323 * @{
324 */
325
326/**
327 * \brief Connect to NETCONF server
328 *
329 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
330 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100331static 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 +0200332{
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200333 struct nc_session* session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200334 struct session_with_mutex * locked_session;
Radek Krejcif23850c2012-07-23 16:14:17 +0200335 char *session_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200336
337 /* connect to the requested NETCONF server */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200338 password = (char*)pass;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100339 DEBUG("prepare to connect %s@%s:%s", user, host, port);
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200340 nc_verbosity(NC_VERB_DEBUG);
David Kupka8e60a372012-09-04 09:15:20 +0200341 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100342 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +0200343
Radek Krejci469aab82012-07-22 18:42:20 +0200344 /* if connected successful, add session to the list */
345 if (session != NULL) {
346 /* generate hash for the session */
Radek Krejcic11fd862012-07-26 12:41:21 +0200347 session_key = gen_ncsession_hash(
348 (host==NULL) ? "localhost" : host,
349 (port==NULL) ? "830" : port,
Radek Krejcia282bed2012-07-27 14:43:45 +0200350 nc_session_get_id(session));
David Kupka8e60a372012-09-04 09:15:20 +0200351
Tomas Cejkaba21b382013-04-13 02:37:32 +0200352 /** \todo allocate from apr_pool */
Tomas Cejka73496bf2014-03-26 15:31:09 +0100353 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 +0200354 nc_session_free(session);
Tomas Cejka73496bf2014-03-26 15:31:09 +0100355 session = NULL;
356 free(locked_session);
357 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100358 DEBUG("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200359 return NULL;
360 }
361 locked_session->session = session;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200362 locked_session->last_activity = apr_time_now();
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200363 locked_session->hello_message = NULL;
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200364 locked_session->closed = 0;
David Kupka8e60a372012-09-04 09:15:20 +0200365 pthread_mutex_init (&locked_session->lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100366 DEBUG("Before session_lock");
David Kupka8e60a372012-09-04 09:15:20 +0200367 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100368 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200369 if (pthread_rwlock_wrlock (&session_lock) != 0) {
370 nc_session_free(session);
371 free (locked_session);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100372 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200373 return NULL;
374 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200375 locked_session->notifications = apr_array_make(pool, NOTIFICATION_QUEUE_SIZE, sizeof(notification_t));
Tomas Cejka654f84e2013-04-19 11:55:01 +0200376 locked_session->ntfc_subscribed = 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100377 DEBUG("Add connection to the list");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200378 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 +0100379 DEBUG("Before session_unlock");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200380
Tomas Cejka47387fd2013-06-10 20:37:46 +0200381 /* lock session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100382 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200383 pthread_mutex_lock(&locked_session->lock);
384
385 /* unlock session list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100386 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200387 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100388 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +0200389 }
390
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200391 /* store information about session from hello message for future usage */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100392 prepare_status_message(locked_session, session);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200393
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100394 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200395 pthread_mutex_unlock(&locked_session->lock);
396
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100397 DEBUG("NETCONF session established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200398 return (session_key);
Radek Krejci469aab82012-07-22 18:42:20 +0200399 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100400 DEBUG("Connection could not be established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200401 return (NULL);
Radek Krejci469aab82012-07-22 18:42:20 +0200402 }
403
Radek Krejci469aab82012-07-22 18:42:20 +0200404}
405
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100406static int close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +0200407{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100408 DEBUG("lock private lock.");
409 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200410 if (pthread_mutex_lock(&locked_session->lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100411 DEBUG("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200412 }
413 locked_session->ntfc_subscribed = 0;
414 locked_session->closed = 1;
Tomas Cejka73496bf2014-03-26 15:31:09 +0100415 if (locked_session->session != NULL) {
416 nc_session_free(locked_session->session);
417 locked_session->session = NULL;
418 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100419 DEBUG("session closed.");
420 DEBUG("unlock private lock.");
421 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200422 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100423 DEBUG("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200424 }
425
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100426 DEBUG("unlock session lock.");
427 DEBUG("closed session, disabled notif(?), wait 2s");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200428 usleep(500000); /* let notification thread stop */
429
430 /* session shouldn't be used by now */
431 /** \todo free all notifications from queue */
432 apr_array_clear(locked_session->notifications);
433 pthread_mutex_destroy(&locked_session->lock);
434 if (locked_session->hello_message != NULL) {
435 /** \todo free hello_message */
436 //json_object_put(locked_session->hello_message);
437 locked_session->hello_message = NULL;
438 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200439 locked_session->session = NULL;
Tomas Cejkaaecc5f72013-10-01 00:03:50 +0200440 free(locked_session);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200441 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100442 DEBUG("NETCONF session closed, everything cleared.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200443 return (EXIT_SUCCESS);
444}
445
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100446static int netconf_close(const char* session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +0200447{
David Kupka8e60a372012-09-04 09:15:20 +0200448 struct session_with_mutex * locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +0200449
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100450 DEBUG("Key in hash to close: %s", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200451
David Kupka8e60a372012-09-04 09:15:20 +0200452 /* get exclusive (write) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100453 DEBUG("lock session lock.");
454 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200455 if (pthread_rwlock_wrlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100456 DEBUG("Error while locking rwlock");
457 (*reply) = create_error("Internal: Error while locking.");
David Kupka8e60a372012-09-04 09:15:20 +0200458 return EXIT_FAILURE;
459 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200460 /* get session to close */
461 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
462 /* remove session from the active sessions list -> nobody new can now work with session */
463 apr_hash_set(netconf_sessions_list, session_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200464
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100465 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200466 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100467 DEBUG("Error while unlocking rwlock");
468 (*reply) = create_error("Internal: Error while unlocking.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200469 }
470
471 if ((locked_session != NULL) && (locked_session->session != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100472 return close_and_free_session(locked_session);
Radek Krejci469aab82012-07-22 18:42:20 +0200473 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100474 DEBUG("Unknown session to close");
475 (*reply) = create_error("Internal: Unkown session to close.");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200476 return (EXIT_FAILURE);
Radek Krejci469aab82012-07-22 18:42:20 +0200477 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100478 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +0200479}
480
Tomas Cejkac7929632013-10-24 19:25:15 +0200481/**
482 * Test reply message type and return error message.
483 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200484 * \param[in] session nc_session internal struct
485 * \param[in] session_key session key, NULL to disable disconnect on error
486 * \param[in] msgt RPC-REPLY message type
487 * \param[out] data
488 * \return NULL on success
489 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100490json_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 +0200491{
492 NC_REPLY_TYPE replyt;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100493 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200494
495 /* process the result of the operation */
496 switch (msgt) {
497 case NC_MSG_UNKNOWN:
498 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100499 DEBUG("mod_netconf: receiving rpc-reply failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200500 if (session_key != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100501 netconf_close(session_key, &err);
502 }
503 if (err != NULL) {
504 return err;
Tomas Cejkac7929632013-10-24 19:25:15 +0200505 }
506 return create_error("Internal: Receiving RPC-REPLY failed.");
507 }
508 case NC_MSG_NONE:
509 /* there is error handled by callback */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100510 if (data != NULL) {
511 free(*data);
512 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200513 (*data) = NULL;
514 return NULL;
515 case NC_MSG_REPLY:
516 switch (replyt = nc_reply_get_type(reply)) {
517 case NC_REPLY_OK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100518 if ((data != NULL) && (*data != NULL)) {
519 free(*data);
520 (*data) = NULL;
521 }
522 return create_ok();
Tomas Cejkac7929632013-10-24 19:25:15 +0200523 case NC_REPLY_DATA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100524 if (((*data) = nc_reply_get_data(reply)) == NULL) {
525 DEBUG("mod_netconf: no data from reply");
Tomas Cejkac7929632013-10-24 19:25:15 +0200526 return create_error("Internal: No data from reply received.");
527 } else {
528 return NULL;
529 }
530 break;
531 case NC_REPLY_ERROR:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100532 DEBUG("mod_netconf: unexpected rpc-reply (%d)", replyt);
533 if (data != NULL) {
534 free(*data);
535 (*data) = NULL;
536 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200537 return create_error(nc_reply_get_errormsg(reply));
538 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100539 DEBUG("mod_netconf: unexpected rpc-reply (%d)", replyt);
540 if (data != NULL) {
541 free(*data);
542 (*data) = NULL;
543 }
Tomas Cejka60885252014-03-26 15:45:47 +0100544 return create_error("Unknown type of NETCONF reply.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200545 }
546 break;
547 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100548 DEBUG("mod_netconf: unexpected reply message received (%d)", msgt);
549 if (data != NULL) {
550 free(*data);
551 (*data) = NULL;
552 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200553 return create_error("Internal: Unexpected RPC-REPLY message type.");
554 }
555}
556
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100557json_object *netconf_unlocked_op(struct nc_session *session, nc_rpc* rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +0200558{
559 nc_reply* reply = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200560 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200561
562 /* check requests */
563 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100564 DEBUG("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200565 return create_error("Internal error: RPC is not created");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200566 }
567
568 if (session != NULL) {
569 /* send the request and get the reply */
570 msgt = nc_session_send_recv(session, rpc, &reply);
571 /* process the result of the operation */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100572 return netconf_test_reply(session, NULL, msgt, reply, NULL);
Tomas Cejka6b886e02013-07-05 09:53:17 +0200573 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100574 DEBUG("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200575 return create_error("Internal error: Unknown session to process.");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200576 }
577}
578
Tomas Cejkac7929632013-10-24 19:25:15 +0200579/**
580 * Perform RPC method that returns data.
581 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200582 * \param[in] session_key session identifier
583 * \param[in] rpc RPC message to perform
584 * \param[out] received_data received data string, can be NULL when no data expected, value can be set to NULL if no data received
585 * \return NULL on success, json object with error otherwise
586 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100587static json_object *netconf_op(const char* session_key, nc_rpc* rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +0200588{
589 struct nc_session *session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200590 struct session_with_mutex * locked_session;
Tomas Cejka00635972013-06-03 15:10:52 +0200591 nc_reply* reply = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200592 json_object *res = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100593 char *data = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100594 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200595
Radek Krejci8e4632a2012-07-26 13:40:34 +0200596 /* check requests */
597 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100598 DEBUG("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200599 res = create_error("Internal: RPC could not be created.");
600 data = NULL;
601 goto finished;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200602 }
603
David Kupka8e60a372012-09-04 09:15:20 +0200604 /* get non-exclusive (read) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100605 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200606 if (pthread_rwlock_rdlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100607 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200608 res = create_error("Internal: Lock failed.");
609 data = NULL;
610 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200611 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200612 /* get session where send the RPC */
Tomas Cejka47387fd2013-06-10 20:37:46 +0200613 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 +0200614 if (locked_session != NULL) {
615 session = locked_session->session;
616 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200617 if (session != NULL) {
David Kupka8e60a372012-09-04 09:15:20 +0200618 /* get exclusive access to session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100619 DEBUG("LOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200620 if (pthread_mutex_lock(&locked_session->lock) != 0) {
621 /* unlock before returning error */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100622 DEBUG("UNLOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200623 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200624
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100625 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200626 res = create_error("Internal: Could not unlock.");
627 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200628 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200629 res = create_error("Internal: Could not unlock.");
David Kupka8e60a372012-09-04 09:15:20 +0200630 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100631 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200632 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200633
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100634 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200635 res = create_error("Internal: Could not unlock.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200636 }
637
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200638 locked_session->last_activity = apr_time_now();
Tomas Cejkac7929632013-10-24 19:25:15 +0200639
Radek Krejci035bf4e2012-07-25 10:59:09 +0200640 /* send the request and get the reply */
Radek Krejcia332b692012-11-12 16:15:54 +0100641 msgt = nc_session_send_recv(session, rpc, &reply);
642
David Kupka8e60a372012-09-04 09:15:20 +0200643 /* first release exclusive lock for this session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100644 DEBUG("UNLOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200645 pthread_mutex_unlock(&locked_session->lock);
646 /* end of critical section */
Radek Krejci035bf4e2012-07-25 10:59:09 +0200647
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100648 res = netconf_test_reply(session, session_key, msgt, reply, &data);
Radek Krejci035bf4e2012-07-25 10:59:09 +0200649 } else {
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100650 /* release lock on failure */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100651 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100652 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100653 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100654 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100655 DEBUG("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200656 res = create_error("Unknown session to process.");
657 data = NULL;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200658 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200659finished:
660 nc_reply_free(reply);
661 if (received_data != NULL) {
662 (*received_data) = data;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100663 } else {
664 if (data != NULL) {
665 free(data);
666 data = NULL;
667 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200668 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200669 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200670}
671
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100672static char* netconf_getconfig(const char* session_key, NC_DATASTORE source, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200673{
674 nc_rpc* rpc;
675 struct nc_filter *f = NULL;
676 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200677 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200678
679 /* create filter if set */
680 if (filter != NULL) {
681 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
682 }
683
684 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100685 rpc = nc_rpc_getconfig (source, f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200686 nc_filter_free(f);
687 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100688 DEBUG("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200689 return (NULL);
690 }
691
Tomas Cejka94674662013-09-13 15:55:24 +0200692 /* tell server to show all elements even if they have default values */
Tomas Cejka1c3840d2014-01-29 21:08:23 +0100693 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100694 DEBUG("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200695 }
696
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100697 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200698 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100699 if (res != NULL) {
700 (*err) = res;
701 } else {
702 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200703 }
704
Radek Krejci8e4632a2012-07-26 13:40:34 +0200705 return (data);
706}
707
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100708static 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 +0100709{
710 nc_rpc* rpc;
711 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200712 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100713
714 /* create requests */
Tomas Cejka94da2c52013-01-08 18:20:30 +0100715 rpc = nc_rpc_getschema(identifier, version, format);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100716 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100717 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100718 return (NULL);
719 }
720
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100721 res = netconf_op(session_key, rpc, &data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100722 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100723 if (res != NULL) {
724 (*err) = res;
725 } else {
726 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200727 }
728
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100729 return (data);
730}
731
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100732static char* netconf_get(const char* session_key, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200733{
734 nc_rpc* rpc;
735 struct nc_filter *f = NULL;
736 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200737 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200738
739 /* create filter if set */
740 if (filter != NULL) {
741 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
742 }
743
744 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100745 rpc = nc_rpc_get (f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200746 nc_filter_free(f);
747 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100748 DEBUG("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200749 return (NULL);
750 }
751
Tomas Cejka94674662013-09-13 15:55:24 +0200752 /* tell server to show all elements even if they have default values */
Tomas Cejka1c3840d2014-01-29 21:08:23 +0100753 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100754 DEBUG("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200755 }
756
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100757 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200758 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200759 if (res == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100760 (*err) = res;
761 } else {
762 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200763 }
764
Radek Krejci8e4632a2012-07-26 13:40:34 +0200765 return (data);
766}
767
Tomas Cejkab4d05872014-02-14 22:44:38 +0100768static 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 +0200769{
770 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200771 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200772
773 /* create requests */
Tomas Cejkab4d05872014-02-14 22:44:38 +0100774 if (source == NC_DATASTORE_CONFIG) {
Tomas Cejka5064c232013-01-17 09:30:58 +0100775 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100776 /* config, url */
777 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100778 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100779 /* config, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100780 rpc = nc_rpc_copyconfig(source, target, config);
781 }
Tomas Cejkab4d05872014-02-14 22:44:38 +0100782 } else if (source == NC_DATASTORE_URL) {
783 if (target == NC_DATASTORE_URL) {
784 /* url, url */
785 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
786 } else {
787 /* url, datastore */
788 rpc = nc_rpc_copyconfig(source, target, uri_src);
789 }
Tomas Cejka5064c232013-01-17 09:30:58 +0100790 } else {
791 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100792 /* datastore, url */
793 rpc = nc_rpc_copyconfig(source, target, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100794 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100795 /* datastore, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100796 rpc = nc_rpc_copyconfig(source, target);
797 }
798 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200799 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100800 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200801 return create_error("Internal: Creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200802 }
803
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100804 res = netconf_op(session_key, rpc, NULL);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200805 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200806
807 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200808}
Radek Krejci035bf4e2012-07-25 10:59:09 +0200809
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100810static 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 +0200811{
812 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200813 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200814
815 /* create requests */
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100816 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200817 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100818 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200819 return create_error("Internal: Creating rpc request failed");
Radek Krejci62ab34b2012-07-26 13:42:05 +0200820 }
821
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100822 res = netconf_op(session_key, rpc, NULL);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200823 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200824
825 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200826}
827
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100828static json_object *netconf_killsession(const char* session_key, const char* sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200829{
830 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200831 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200832
833 /* create requests */
834 rpc = nc_rpc_killsession(sid);
835 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100836 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200837 return create_error("Internal: Creating rpc request failed");
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200838 }
839
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100840 res = netconf_op(session_key, rpc, NULL);
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200841 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200842 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200843}
844
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100845static json_object *netconf_onlytargetop(const char* session_key, NC_DATASTORE target, nc_rpc* (*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +0200846{
847 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200848 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +0200849
850 /* create requests */
Radek Krejci5cd7d422012-07-26 14:50:29 +0200851 rpc = op_func(target);
Radek Krejci2f318372012-07-26 14:22:35 +0200852 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100853 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200854 return create_error("Internal: Creating rpc request failed");
Radek Krejci2f318372012-07-26 14:22:35 +0200855 }
856
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100857 res = netconf_op(session_key, rpc, NULL);
Radek Krejci2f318372012-07-26 14:22:35 +0200858 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200859 return res;
Radek Krejci2f318372012-07-26 14:22:35 +0200860}
861
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100862static json_object *netconf_deleteconfig(const char* session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200863{
Tomas Cejka404d37e2013-04-13 02:31:35 +0200864 nc_rpc *rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200865 json_object *res = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +0200866 if (target != NC_DATASTORE_URL) {
867 rpc = nc_rpc_deleteconfig(target);
868 } else {
Tomas Cejkac7929632013-10-24 19:25:15 +0200869 rpc = nc_rpc_deleteconfig(target, url);
870 }
871 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100872 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200873 return create_error("Internal: Creating rpc request failed");
Tomas Cejka404d37e2013-04-13 02:31:35 +0200874 }
875
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100876 res = netconf_op(session_key, rpc, NULL);
Tomas Cejkac7929632013-10-24 19:25:15 +0200877 nc_rpc_free (rpc);
878 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +0200879}
880
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100881static json_object *netconf_lock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200882{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100883 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200884}
885
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100886static json_object *netconf_unlock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200887{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100888 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200889}
890
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100891static json_object *netconf_generic(const char* session_key, const char* content, char** data)
Radek Krejci80c10d92012-07-30 08:38:50 +0200892{
Tomas Cejka00635972013-06-03 15:10:52 +0200893 nc_rpc* rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200894 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +0200895
896 /* create requests */
897 rpc = nc_rpc_generic(content);
898 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100899 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200900 return create_error("Internal: Creating rpc request failed");
Radek Krejci80c10d92012-07-30 08:38:50 +0200901 }
902
Radek Krejcia332b692012-11-12 16:15:54 +0100903 if (data != NULL) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200904 // TODO ?free(*data);
905 (*data) = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100906 }
Radek Krejci80c10d92012-07-30 08:38:50 +0200907
908 /* get session where send the RPC */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100909 res = netconf_op(session_key, rpc, data);
Tomas Cejkac7929632013-10-24 19:25:15 +0200910 nc_rpc_free (rpc);
911 return res;
Radek Krejci80c10d92012-07-30 08:38:50 +0200912}
913
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200914/**
915 * @}
916 *//* netconf_operations */
917
Radek Krejci7338bde2012-08-10 12:57:30 +0200918void clb_print(NC_VERB_LEVEL level, const char* msg)
Radek Krejci469aab82012-07-22 18:42:20 +0200919{
Radek Krejci7338bde2012-08-10 12:57:30 +0200920 switch (level) {
921 case NC_VERB_ERROR:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100922 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200923 break;
924 case NC_VERB_WARNING:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100925 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200926 break;
927 case NC_VERB_VERBOSE:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100928 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200929 break;
930 case NC_VERB_DEBUG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100931 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200932 break;
933 }
Radek Krejci469aab82012-07-22 18:42:20 +0200934}
935
Tomas Cejka64b87482013-06-03 16:30:53 +0200936/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +0200937 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +0200938 * Caller should free message memory.
939 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +0200940 * \return pointer to message
941 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100942char *get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +0200943{
944 /* read json in chunked framing */
945 unsigned int buffer_size = 0;
946 ssize_t buffer_len = 0;
947 char *buffer = NULL;
948 char c;
949 ssize_t ret;
950 int i, chunk_len;
951 char chunk_len_str[12];
952
953 while (1) {
954 /* read chunk length */
955 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
956 if (buffer != NULL) {
957 free (buffer);
958 buffer = NULL;
959 }
960 break;
961 }
962 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
963 if (buffer != NULL) {
964 free (buffer);
965 buffer = NULL;
966 }
967 break;
968 }
969 i=0;
970 memset (chunk_len_str, 0, 12);
971 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
972 if (i==0 && c == '#') {
973 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
974 /* end but invalid */
975 if (buffer != NULL) {
976 free (buffer);
977 buffer = NULL;
978 }
979 }
980 /* end of message, double-loop break */
981 goto msg_complete;
982 }
983 chunk_len_str[i++] = c;
984 if (i==11) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100985 DEBUG("Message is too long, buffer for length is not big enought!!!!");
Tomas Cejka64b87482013-06-03 16:30:53 +0200986 break;
987 }
988 }
989 if (c != '\n') {
990 if (buffer != NULL) {
991 free (buffer);
992 buffer = NULL;
993 }
994 break;
995 }
996 chunk_len_str[i] = 0;
997 if ((chunk_len = atoi (chunk_len_str)) == 0) {
998 if (buffer != NULL) {
999 free (buffer);
1000 buffer = NULL;
1001 }
1002 break;
1003 }
1004 buffer_size += chunk_len+1;
1005 buffer = realloc (buffer, sizeof(char)*buffer_size);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001006 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
Tomas Cejka64b87482013-06-03 16:30:53 +02001007 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
1008 if (buffer != NULL) {
1009 free (buffer);
1010 buffer = NULL;
1011 }
1012 break;
1013 }
1014 buffer_len += ret;
1015 }
1016msg_complete:
1017 return buffer;
1018}
1019
Tomas Cejkad5b53772013-06-08 23:01:07 +02001020NC_DATASTORE parse_datastore(const char *ds)
1021{
1022 if (strcmp(ds, "running") == 0) {
1023 return NC_DATASTORE_RUNNING;
1024 } else if (strcmp(ds, "startup") == 0) {
1025 return NC_DATASTORE_STARTUP;
1026 } else if (strcmp(ds, "candidate") == 0) {
1027 return NC_DATASTORE_CANDIDATE;
Tomas Cejka4003a702013-10-01 00:02:45 +02001028 } else if (strcmp(ds, "url") == 0) {
1029 return NC_DATASTORE_URL;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001030 } else if (strcmp(ds, "config") == 0) {
1031 return NC_DATASTORE_CONFIG;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001032 }
1033 return -1;
1034}
1035
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001036NC_EDIT_TESTOPT_TYPE parse_testopt(const char *t)
1037{
1038 if (strcmp(t, "notset") == 0) {
1039 return NC_EDIT_TESTOPT_NOTSET;
1040 } else if (strcmp(t, "testset") == 0) {
1041 return NC_EDIT_TESTOPT_TESTSET;
1042 } else if (strcmp(t, "set") == 0) {
1043 return NC_EDIT_TESTOPT_SET;
1044 } else if (strcmp(t, "test") == 0) {
1045 return NC_EDIT_TESTOPT_TEST;
1046 }
1047 return NC_EDIT_TESTOPT_ERROR;
1048}
1049
Tomas Cejkad5b53772013-06-08 23:01:07 +02001050json_object *create_error(const char *errmess)
1051{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001052 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001053 json_object *reply = json_object_new_object();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001054 json_object *array = json_object_new_array();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001055 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001056 json_object_array_add(array, json_object_new_string(errmess));
1057 json_object_object_add(reply, "errors", array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001058 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001059 return reply;
1060
1061}
1062
1063json_object *create_data(const char *data)
1064{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001065 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001066 json_object *reply = json_object_new_object();
1067 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1068 json_object_object_add(reply, "data", json_object_new_string(data));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001069 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001070 return reply;
1071}
1072
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001073json_object *create_ok()
1074{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001075 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001076 json_object *reply = json_object_new_object();
1077 reply = json_object_new_object();
1078 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001079 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001080 return reply;
1081}
1082
1083json_object *handle_op_connect(apr_pool_t *pool, json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001084{
1085 const char *host = NULL;
1086 const char *port = NULL;
1087 const char *user = NULL;
1088 const char *pass = NULL;
1089 json_object *capabilities = NULL;
1090 json_object *reply = NULL;
1091 char *session_key_hash = NULL;
1092 struct nc_cpblts* cpblts = NULL;
1093 unsigned int len, i;
1094
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001095 DEBUG("Request: Connect");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001096 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001097 host = json_object_get_string(json_object_object_get((json_object *) request, "host"));
1098 port = json_object_get_string(json_object_object_get((json_object *) request, "port"));
1099 user = json_object_get_string(json_object_object_get((json_object *) request, "user"));
1100 pass = json_object_get_string(json_object_object_get((json_object *) request, "pass"));
1101
1102 capabilities = json_object_object_get((json_object *) request, "capabilities");
1103 if ((capabilities != NULL) && ((len = json_object_array_length(capabilities)) > 0)) {
1104 cpblts = nc_cpblts_new(NULL);
1105 for (i=0; i<len; i++) {
1106 nc_cpblts_add(cpblts, json_object_get_string(json_object_array_get_idx(capabilities, i)));
1107 }
1108 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001109 DEBUG("no capabilities specified");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001110 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001111 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001112
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001113 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001114 if ((host == NULL) || (user == NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001115 DEBUG("Cannot connect - insufficient input.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001116 session_key_hash = NULL;
1117 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001118 session_key_hash = netconf_connect(pool, host, port, user, pass, cpblts);
1119 DEBUG("hash: %s", session_key_hash);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001120 }
1121 if (cpblts != NULL) {
1122 nc_cpblts_free(cpblts);
1123 }
1124
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001125 GETSPEC_ERR_REPLY
1126
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001127 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001128 if (session_key_hash == NULL) {
1129 /* negative reply */
1130 if (err_reply == NULL) {
1131 reply = json_object_new_object();
1132 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1133 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001134 DEBUG("Connection failed.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001135 } else {
1136 /* use filled err_reply from libnetconf's callback */
1137 reply = err_reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001138 DEBUG("Connect - error from libnetconf's callback.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001139 }
1140 } else {
1141 /* positive reply */
1142 reply = json_object_new_object();
1143 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1144 json_object_object_add(reply, "session", json_object_new_string(session_key_hash));
1145
1146 free(session_key_hash);
1147 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001148 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001149 return reply;
1150}
1151
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001152json_object *handle_op_get(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001153{
1154 const char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001155 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001156 json_object *reply = NULL;
1157
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001158 DEBUG("Request: get (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001159
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001160 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001161 filter = json_object_get_string(json_object_object_get(request, "filter"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001162 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001163
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001164 if ((data = netconf_get(session_key, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001165 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001166 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001167 reply = create_data(data);
1168 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001169 }
1170 return reply;
1171}
1172
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001173json_object *handle_op_getconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001174{
1175 NC_DATASTORE ds_type_s = -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001176 const char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001177 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001178 const char *source = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001179 json_object *reply = NULL;
1180
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001181 DEBUG("Request: get-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001182
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001183 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001184 filter = json_object_get_string(json_object_object_get(request, "filter"));
1185
Tomas Cejkad5b53772013-06-08 23:01:07 +02001186 if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
1187 ds_type_s = parse_datastore(source);
1188 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001189 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001190 if (ds_type_s == -1) {
1191 return create_error("Invalid source repository type requested.");
1192 }
1193
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001194 if ((data = netconf_getconfig(session_key, ds_type_s, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001195 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001196 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001197 reply = create_data(data);
1198 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001199 }
1200 return reply;
1201}
1202
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001203json_object *handle_op_getschema(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001204{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001205 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001206 const char *identifier = NULL;
1207 const char *version = NULL;
1208 const char *format = NULL;
1209 json_object *reply = NULL;
1210
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001211 DEBUG("Request: get-schema (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001212 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001213 identifier = json_object_get_string(json_object_object_get(request, "identifier"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001214 version = json_object_get_string(json_object_object_get(request, "version"));
1215 format = json_object_get_string(json_object_object_get(request, "format"));
1216 pthread_mutex_lock(&json_lock);
1217
Tomas Cejkad5b53772013-06-08 23:01:07 +02001218 if (identifier == NULL) {
1219 return create_error("No identifier for get-schema supplied.");
1220 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001221
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001222 DEBUG("get-schema(version: %s, format: %s)", version, format);
1223 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001224 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001225 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001226 reply = create_data(data);
1227 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001228 }
1229 return reply;
1230}
1231
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001232json_object *handle_op_editconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001233{
1234 NC_DATASTORE ds_type_s = -1;
1235 NC_DATASTORE ds_type_t = -1;
1236 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
1237 NC_EDIT_ERROPT_TYPE erropt_type = 0;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001238 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001239 const char *defop = NULL;
1240 const char *erropt = NULL;
1241 const char *config = NULL;
1242 const char *source = NULL;
1243 const char *target = NULL;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001244 const char *testopt = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001245 json_object *reply = NULL;
1246
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001247 DEBUG("Request: edit-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001248
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001249 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001250 defop = json_object_get_string(json_object_object_get(request, "default-operation"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001251 erropt = json_object_get_string(json_object_object_get(request, "error-option"));
1252 /* get parameters */
1253 if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
1254 ds_type_t = parse_datastore(target);
1255 }
1256 if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
1257 ds_type_s = parse_datastore(source);
1258 } else {
1259 /* source is optional, default value is config */
1260 ds_type_s = NC_DATASTORE_CONFIG;
1261 }
1262 config = json_object_get_string(json_object_object_get(request, "config"));
1263 testopt = json_object_get_string(json_object_object_get(request, "test-option"));
1264 pthread_mutex_unlock(&json_lock);
1265
Tomas Cejkad5b53772013-06-08 23:01:07 +02001266 if (defop != NULL) {
1267 if (strcmp(defop, "merge") == 0) {
1268 defop_type = NC_EDIT_DEFOP_MERGE;
1269 } else if (strcmp(defop, "replace") == 0) {
1270 defop_type = NC_EDIT_DEFOP_REPLACE;
1271 } else if (strcmp(defop, "none") == 0) {
1272 defop_type = NC_EDIT_DEFOP_NONE;
1273 } else {
1274 return create_error("Invalid default-operation parameter.");
1275 }
1276 } else {
1277 defop_type = NC_EDIT_DEFOP_NOTSET;
1278 }
1279
Tomas Cejkad5b53772013-06-08 23:01:07 +02001280 if (erropt != NULL) {
1281 if (strcmp(erropt, "continue-on-error") == 0) {
1282 erropt_type = NC_EDIT_ERROPT_CONT;
1283 } else if (strcmp(erropt, "stop-on-error") == 0) {
1284 erropt_type = NC_EDIT_ERROPT_STOP;
1285 } else if (strcmp(erropt, "rollback-on-error") == 0) {
1286 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
1287 } else {
1288 return create_error("Invalid error-option parameter.");
1289 }
1290 } else {
1291 erropt_type = 0;
1292 }
1293
Tomas Cejkad5b53772013-06-08 23:01:07 +02001294 if (ds_type_t == -1) {
1295 return create_error("Invalid target repository type requested.");
1296 }
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001297 if (ds_type_s == NC_DATASTORE_CONFIG) {
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001298 if (config == NULL) {
1299 return create_error("Invalid config data parameter.");
1300 }
1301 } else if (ds_type_s == NC_DATASTORE_URL){
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001302 if (config == NULL) {
1303 config = "";
1304 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001305 }
1306
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001307 if (testopt != NULL) {
1308 testopt_type = parse_testopt(testopt);
1309 } else {
1310 testopt_type = NC_EDIT_TESTOPT_TESTSET;
1311 }
1312
1313 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 +01001314
1315 CHECK_ERR_SET_REPLY
1316
Tomas Cejkad5b53772013-06-08 23:01:07 +02001317 return reply;
1318}
1319
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001320json_object *handle_op_copyconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001321{
1322 NC_DATASTORE ds_type_s = -1;
1323 NC_DATASTORE ds_type_t = -1;
1324 const char *config = NULL;
1325 const char *target = NULL;
1326 const char *source = NULL;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001327 const char *uri_src = NULL;
1328 const char *uri_trg = NULL;
1329
Tomas Cejkad5b53772013-06-08 23:01:07 +02001330 json_object *reply = NULL;
1331
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001332 DEBUG("Request: copy-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001333
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001334 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001335 /* get parameters */
1336 if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
1337 ds_type_t = parse_datastore(target);
1338 }
1339 if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
1340 ds_type_s = parse_datastore(source);
1341 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001342 config = json_object_get_string(json_object_object_get(request, "config"));
1343 uri_src = json_object_get_string(json_object_object_get(request, "uri-source"));
1344 uri_trg = json_object_get_string(json_object_object_get(request, "uri-target"));
1345 pthread_mutex_unlock(&json_lock);
1346
Tomas Cejkad5b53772013-06-08 23:01:07 +02001347 if (source == NULL) {
1348 /* no explicit source specified -> use config data */
1349 ds_type_s = NC_DATASTORE_CONFIG;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001350 } else if (ds_type_s == -1) {
1351 /* source datastore specified, but it is invalid */
1352 return create_error("Invalid source repository type requested.");
1353 }
1354
1355 if (ds_type_t == -1) {
1356 /* invalid target datastore specified */
1357 return create_error("Invalid target repository type requested.");
1358 }
1359
Tomas Cejka55c6df52014-02-20 12:59:33 +01001360 /* source can be missing when config is given */
1361 if (source == NULL && config == NULL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001362 reply = create_error("invalid input parameters - source and config is required.");
1363 return reply;
1364 }
1365
1366 if (ds_type_s == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001367 if (uri_src == NULL) {
1368 uri_src = "";
1369 }
1370 }
1371 if (ds_type_t == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001372 if (uri_trg == NULL) {
1373 uri_trg = "";
1374 }
1375 }
1376 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001377
1378 CHECK_ERR_SET_REPLY
1379
Tomas Cejkad5b53772013-06-08 23:01:07 +02001380 return reply;
1381}
1382
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001383json_object *handle_op_generic(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001384{
1385 json_object *reply = NULL;
1386 const char *config = NULL;
1387 char *data = NULL;
1388
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001389 DEBUG("Request: generic request for session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001390
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001391 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001392 config = json_object_get_string(json_object_object_get(request, "content"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001393 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001394
1395 if (config == NULL) {
1396 return create_error("Missing content parameter.");
1397 }
1398
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001399 /* TODO */
1400 reply = netconf_generic(session_key, config, &data);
1401 if (reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001402 GETSPEC_ERR_REPLY
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001403 if (err_reply != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001404 /* use filled err_reply from libnetconf's callback */
1405 reply = err_reply;
1406 }
1407 } else {
1408 if (data == NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001409 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001410 reply = json_object_new_object();
1411 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001412 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001413 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001414 reply = create_data(data);
1415 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001416 }
1417 }
1418 return reply;
1419}
1420
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001421json_object *handle_op_disconnect(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001422{
1423 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001424 DEBUG("Request: Disconnect session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001425
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001426 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001427 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001428 } else {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001429 reply = create_ok();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001430 }
1431 return reply;
1432}
1433
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001434json_object *handle_op_kill(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001435{
1436 json_object *reply = NULL;
1437 const char *sid = NULL;
1438
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001439 DEBUG("Request: kill-session, session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001440
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001441 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001442 sid = json_object_get_string(json_object_object_get(request, "session-id"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001443 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001444
1445 if (sid == NULL) {
1446 return create_error("Missing session-id parameter.");
1447 }
1448
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001449 reply = netconf_killsession(session_key, sid);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001450
1451 CHECK_ERR_SET_REPLY
1452
Tomas Cejkad5b53772013-06-08 23:01:07 +02001453 return reply;
1454}
1455
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001456json_object *handle_op_reloadhello(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001457{
Tomas Cejka47387fd2013-06-10 20:37:46 +02001458 struct nc_session *temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001459 struct session_with_mutex * locked_session = NULL;
1460 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001461 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001462
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001463 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001464 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001465 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001466 return NULL;
1467 }
1468
Tomas Cejkad5b53772013-06-08 23:01:07 +02001469 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1470 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001471 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001472 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001473 DEBUG("creating temporal NC session.");
Tomas Cejka47387fd2013-06-10 20:37:46 +02001474 temp_session = nc_session_connect_channel(locked_session->session, NULL);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001475 if (temp_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001476 prepare_status_message(locked_session, temp_session);
1477 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001478 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001479 temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001480 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001481 DEBUG("Reload hello failed due to channel establishment");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001482 reply = create_error("Reload was unsuccessful, connection failed.");
1483 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001484 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001485 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001486 DEBUG("UNLOCK wrlock %s", __func__);
1487 if (pthread_rwlock_unlock(&session_lock) != 0) {
1488 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1489 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001490 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001491 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001492 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001493 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001494 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001495 reply = create_error("Invalid session identifier.");
1496 }
1497
1498 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
1499 reply = locked_session->hello_message;
1500 }
1501 return reply;
1502}
1503
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001504json_object *handle_op_info(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001505{
1506 json_object *reply = NULL;
Tomas Cejka47387fd2013-06-10 20:37:46 +02001507 struct session_with_mutex * locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001508 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001509
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001510 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001511 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001512 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001513 }
1514
Tomas Cejkad5b53772013-06-08 23:01:07 +02001515 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1516 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001517 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001518 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001519 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001520 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001521 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001522 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001523 if (locked_session->hello_message != NULL) {
1524 reply = locked_session->hello_message;
1525 } else {
1526 reply = create_error("Invalid session identifier.");
1527 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001528 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001529 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001530 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001531 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001532 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001533 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001534 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001535 reply = create_error("Invalid session identifier.");
1536 }
1537
Tomas Cejka47387fd2013-06-10 20:37:46 +02001538
Tomas Cejkad5b53772013-06-08 23:01:07 +02001539 return reply;
1540}
1541
Tomas Cejka6b886e02013-07-05 09:53:17 +02001542void notification_history(time_t eventtime, const char *content)
1543{
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001544 json_object *notif_history_array = (json_object *) pthread_getspecific(notif_history_key);
1545 if (notif_history_array == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001546 DEBUG("No list of notification history found.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001547 return;
1548 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001549 DEBUG("Got notification from history %lu.", (long unsigned) eventtime);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001550 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001551 json_object *notif = json_object_new_object();
1552 if (notif == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001553 DEBUG("Could not allocate memory for notification (json).");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001554 goto failed;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001555 }
1556 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
1557 json_object_object_add(notif, "content", json_object_new_string(content));
1558 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001559failed:
1560 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001561}
1562
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001563json_object *handle_op_ntfgethistory(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001564{
1565 json_object *reply = NULL;
1566 const char *sid = NULL;
1567 struct session_with_mutex *locked_session = NULL;
1568 struct nc_session *temp_session = NULL;
1569 nc_rpc *rpc = NULL;
1570 time_t start = 0;
1571 time_t stop = 0;
1572 int64_t from, to;
1573
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001574 DEBUG("Request: get notification history, session %s", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001575
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001576 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001577 sid = json_object_get_string(json_object_object_get(request, "session"));
1578 from = json_object_get_int64(json_object_object_get(request, "from"));
1579 to = json_object_get_int64(json_object_object_get(request, "to"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001580 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001581
1582 start = time(NULL) + from;
1583 stop = time(NULL) + to;
1584
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001585 DEBUG("notification history interval %li %li", (long int) from, (long int) to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001586
1587 if (sid == NULL) {
1588 return create_error("Missing session parameter.");
1589 }
1590
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001591 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001592 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001593 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001594 return NULL;
1595 }
1596
1597 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1598 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001599 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001600 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001601 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001602 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001603 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001604 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001605 DEBUG("creating temporal NC session.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001606 temp_session = nc_session_connect_channel(locked_session->session, NULL);
1607 if (temp_session != NULL) {
1608 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
1609 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001610 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001611 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001612 DEBUG("notifications: creating an rpc request failed.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001613 return create_error("notifications: creating an rpc request failed.");
1614 }
1615
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001616 DEBUG("Send NC subscribe.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001617 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001618 json_object *res = netconf_unlocked_op(temp_session, rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001619 if (res != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001620 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001621 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001622 DEBUG("Subscription RPC failed.");
Tomas Cejkac7929632013-10-24 19:25:15 +02001623 return res;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001624 }
1625 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
1626
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001627 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001628 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001629 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001630 pthread_mutex_lock(&ntf_history_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001631 pthread_mutex_lock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001632 json_object *notif_history_array = json_object_new_array();
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001633 pthread_mutex_unlock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001634 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001635 DEBUG("notif_history: cannot set thread-specific hash value.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001636 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001637
1638 ncntf_dispatch_receive(temp_session, notification_history);
1639
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001640 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001641 reply = json_object_new_object();
1642 json_object_object_add(reply, "notifications", notif_history_array);
1643 //json_object_put(notif_history_array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001644 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001645
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001646 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001647 pthread_mutex_unlock(&ntf_history_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001648 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001649 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001650 temp_session = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001651 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001652 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001653 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001654 DEBUG("Get history of notification failed due to channel establishment");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001655 reply = create_error("Get history of notification was unsuccessful, connection failed.");
1656 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001657 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001658 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001659 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001660 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001661 }
1662 reply = create_error("Invalid session identifier.");
1663 }
1664
Tomas Cejka4003a702013-10-01 00:02:45 +02001665 return reply;
1666}
1667
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001668json_object *handle_op_validate(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02001669{
1670 json_object *reply = NULL;
1671 const char *sid = NULL;
1672 const char *target = NULL;
1673 const char *url = NULL;
1674 struct session_with_mutex *locked_session = NULL;
1675 nc_rpc *rpc = NULL;
1676 NC_DATASTORE target_ds;
1677
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001678 DEBUG("Request: validate datastore, session %s", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02001679
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001680 pthread_mutex_lock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02001681 sid = json_object_get_string(json_object_object_get(request, "session"));
1682 target = json_object_get_string(json_object_object_get(request, "target"));
1683 url = json_object_get_string(json_object_object_get(request, "url"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001684 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02001685
1686
1687 if ((sid == NULL) || (target == NULL)) {
1688 return create_error("Missing session parameter.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001689 }
Tomas Cejka4003a702013-10-01 00:02:45 +02001690
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001691 /* validation */
1692 target_ds = parse_datastore(target);
1693 if (target_ds == NC_DATASTORE_URL) {
1694 if (url != NULL) {
1695 rpc = nc_rpc_validate(target_ds, url);
Tomas Cejka4003a702013-10-01 00:02:45 +02001696 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001697 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
Tomas Cejka4003a702013-10-01 00:02:45 +02001698 || (target_ds == NC_DATASTORE_CANDIDATE)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001699 rpc = nc_rpc_validate(target_ds);
1700 }
1701 if (rpc == NULL) {
1702 DEBUG("mod_netconf: creating rpc request failed");
1703 reply = create_error("Creation of RPC request failed.");
1704 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka4003a702013-10-01 00:02:45 +02001705 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001706 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02001707 }
1708
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001709 DEBUG("Request: validate datastore");
1710 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001711
1712 CHECK_ERR_SET_REPLY
1713
1714 if (reply == NULL) {
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001715 DEBUG("Request: validation ok.");
1716 reply = create_ok();
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001717 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001718 }
1719 nc_rpc_free (rpc);
1720
Tomas Cejka6b886e02013-07-05 09:53:17 +02001721 return reply;
1722}
1723
David Kupka8e60a372012-09-04 09:15:20 +02001724void * thread_routine (void * arg)
1725{
1726 void * retval = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001727 struct pollfd fds;
Tomas Cejka00635972013-06-03 15:10:52 +02001728 json_object *request = NULL;
1729 json_object *reply = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001730 int operation;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001731 int status = 0;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001732 const char *msgtext;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001733 const char *session_key;
1734 const char *target = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001735 const char *url = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001736 NC_DATASTORE ds_type_t = -1;
Tomas Cejka64b87482013-06-03 16:30:53 +02001737 char *chunked_out_msg = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001738 apr_pool_t * pool = ((struct pass_to_thread*)arg)->pool;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001739 //server_rec * server = ((struct pass_to_thread*)arg)->server;
David Kupka8e60a372012-09-04 09:15:20 +02001740 int client = ((struct pass_to_thread*)arg)->client;
1741
Tomas Cejka00635972013-06-03 15:10:52 +02001742 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001743
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001744 /* init thread specific err_reply memory */
1745 json_object **err_reply = calloc(1, sizeof(json_object **));
1746 if (err_reply == NULL) {
1747 DEBUG("Allocation of err_reply storage failed!");
1748 isterminated = 1;
1749 }
1750 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
1751 DEBUG("notif_history: cannot set thread-specific hash value.");
1752 }
David Kupka8e60a372012-09-04 09:15:20 +02001753 while (!isterminated) {
1754 fds.fd = client;
1755 fds.events = POLLIN;
1756 fds.revents = 0;
1757
1758 status = poll(&fds, 1, 1000);
1759
1760 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
1761 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001762 //DEBUG("poll interrupted");
David Kupka8e60a372012-09-04 09:15:20 +02001763 continue;
1764 } else if (status < 0) {
1765 /* 0: poll time outed
1766 * close socket and ignore this request from the client, it can try it again
1767 * -1: poll failed
1768 * something wrong happend, close this socket and wait for another request
1769 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001770 //DEBUG("poll failed, status %d(%d: %s)", status, errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +02001771 close(client);
1772 break;
1773 }
1774 /* status > 0 */
1775
1776 /* check the status of the socket */
1777
1778 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
1779 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
1780 /* close client's socket (it's probably already closed by client */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001781 //DEBUG("socket error (%d)", fds.revents);
David Kupka8e60a372012-09-04 09:15:20 +02001782 close(client);
1783 break;
1784 }
1785
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001786 DEBUG("Get framed message...");
1787 buffer = get_framed_message(client);
David Kupka8e60a372012-09-04 09:15:20 +02001788
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001789 DEBUG("Check read buffer.");
David Kupka8e60a372012-09-04 09:15:20 +02001790 if (buffer != NULL) {
Tomas Cejka00635972013-06-03 15:10:52 +02001791 enum json_tokener_error jerr;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001792 pthread_mutex_lock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001793 request = json_tokener_parse_verbose(buffer, &jerr);
1794 if (jerr != json_tokener_success) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001795 DEBUG("JSON parsing error");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001796 pthread_mutex_unlock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001797 continue;
1798 }
David Kupka8e60a372012-09-04 09:15:20 +02001799
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001800 operation = json_object_get_int(json_object_object_get(request, "type"));
Tomas Cejka64b87482013-06-03 16:30:53 +02001801 session_key = json_object_get_string(json_object_object_get(request, "session"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001802 pthread_mutex_unlock(&json_lock);
1803
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001804 DEBUG("operation %d session_key %s.", operation, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001805 /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
1806 if (operation != MSG_CONNECT && session_key == NULL) {
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001807 reply = create_error("Missing session specification.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001808 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001809 msgtext = json_object_to_json_string(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001810 pthread_mutex_unlock(&json_lock);
1811
David Kupka8e60a372012-09-04 09:15:20 +02001812 send(client, msgtext, strlen(msgtext) + 1, 0);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001813
1814 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001815 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001816 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001817 /* there is some stupid client, so close the connection to give a chance to some other client */
1818 close(client);
1819 break;
1820 }
1821
David Kupka8e60a372012-09-04 09:15:20 +02001822 /* null global JSON error-reply */
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001823 (*err_reply) = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001824
1825 /* prepare reply envelope */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001826 if (reply != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001827 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001828 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001829 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001830 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001831 reply = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001832
1833 /* process required operation */
1834 switch (operation) {
1835 case MSG_CONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001836 reply = handle_op_connect(pool, request);
David Kupka8e60a372012-09-04 09:15:20 +02001837 break;
1838 case MSG_GET:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001839 reply = handle_op_get(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001840 break;
1841 case MSG_GETCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001842 reply = handle_op_getconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001843 break;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001844 case MSG_GETSCHEMA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001845 reply = handle_op_getschema(pool, request, session_key);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001846 break;
David Kupka8e60a372012-09-04 09:15:20 +02001847 case MSG_EDITCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001848 reply = handle_op_editconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001849 break;
1850 case MSG_COPYCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001851 reply = handle_op_copyconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001852 break;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001853
David Kupka8e60a372012-09-04 09:15:20 +02001854 case MSG_DELETECONFIG:
David Kupka8e60a372012-09-04 09:15:20 +02001855 case MSG_LOCK:
David Kupka8e60a372012-09-04 09:15:20 +02001856 case MSG_UNLOCK:
Tomas Cejkad5b53772013-06-08 23:01:07 +02001857 /* get parameters */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001858 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001859 if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
1860 ds_type_t = parse_datastore(target);
1861 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001862 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001863
1864 if (ds_type_t == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001865 reply = create_error("Invalid target repository type requested.");
David Kupka8e60a372012-09-04 09:15:20 +02001866 break;
1867 }
David Kupka8e60a372012-09-04 09:15:20 +02001868 switch(operation) {
1869 case MSG_DELETECONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001870 DEBUG("Request: delete-config (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001871 pthread_mutex_lock(&json_lock);
Tomas Cejkac7929632013-10-24 19:25:15 +02001872 url = json_object_get_string(json_object_object_get(request, "url"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001873 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001874 reply = netconf_deleteconfig(session_key, ds_type_t, url);
David Kupka8e60a372012-09-04 09:15:20 +02001875 break;
1876 case MSG_LOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001877 DEBUG("Request: lock (session %s)", session_key);
1878 reply = netconf_lock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001879 break;
1880 case MSG_UNLOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001881 DEBUG("Request: unlock (session %s)", session_key);
1882 reply = netconf_unlock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001883 break;
1884 default:
Tomas Cejkac7929632013-10-24 19:25:15 +02001885 reply = create_error("Internal: Unknown request type.");
David Kupka8e60a372012-09-04 09:15:20 +02001886 break;
1887 }
1888
Tomas Cejkac7929632013-10-24 19:25:15 +02001889 if (reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001890 if (*err_reply == NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001891 /** \todo more clever error message wanted */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001892 pthread_mutex_lock(&json_lock);
Tomas Cejkac7929632013-10-24 19:25:15 +02001893 reply = json_object_new_object();
1894 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001895 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001896 } else {
1897 /* use filled err_reply from libnetconf's callback */
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001898 reply = *err_reply;
David Kupka8e60a372012-09-04 09:15:20 +02001899 }
David Kupka8e60a372012-09-04 09:15:20 +02001900 }
1901 break;
1902 case MSG_KILL:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001903 reply = handle_op_kill(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001904 break;
1905 case MSG_DISCONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001906 reply = handle_op_disconnect(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001907 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001908 case MSG_RELOADHELLO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001909 reply = handle_op_reloadhello(pool, request, session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001910 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001911 case MSG_INFO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001912 reply = handle_op_info(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001913 break;
1914 case MSG_GENERIC:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001915 reply = handle_op_generic(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001916 break;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001917 case MSG_NTF_GETHISTORY:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001918 reply = handle_op_ntfgethistory(pool, request, session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001919 break;
Tomas Cejka4003a702013-10-01 00:02:45 +02001920 case MSG_VALIDATE:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001921 reply = handle_op_validate(pool, request, session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02001922 break;
David Kupka8e60a372012-09-04 09:15:20 +02001923 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001924 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001925 reply = create_error("Operation not supported.");
David Kupka8e60a372012-09-04 09:15:20 +02001926 break;
1927 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001928 DEBUG("Clean request json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001929 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001930 json_object_put(request);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001931 DEBUG("Send reply json object.");
David Kupka1e3e4c82012-09-04 09:32:15 +02001932 /* send reply to caller */
1933 if (reply != NULL) {
1934 msgtext = json_object_to_json_string(reply);
Tomas Cejka64b87482013-06-03 16:30:53 +02001935 if (asprintf (&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
Tomas Cejka00635972013-06-03 15:10:52 +02001936 if (buffer != NULL) {
1937 free(buffer);
1938 buffer = NULL;
1939 }
David Kupka8e60a372012-09-04 09:15:20 +02001940 break;
1941 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001942 pthread_mutex_unlock(&json_lock);
1943
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001944 DEBUG("Send framed reply json object.");
Tomas Cejka64b87482013-06-03 16:30:53 +02001945 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001946 DEBUG("Clean reply json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001947 pthread_mutex_lock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02001948 json_object_put(reply);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001949 reply = NULL;
1950 DEBUG("Clean message buffer.");
Tomas Cejka64b87482013-06-03 16:30:53 +02001951 free(chunked_out_msg);
1952 chunked_out_msg = NULL;
Tomas Cejka00635972013-06-03 15:10:52 +02001953 if (buffer != NULL) {
1954 free(buffer);
1955 buffer = NULL;
1956 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001957 if ((err_reply != NULL) && (*err_reply != NULL)) {
1958 json_object_put(*err_reply);
1959 *err_reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001960 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001961 pthread_mutex_unlock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02001962 } else {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001963 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001964 DEBUG("Reply is NULL, shouldn't be...");
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001965 if (*err_reply == NULL) {
1966 DEBUG("No error was set - really strange situation");
1967 } else {
1968 DEBUG("Error was set but it was not sent.");
1969 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001970 continue;
David Kupka8e60a372012-09-04 09:15:20 +02001971 }
1972 }
1973 }
David Kupka8e60a372012-09-04 09:15:20 +02001974 free (arg);
1975
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001976 err_reply = (json_object **) pthread_getspecific(err_reply_key);
1977 if (err_reply != NULL) {
1978 if (*err_reply != NULL) {
1979 pthread_mutex_lock(&json_lock);
1980 json_object_put(*err_reply);
1981 pthread_mutex_unlock(&json_lock);
1982 }
1983 free(err_reply);
1984 err_reply = NULL;
1985 }
1986
David Kupka8e60a372012-09-04 09:15:20 +02001987 return retval;
1988}
1989
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001990/**
1991 * \brief Close all open NETCONF sessions.
1992 *
1993 * During termination of mod_netconf, it is useful to close all remaining
1994 * sessions. This function iterates over the list of sessions and close them
1995 * all.
1996 *
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001997 * \param[in] p apr pool needed for hash table iterating
1998 * \param[in] ht hash table of session_with_mutex structs
1999 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002000static void close_all_nc_sessions(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002001{
2002 apr_hash_index_t *hi;
2003 void *val = NULL;
2004 struct session_with_mutex *swm = NULL;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002005 const char *hashed_key = NULL;
2006 apr_ssize_t hashed_key_length;
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002007 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002008
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002009 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002010 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002011 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002012 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002013 return;
2014 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002015 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002016 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002017 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002018 swm = (struct session_with_mutex *) val;
2019 if (swm != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002020 DEBUG("LOCK mutex %s", __func__);
2021 pthread_mutex_lock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002022 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002023 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002024 pthread_mutex_unlock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002025
2026 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002027 close_and_free_session(swm);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002028 }
2029 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002030 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002031 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002032 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002033 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002034 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002035}
2036
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002037static void check_timeout_and_close(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002038{
2039 apr_hash_index_t *hi;
2040 void *val = NULL;
2041 struct nc_session *ns = NULL;
2042 struct session_with_mutex *swm = NULL;
2043 const char *hashed_key = NULL;
2044 apr_ssize_t hashed_key_length;
2045 apr_time_t current_time = apr_time_now();
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002046 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002047
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002048 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002049//DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002050 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002051 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002052 return;
2053 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002054 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002055 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
2056 swm = (struct session_with_mutex *) val;
2057 if (swm == NULL) {
2058 continue;
2059 }
2060 ns = swm->session;
2061 if (ns == NULL) {
2062 continue;
2063 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002064//DEBUG("LOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002065 pthread_mutex_lock(&swm->lock);
2066 if ((current_time - swm->last_activity) > apr_time_from_sec(ACTIVITY_TIMEOUT)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002067 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002068 /* remove session from the active sessions list */
Tomas Cejka47387fd2013-06-10 20:37:46 +02002069 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002070//DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002071 pthread_mutex_unlock(&swm->lock);
2072
2073 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002074 close_and_free_session(swm);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002075 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002076//DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002077 pthread_mutex_unlock(&swm->lock);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002078 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002079 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002080 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002081//DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002082 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002083 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002084 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002085}
2086
2087
2088/**
Radek Krejcif23850c2012-07-23 16:14:17 +02002089 * This is actually implementation of NETCONF client
2090 * - requests are received from UNIX socket in the predefined format
2091 * - results are replied through the same way
2092 * - the daemon run as a separate process, but it is started and stopped
2093 * automatically by Apache.
2094 *
2095 */
Radek Krejci469aab82012-07-22 18:42:20 +02002096static void forked_proc(apr_pool_t * pool, server_rec * server)
2097{
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002098 struct timeval tv;
Radek Krejci469aab82012-07-22 18:42:20 +02002099 struct sockaddr_un local, remote;
David Kupka8e60a372012-09-04 09:15:20 +02002100 int lsock, client, ret, i, pthread_count = 0;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002101 unsigned int olds = 0, timediff = 0;
David Kupka8e60a372012-09-04 09:15:20 +02002102 socklen_t len;
Radek Krejciae021c12012-07-25 18:03:52 +02002103 mod_netconf_cfg *cfg;
David Kupka8e60a372012-09-04 09:15:20 +02002104 struct pass_to_thread * arg;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002105 pthread_t * ptids = calloc(1, sizeof(pthread_t));
David Kupka8e60a372012-09-04 09:15:20 +02002106 struct timespec maxtime;
2107 pthread_rwlockattr_t lock_attrs;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002108 char *sockname = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +02002109 #ifdef WITH_NOTIFICATIONS
2110 char use_notifications = 0;
2111 #endif
David Kupka8e60a372012-09-04 09:15:20 +02002112
Tomas Cejka6b886e02013-07-05 09:53:17 +02002113 http_server = server;
2114
Tomas Cejka404d37e2013-04-13 02:31:35 +02002115 /* wait at most 5 seconds for every thread to terminate */
David Kupka8e60a372012-09-04 09:15:20 +02002116 maxtime.tv_sec = 5;
2117 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02002118
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002119 #ifndef HTTPD_INDEPENDENT
Radek Krejcif23850c2012-07-23 16:14:17 +02002120 /* change uid and gid of process for security reasons */
Radek Krejci469aab82012-07-22 18:42:20 +02002121 unixd_setup_child();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002122 #endif
Radek Krejci469aab82012-07-22 18:42:20 +02002123
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002124 if (server != NULL) {
2125 cfg = ap_get_module_config(server->module_config, &netconf_module);
2126 if (cfg == NULL) {
2127 DEBUG("Getting mod_netconf configuration failed");
2128 return;
2129 }
2130 sockname = cfg->sockname;
2131 } else {
2132 sockname = SOCKET_FILENAME;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002133 }
Radek Krejci469aab82012-07-22 18:42:20 +02002134
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002135 /* create listening UNIX socket to accept incoming connections */
Radek Krejci469aab82012-07-22 18:42:20 +02002136 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002137 DEBUG("Creating socket failed (%s)", strerror(errno));
2138 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002139 }
2140
2141 local.sun_family = AF_UNIX;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002142 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
Radek Krejci469aab82012-07-22 18:42:20 +02002143 unlink(local.sun_path);
2144 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
2145
2146 if (bind(lsock, (struct sockaddr *) &local, len) == -1) {
2147 if (errno == EADDRINUSE) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002148 DEBUG("mod_netconf socket address already in use");
2149 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002150 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002151 DEBUG("Binding socket failed (%s)", strerror(errno));
2152 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002153 }
2154
2155 if (listen(lsock, MAX_SOCKET_CL) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002156 DEBUG("Setting up listen socket failed (%s)", strerror(errno));
2157 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002158 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002159 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02002160
Tomas Cejkaba21b382013-04-13 02:37:32 +02002161 /* prepare internal lists */
2162 netconf_sessions_list = apr_hash_make(pool);
2163
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002164 #ifdef WITH_NOTIFICATIONS
Tomas Cejka47387fd2013-06-10 20:37:46 +02002165 if (notification_init(pool, server) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002166 DEBUG("libwebsockets initialization failed");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002167 use_notifications = 0;
2168 } else {
2169 use_notifications = 1;
2170 }
2171 #endif
2172
Radek Krejci469aab82012-07-22 18:42:20 +02002173 /* setup libnetconf's callbacks */
2174 nc_verbosity(NC_VERB_DEBUG);
Radek Krejci469aab82012-07-22 18:42:20 +02002175 nc_callback_print(clb_print);
2176 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
2177 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
2178 nc_callback_sshauth_password(netconf_callback_sshauth_password);
Radek Krejcic11fd862012-07-26 12:41:21 +02002179 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02002180
2181 /* disable publickey authentication */
2182 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
2183
David Kupka8e60a372012-09-04 09:15:20 +02002184 /* create mutex protecting session list */
2185 pthread_rwlockattr_init(&lock_attrs);
2186 /* rwlock is shared only with threads in this process */
2187 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
2188 /* create rw lock */
2189 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002190 DEBUG("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
2191 goto error_exit;
David Kupka8e60a372012-09-04 09:15:20 +02002192 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002193 pthread_mutex_init(&ntf_history_lock, NULL);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002194 pthread_mutex_init(&json_lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002195 DEBUG("init of notif_history_key.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002196 if (pthread_key_create(&notif_history_key, NULL) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002197 DEBUG("init of notif_history_key failed");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002198 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002199 DEBUG("init of err_reply_key.");
2200 if (pthread_key_create(&err_reply_key, NULL) != 0) {
2201 DEBUG("init of err_reply_key failed");
2202 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02002203
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002204 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002205 while (isterminated == 0) {
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002206 gettimeofday(&tv, NULL);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002207 timediff = (unsigned int)tv.tv_sec - olds;
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002208 #ifdef WITH_NOTIFICATIONS
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002209 if (timediff > 60) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002210 DEBUG("handling notifications");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002211 }
2212 if (use_notifications == 1) {
2213 notification_handle();
2214 }
2215 #endif
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002216 if (timediff > ACTIVITY_CHECK_INTERVAL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002217 check_timeout_and_close(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002218 }
Radek Krejci469aab82012-07-22 18:42:20 +02002219
2220 /* open incoming connection if any */
David Kupka8e60a372012-09-04 09:15:20 +02002221 len = sizeof(remote);
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002222 if (((unsigned int)tv.tv_sec - olds) > 60) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002223 DEBUG("accepting another client");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002224 olds = tv.tv_sec;
2225 }
David Kupka8e60a372012-09-04 09:15:20 +02002226 client = accept(lsock, (struct sockaddr *) &remote, &len);
Radek Krejci469aab82012-07-22 18:42:20 +02002227 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
2228 apr_sleep(SLEEP_TIME);
2229 continue;
2230 } else if (client == -1 && (errno == EINTR)) {
2231 continue;
2232 } else if (client == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002233 DEBUG("Accepting mod_netconf client connection failed (%s)", strerror(errno));
Radek Krejci469aab82012-07-22 18:42:20 +02002234 continue;
2235 }
Radek Krejci469aab82012-07-22 18:42:20 +02002236
2237 /* set client's socket as non-blocking */
Radek Krejcif23850c2012-07-23 16:14:17 +02002238 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002239
David Kupka8e60a372012-09-04 09:15:20 +02002240 arg = malloc (sizeof(struct pass_to_thread));
2241 arg->client = client;
2242 arg->pool = pool;
2243 arg->server = server;
2244 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02002245
David Kupka8e60a372012-09-04 09:15:20 +02002246 /* start new thread. It will serve this particular request and then terminate */
2247 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void*)arg)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002248 DEBUG("Creating POSIX thread failed: %d\n", ret);
David Kupka8e60a372012-09-04 09:15:20 +02002249 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002250 DEBUG("Thread %lu created", ptids[pthread_count]);
David Kupka8e60a372012-09-04 09:15:20 +02002251 pthread_count++;
2252 ptids = realloc (ptids, sizeof(pthread_t)*(pthread_count+1));
2253 ptids[pthread_count] = 0;
2254 }
Radek Krejci469aab82012-07-22 18:42:20 +02002255
David Kupka8e60a372012-09-04 09:15:20 +02002256 /* check if some thread already terminated, free some resources by joining it */
2257 for (i=0; i<pthread_count; i++) {
2258 if (pthread_tryjoin_np (ptids[i], (void**)&arg) == 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002259 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
David Kupka8e60a372012-09-04 09:15:20 +02002260 pthread_count--;
2261 if (pthread_count > 0) {
2262 /* place last Thread ID on the place of joined one */
2263 ptids[i] = ptids[pthread_count];
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002264 }
Radek Krejci469aab82012-07-22 18:42:20 +02002265 }
2266 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002267 DEBUG("Running %d threads", pthread_count);
Radek Krejci469aab82012-07-22 18:42:20 +02002268 }
2269
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002270 DEBUG("mod_netconf terminating...");
David Kupka8e60a372012-09-04 09:15:20 +02002271 /* join all threads */
2272 for (i=0; i<pthread_count; i++) {
2273 pthread_timedjoin_np (ptids[i], (void**)&arg, &maxtime);
2274 }
Radek Krejci469aab82012-07-22 18:42:20 +02002275
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002276 #ifdef WITH_NOTIFICATIONS
2277 notification_close();
2278 #endif
2279
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002280 /* close all NETCONF sessions */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002281 close_all_nc_sessions(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002282
David Kupka8e60a372012-09-04 09:15:20 +02002283 /* destroy rwlock */
2284 pthread_rwlock_destroy(&session_lock);
2285 pthread_rwlockattr_destroy(&lock_attrs);
2286
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002287 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02002288
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002289 free(ptids);
2290 close(lsock);
Radek Krejci469aab82012-07-22 18:42:20 +02002291 exit(APR_SUCCESS);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002292 return;
2293error_exit:
2294 close(lsock);
2295 free(ptids);
2296 return;
Radek Krejci469aab82012-07-22 18:42:20 +02002297}
2298
Radek Krejcif23850c2012-07-23 16:14:17 +02002299static void *mod_netconf_create_conf(apr_pool_t * pool, server_rec * s)
Radek Krejci469aab82012-07-22 18:42:20 +02002300{
Radek Krejcif23850c2012-07-23 16:14:17 +02002301 mod_netconf_cfg *config = apr_pcalloc(pool, sizeof(mod_netconf_cfg));
2302 apr_pool_create(&config->pool, pool);
2303 config->forkproc = NULL;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002304 config->sockname = SOCKET_FILENAME;
Radek Krejcif23850c2012-07-23 16:14:17 +02002305
2306 return (void *)config;
Radek Krejci469aab82012-07-22 18:42:20 +02002307}
2308
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002309#ifndef HTTPD_INDEPENDENT
Radek Krejci469aab82012-07-22 18:42:20 +02002310static int mod_netconf_master_init(apr_pool_t * pconf, apr_pool_t * ptemp,
2311 apr_pool_t * plog, server_rec * s)
2312{
Radek Krejcif23850c2012-07-23 16:14:17 +02002313 mod_netconf_cfg *config;
2314 apr_status_t res;
2315
Radek Krejci469aab82012-07-22 18:42:20 +02002316 /* These two help ensure that we only init once. */
Radek Krejcia332b692012-11-12 16:15:54 +01002317 void *data = NULL;
Radek Krejcif23850c2012-07-23 16:14:17 +02002318 const char *userdata_key = "netconf_ipc_init";
Radek Krejci469aab82012-07-22 18:42:20 +02002319
2320 /*
2321 * The following checks if this routine has been called before.
2322 * This is necessary because the parent process gets initialized
2323 * a couple of times as the server starts up.
2324 */
2325 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2326 if (!data) {
2327 apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
2328 return (OK);
2329 }
2330
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002331 DEBUG("creating mod_netconf daemon");
Radek Krejcif23850c2012-07-23 16:14:17 +02002332 config = ap_get_module_config(s->module_config, &netconf_module);
Radek Krejcidfaa6ea2012-07-23 09:04:43 +02002333
Radek Krejcif23850c2012-07-23 16:14:17 +02002334 if (config && config->forkproc == NULL) {
2335 config->forkproc = apr_pcalloc(config->pool, sizeof(apr_proc_t));
2336 res = apr_proc_fork(config->forkproc, config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002337 switch (res) {
2338 case APR_INCHILD:
2339 /* set signal handler */
Radek Krejcif23850c2012-07-23 16:14:17 +02002340 apr_signal_init(config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002341 apr_signal(SIGTERM, signal_handler);
2342
2343 /* log start of the separated NETCONF communication process */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002344 DEBUG("mod_netconf daemon started (PID %d)", getpid());
Radek Krejci469aab82012-07-22 18:42:20 +02002345
2346 /* start main loop providing NETCONF communication */
Radek Krejcif23850c2012-07-23 16:14:17 +02002347 forked_proc(config->pool, s);
Radek Krejci469aab82012-07-22 18:42:20 +02002348
Radek Krejcif23850c2012-07-23 16:14:17 +02002349 /* I never should be here, wtf?!? */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002350 DEBUG("mod_netconf daemon unexpectedly stopped");
Radek Krejci469aab82012-07-22 18:42:20 +02002351 exit(APR_EGENERAL);
2352 break;
2353 case APR_INPARENT:
2354 /* register child to be killed (SIGTERM) when the module config's pool dies */
Radek Krejcif23850c2012-07-23 16:14:17 +02002355 apr_pool_note_subprocess(config->pool, config->forkproc, APR_KILL_AFTER_TIMEOUT);
Radek Krejci469aab82012-07-22 18:42:20 +02002356 break;
2357 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002358 DEBUG("apr_proc_fork() failed");
Radek Krejci469aab82012-07-22 18:42:20 +02002359 break;
2360 }
Radek Krejcif23850c2012-07-23 16:14:17 +02002361 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002362 DEBUG("mod_netconf misses configuration structure");
Radek Krejci469aab82012-07-22 18:42:20 +02002363 }
2364
2365 return OK;
2366}
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002367#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002368
Radek Krejci469aab82012-07-22 18:42:20 +02002369/**
2370 * Register module hooks
2371 */
2372static void mod_netconf_register_hooks(apr_pool_t * p)
2373{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002374#ifndef HTTPD_INDEPENDENT
Radek Krejcif23850c2012-07-23 16:14:17 +02002375 ap_hook_post_config(mod_netconf_master_init, NULL, NULL, APR_HOOK_LAST);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002376#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002377}
2378
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002379static const char* cfg_set_socket_path(cmd_parms* cmd, void* cfg, const char* arg)
2380{
2381 ((mod_netconf_cfg*)cfg)->sockname = apr_pstrdup(cmd->pool, arg);
2382 return NULL;
2383}
2384
2385static const command_rec netconf_cmds[] = {
2386 AP_INIT_TAKE1("NetconfSocket", cfg_set_socket_path, NULL, OR_ALL, "UNIX socket path for mod_netconf communication."),
2387 {NULL}
2388};
2389
Radek Krejci469aab82012-07-22 18:42:20 +02002390/* Dispatch list for API hooks */
2391module AP_MODULE_DECLARE_DATA netconf_module = {
2392 STANDARD20_MODULE_STUFF,
2393 NULL, /* create per-dir config structures */
2394 NULL, /* merge per-dir config structures */
Radek Krejcif23850c2012-07-23 16:14:17 +02002395 mod_netconf_create_conf, /* create per-server config structures */
Radek Krejci469aab82012-07-22 18:42:20 +02002396 NULL, /* merge per-server config structures */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002397 netconf_cmds, /* table of config file commands */
Radek Krejci469aab82012-07-22 18:42:20 +02002398 mod_netconf_register_hooks /* register hooks */
2399};
Radek Krejcia332b692012-11-12 16:15:54 +01002400
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002401int main(int argc, char **argv)
2402{
2403 apr_pool_t *pool;
2404 apr_app_initialize(&argc, (char const *const **) &argv, NULL);
2405 apr_signal(SIGTERM, signal_handler);
2406 apr_signal(SIGINT, signal_handler);
2407 apr_pool_create(&pool, NULL);
2408 forked_proc(pool, NULL);
2409 apr_pool_destroy(pool);
2410 apr_terminate();
2411 DEBUG("Terminated");
2412 return 0;
2413}