blob: 5093f67a48845b46fa8fad59cb7e06d89d4009ed [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
Radek Krejci469aab82012-07-22 18:42:20 +0200119volatile int isterminated = 0;
120
121static char* password;
122
Radek Krejci469aab82012-07-22 18:42:20 +0200123static void signal_handler(int sign)
124{
125 switch (sign) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100126 case SIGINT:
Radek Krejci469aab82012-07-22 18:42:20 +0200127 case SIGTERM:
128 isterminated = 1;
Radek Krejci469aab82012-07-22 18:42:20 +0200129 break;
130 }
131}
132
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200133static char* gen_ncsession_hash( const char* hostname, const char* port, const char* sid)
Radek Krejci469aab82012-07-22 18:42:20 +0200134{
Radek Krejcif23850c2012-07-23 16:14:17 +0200135 unsigned char hash_raw[APR_SHA1_DIGESTSIZE];
136 int i;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200137 char* hash;
Radek Krejcif23850c2012-07-23 16:14:17 +0200138
Radek Krejci469aab82012-07-22 18:42:20 +0200139 apr_sha1_ctx_t sha1_ctx;
140 apr_sha1_init(&sha1_ctx);
141 apr_sha1_update(&sha1_ctx, hostname, strlen(hostname));
142 apr_sha1_update(&sha1_ctx, port, strlen(port));
143 apr_sha1_update(&sha1_ctx, sid, strlen(sid));
Radek Krejcif23850c2012-07-23 16:14:17 +0200144 apr_sha1_final(hash_raw, &sha1_ctx);
Radek Krejci469aab82012-07-22 18:42:20 +0200145
Radek Krejcif23850c2012-07-23 16:14:17 +0200146 /* convert binary hash into hex string, which is printable */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200147 hash = malloc(sizeof(char) * ((2*APR_SHA1_DIGESTSIZE)+1));
Radek Krejcif23850c2012-07-23 16:14:17 +0200148 for (i = 0; i < APR_SHA1_DIGESTSIZE; i++) {
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200149 snprintf(hash + (2*i), 3, "%02x", hash_raw[i]);
Radek Krejcif23850c2012-07-23 16:14:17 +0200150 }
151 //hash[2*APR_SHA1_DIGESTSIZE] = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200152
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200153 return (hash);
Radek Krejci469aab82012-07-22 18:42:20 +0200154}
155
156int netconf_callback_ssh_hostkey_check (const char* hostname, int keytype, const char* fingerprint)
157{
158 /* always approve */
159 return (EXIT_SUCCESS);
160}
161
162char* netconf_callback_sshauth_password (const char* username, const char* hostname)
163{
164 char* buf;
165
166 buf = malloc ((strlen(password) + 1) * sizeof(char));
167 apr_cpystrn(buf, password, strlen(password) + 1);
168
169 return (buf);
170}
171
172void netconf_callback_sshauth_interactive (const char* name,
173 int name_len,
174 const char* instruction,
175 int instruction_len,
176 int num_prompts,
177 const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts,
178 LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses,
179 void** abstract)
180{
181 int i;
182
183 for (i=0; i<num_prompts; i++) {
184 responses[i].text = malloc ((strlen(password) + 1) * sizeof(char));
185 apr_cpystrn(responses[i].text, password, strlen(password) + 1);
186 responses[i].length = strlen(responses[i].text) + 1;
187 }
188
189 return;
190}
191
Radek Krejcic11fd862012-07-26 12:41:21 +0200192static json_object *err_reply = NULL;
193void netconf_callback_error_process(const char* tag,
194 const char* type,
195 const char* severity,
196 const char* apptag,
197 const char* path,
198 const char* message,
199 const char* attribute,
200 const char* element,
201 const char* ns,
202 const char* sid)
203{
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100204 json_object *array = NULL;
205 if (err_reply == NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100206 pthread_mutex_lock(&json_lock);
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100207 err_reply = json_object_new_object();
208 array = json_object_new_array();
209 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
210 json_object_object_add(err_reply, "errors", array);
211 if (message != NULL) {
212 json_object_array_add(array, json_object_new_string(message));
213 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100214 pthread_mutex_unlock(&json_lock);
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100215 return;
216 } else {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100217 pthread_mutex_lock(&json_lock);
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100218 array = json_object_object_get(err_reply, "errors");
219 if (array != NULL) {
220 if (message != NULL) {
221 json_object_array_add(array, json_object_new_string(message));
222 }
223 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100224 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100225 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100226 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200227}
228
Tomas Cejka47387fd2013-06-10 20:37:46 +0200229/**
230 * should be used in locked area
231 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100232void prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200233{
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200234 json_object *json_obj = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100235 char *old_sid = NULL;
236 const char *j_old_sid = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200237 const char *cpbltstr;
238 struct nc_cpblts* cpblts = NULL;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200239
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200240 if (s == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100241 DEBUG("No session given.");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200242 return;
243 }
244
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100245 pthread_mutex_lock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200246 if (s->hello_message != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100247 DEBUG("clean previous hello message");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200248 //json_object_put(s->hello_message);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100249 j_old_sid = json_object_get_string(json_object_object_get(s->hello_message, "sid"));
250 if (j_old_sid != NULL) {
251 old_sid = strdup(j_old_sid);
252 }
Tomas Cejkadd5cb452014-03-26 15:22:00 +0100253 json_object_put(s->hello_message);
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200254 s->hello_message = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200255 }
Tomas Cejkadd5cb452014-03-26 15:22:00 +0100256 s->hello_message = json_object_get(json_object_new_object());
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200257 if (session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100258 if (old_sid != NULL) {
259 /* use previous sid */
260 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
261 free(old_sid);
262 } else {
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200263 /* we don't have old sid */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100264 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
265 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200266 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
267 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
268 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
269 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
270 cpblts = nc_session_get_cpblts (session);
271 if (cpblts != NULL) {
272 json_obj = json_object_new_array();
273 nc_cpblts_iter_start (cpblts);
274 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
275 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
276 }
277 json_object_object_add(s->hello_message, "capabilities", json_obj);
278 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100279 DEBUG("%s", json_object_to_json_string(s->hello_message));
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200280 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100281 DEBUG("Session was not given.");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200282 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
283 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
284 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100285 DEBUG("Status info from hello message prepared");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100286 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200287
288}
289
290
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200291/**
292 * \defgroup netconf_operations NETCONF operations
293 * The list of NETCONF operations that mod_netconf supports.
294 * @{
295 */
296
297/**
298 * \brief Connect to NETCONF server
299 *
300 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
301 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100302static 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 +0200303{
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200304 struct nc_session* session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200305 struct session_with_mutex * locked_session;
Radek Krejcif23850c2012-07-23 16:14:17 +0200306 char *session_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200307
308 /* connect to the requested NETCONF server */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200309 password = (char*)pass;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100310 DEBUG("prepare to connect %s@%s:%s", user, host, port);
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200311 nc_verbosity(NC_VERB_DEBUG);
David Kupka8e60a372012-09-04 09:15:20 +0200312 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100313 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +0200314
Radek Krejci469aab82012-07-22 18:42:20 +0200315 /* if connected successful, add session to the list */
316 if (session != NULL) {
317 /* generate hash for the session */
Radek Krejcic11fd862012-07-26 12:41:21 +0200318 session_key = gen_ncsession_hash(
319 (host==NULL) ? "localhost" : host,
320 (port==NULL) ? "830" : port,
Radek Krejcia282bed2012-07-27 14:43:45 +0200321 nc_session_get_id(session));
David Kupka8e60a372012-09-04 09:15:20 +0200322
Tomas Cejkaba21b382013-04-13 02:37:32 +0200323 /** \todo allocate from apr_pool */
Tomas Cejka73496bf2014-03-26 15:31:09 +0100324 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 +0200325 nc_session_free(session);
Tomas Cejka73496bf2014-03-26 15:31:09 +0100326 session = NULL;
327 free(locked_session);
328 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100329 DEBUG("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200330 return NULL;
331 }
332 locked_session->session = session;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200333 locked_session->last_activity = apr_time_now();
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200334 locked_session->hello_message = NULL;
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200335 locked_session->closed = 0;
David Kupka8e60a372012-09-04 09:15:20 +0200336 pthread_mutex_init (&locked_session->lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100337 DEBUG("Before session_lock");
David Kupka8e60a372012-09-04 09:15:20 +0200338 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100339 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200340 if (pthread_rwlock_wrlock (&session_lock) != 0) {
341 nc_session_free(session);
342 free (locked_session);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100343 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200344 return NULL;
345 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200346 locked_session->notifications = apr_array_make(pool, NOTIFICATION_QUEUE_SIZE, sizeof(notification_t));
Tomas Cejka654f84e2013-04-19 11:55:01 +0200347 locked_session->ntfc_subscribed = 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100348 DEBUG("Add connection to the list");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200349 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 +0100350 DEBUG("Before session_unlock");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200351
Tomas Cejka47387fd2013-06-10 20:37:46 +0200352 /* lock session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100353 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200354 pthread_mutex_lock(&locked_session->lock);
355
356 /* unlock session list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100357 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200358 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100359 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +0200360 }
361
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200362 /* store information about session from hello message for future usage */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100363 prepare_status_message(locked_session, session);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200364
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100365 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200366 pthread_mutex_unlock(&locked_session->lock);
367
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100368 DEBUG("NETCONF session established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200369 return (session_key);
Radek Krejci469aab82012-07-22 18:42:20 +0200370 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100371 DEBUG("Connection could not be established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200372 return (NULL);
Radek Krejci469aab82012-07-22 18:42:20 +0200373 }
374
Radek Krejci469aab82012-07-22 18:42:20 +0200375}
376
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100377static int close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +0200378{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100379 DEBUG("lock private lock.");
380 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200381 if (pthread_mutex_lock(&locked_session->lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100382 DEBUG("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200383 }
384 locked_session->ntfc_subscribed = 0;
385 locked_session->closed = 1;
Tomas Cejka73496bf2014-03-26 15:31:09 +0100386 if (locked_session->session != NULL) {
387 nc_session_free(locked_session->session);
388 locked_session->session = NULL;
389 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100390 DEBUG("session closed.");
391 DEBUG("unlock private lock.");
392 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200393 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100394 DEBUG("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200395 }
396
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100397 DEBUG("unlock session lock.");
398 DEBUG("closed session, disabled notif(?), wait 2s");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200399 usleep(500000); /* let notification thread stop */
400
401 /* session shouldn't be used by now */
402 /** \todo free all notifications from queue */
403 apr_array_clear(locked_session->notifications);
404 pthread_mutex_destroy(&locked_session->lock);
405 if (locked_session->hello_message != NULL) {
406 /** \todo free hello_message */
407 //json_object_put(locked_session->hello_message);
408 locked_session->hello_message = NULL;
409 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200410 locked_session->session = NULL;
Tomas Cejkaaecc5f72013-10-01 00:03:50 +0200411 free(locked_session);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200412 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100413 DEBUG("NETCONF session closed, everything cleared.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200414 return (EXIT_SUCCESS);
415}
416
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100417static int netconf_close(const char* session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +0200418{
David Kupka8e60a372012-09-04 09:15:20 +0200419 struct session_with_mutex * locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +0200420
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100421 DEBUG("Key in hash to close: %s", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200422
David Kupka8e60a372012-09-04 09:15:20 +0200423 /* get exclusive (write) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100424 DEBUG("lock session lock.");
425 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200426 if (pthread_rwlock_wrlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100427 DEBUG("Error while locking rwlock");
428 (*reply) = create_error("Internal: Error while locking.");
David Kupka8e60a372012-09-04 09:15:20 +0200429 return EXIT_FAILURE;
430 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200431 /* get session to close */
432 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
433 /* remove session from the active sessions list -> nobody new can now work with session */
434 apr_hash_set(netconf_sessions_list, session_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200435
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100436 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200437 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100438 DEBUG("Error while unlocking rwlock");
439 (*reply) = create_error("Internal: Error while unlocking.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200440 }
441
442 if ((locked_session != NULL) && (locked_session->session != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100443 return close_and_free_session(locked_session);
Radek Krejci469aab82012-07-22 18:42:20 +0200444 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100445 DEBUG("Unknown session to close");
446 (*reply) = create_error("Internal: Unkown session to close.");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200447 return (EXIT_FAILURE);
Radek Krejci469aab82012-07-22 18:42:20 +0200448 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100449 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +0200450}
451
Tomas Cejkac7929632013-10-24 19:25:15 +0200452/**
453 * Test reply message type and return error message.
454 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200455 * \param[in] session nc_session internal struct
456 * \param[in] session_key session key, NULL to disable disconnect on error
457 * \param[in] msgt RPC-REPLY message type
458 * \param[out] data
459 * \return NULL on success
460 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100461json_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 +0200462{
463 NC_REPLY_TYPE replyt;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100464 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200465
466 /* process the result of the operation */
467 switch (msgt) {
468 case NC_MSG_UNKNOWN:
469 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100470 DEBUG("mod_netconf: receiving rpc-reply failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200471 if (session_key != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100472 netconf_close(session_key, &err);
473 }
474 if (err != NULL) {
475 return err;
Tomas Cejkac7929632013-10-24 19:25:15 +0200476 }
477 return create_error("Internal: Receiving RPC-REPLY failed.");
478 }
479 case NC_MSG_NONE:
480 /* there is error handled by callback */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100481 if (data != NULL) {
482 free(*data);
483 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200484 (*data) = NULL;
485 return NULL;
486 case NC_MSG_REPLY:
487 switch (replyt = nc_reply_get_type(reply)) {
488 case NC_REPLY_OK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100489 if ((data != NULL) && (*data != NULL)) {
490 free(*data);
491 (*data) = NULL;
492 }
493 return create_ok();
Tomas Cejkac7929632013-10-24 19:25:15 +0200494 case NC_REPLY_DATA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100495 if (((*data) = nc_reply_get_data(reply)) == NULL) {
496 DEBUG("mod_netconf: no data from reply");
Tomas Cejkac7929632013-10-24 19:25:15 +0200497 return create_error("Internal: No data from reply received.");
498 } else {
499 return NULL;
500 }
501 break;
502 case NC_REPLY_ERROR:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100503 DEBUG("mod_netconf: unexpected rpc-reply (%d)", replyt);
504 if (data != NULL) {
505 free(*data);
506 (*data) = NULL;
507 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200508 return create_error(nc_reply_get_errormsg(reply));
509 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100510 DEBUG("mod_netconf: unexpected rpc-reply (%d)", replyt);
511 if (data != NULL) {
512 free(*data);
513 (*data) = NULL;
514 }
Tomas Cejka60885252014-03-26 15:45:47 +0100515 return create_error("Unknown type of NETCONF reply.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200516 }
517 break;
518 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100519 DEBUG("mod_netconf: unexpected reply message received (%d)", msgt);
520 if (data != NULL) {
521 free(*data);
522 (*data) = NULL;
523 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200524 return create_error("Internal: Unexpected RPC-REPLY message type.");
525 }
526}
527
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100528json_object *netconf_unlocked_op(struct nc_session *session, nc_rpc* rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +0200529{
530 nc_reply* reply = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200531 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200532
533 /* check requests */
534 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100535 DEBUG("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200536 return create_error("Internal error: RPC is not created");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200537 }
538
539 if (session != NULL) {
540 /* send the request and get the reply */
541 msgt = nc_session_send_recv(session, rpc, &reply);
542 /* process the result of the operation */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100543 return netconf_test_reply(session, NULL, msgt, reply, NULL);
Tomas Cejka6b886e02013-07-05 09:53:17 +0200544 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100545 DEBUG("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200546 return create_error("Internal error: Unknown session to process.");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200547 }
548}
549
Tomas Cejkac7929632013-10-24 19:25:15 +0200550/**
551 * Perform RPC method that returns data.
552 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200553 * \param[in] session_key session identifier
554 * \param[in] rpc RPC message to perform
555 * \param[out] received_data received data string, can be NULL when no data expected, value can be set to NULL if no data received
556 * \return NULL on success, json object with error otherwise
557 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100558static json_object *netconf_op(const char* session_key, nc_rpc* rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +0200559{
560 struct nc_session *session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200561 struct session_with_mutex * locked_session;
Tomas Cejka00635972013-06-03 15:10:52 +0200562 nc_reply* reply = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200563 json_object *res = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100564 char *data = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100565 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200566
Radek Krejci8e4632a2012-07-26 13:40:34 +0200567 /* check requests */
568 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100569 DEBUG("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200570 res = create_error("Internal: RPC could not be created.");
571 data = NULL;
572 goto finished;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200573 }
574
David Kupka8e60a372012-09-04 09:15:20 +0200575 /* get non-exclusive (read) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100576 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200577 if (pthread_rwlock_rdlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100578 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200579 res = create_error("Internal: Lock failed.");
580 data = NULL;
581 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200582 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200583 /* get session where send the RPC */
Tomas Cejka47387fd2013-06-10 20:37:46 +0200584 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 +0200585 if (locked_session != NULL) {
586 session = locked_session->session;
587 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200588 if (session != NULL) {
David Kupka8e60a372012-09-04 09:15:20 +0200589 /* get exclusive access to session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100590 DEBUG("LOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200591 if (pthread_mutex_lock(&locked_session->lock) != 0) {
592 /* unlock before returning error */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100593 DEBUG("UNLOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200594 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200595
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100596 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200597 res = create_error("Internal: Could not unlock.");
598 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200599 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200600 res = create_error("Internal: Could not unlock.");
David Kupka8e60a372012-09-04 09:15:20 +0200601 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100602 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200603 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200604
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100605 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200606 res = create_error("Internal: Could not unlock.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200607 }
608
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200609 locked_session->last_activity = apr_time_now();
Tomas Cejkac7929632013-10-24 19:25:15 +0200610
Radek Krejci035bf4e2012-07-25 10:59:09 +0200611 /* send the request and get the reply */
Radek Krejcia332b692012-11-12 16:15:54 +0100612 msgt = nc_session_send_recv(session, rpc, &reply);
613
David Kupka8e60a372012-09-04 09:15:20 +0200614 /* first release exclusive lock for this session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100615 DEBUG("UNLOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200616 pthread_mutex_unlock(&locked_session->lock);
617 /* end of critical section */
Radek Krejci035bf4e2012-07-25 10:59:09 +0200618
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100619 res = netconf_test_reply(session, session_key, msgt, reply, &data);
Radek Krejci035bf4e2012-07-25 10:59:09 +0200620 } else {
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100621 /* release lock on failure */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100622 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100623 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100624 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100625 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100626 DEBUG("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200627 res = create_error("Unknown session to process.");
628 data = NULL;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200629 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200630finished:
631 nc_reply_free(reply);
632 if (received_data != NULL) {
633 (*received_data) = data;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100634 } else {
635 if (data != NULL) {
636 free(data);
637 data = NULL;
638 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200639 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200640 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200641}
642
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100643static char* netconf_getconfig(const char* session_key, NC_DATASTORE source, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200644{
645 nc_rpc* rpc;
646 struct nc_filter *f = NULL;
647 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200648 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200649
650 /* create filter if set */
651 if (filter != NULL) {
652 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
653 }
654
655 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100656 rpc = nc_rpc_getconfig (source, f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200657 nc_filter_free(f);
658 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100659 DEBUG("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200660 return (NULL);
661 }
662
Tomas Cejka94674662013-09-13 15:55:24 +0200663 /* tell server to show all elements even if they have default values */
Tomas Cejka1c3840d2014-01-29 21:08:23 +0100664 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100665 DEBUG("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200666 }
667
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100668 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200669 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100670 if (res != NULL) {
671 (*err) = res;
672 } else {
673 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200674 }
675
Radek Krejci8e4632a2012-07-26 13:40:34 +0200676 return (data);
677}
678
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100679static 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 +0100680{
681 nc_rpc* rpc;
682 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200683 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100684
685 /* create requests */
Tomas Cejka94da2c52013-01-08 18:20:30 +0100686 rpc = nc_rpc_getschema(identifier, version, format);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100687 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100688 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100689 return (NULL);
690 }
691
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100692 res = netconf_op(session_key, rpc, &data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100693 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100694 if (res != NULL) {
695 (*err) = res;
696 } else {
697 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200698 }
699
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100700 return (data);
701}
702
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100703static char* netconf_get(const char* session_key, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200704{
705 nc_rpc* rpc;
706 struct nc_filter *f = NULL;
707 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200708 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200709
710 /* create filter if set */
711 if (filter != NULL) {
712 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
713 }
714
715 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100716 rpc = nc_rpc_get (f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200717 nc_filter_free(f);
718 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100719 DEBUG("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200720 return (NULL);
721 }
722
Tomas Cejka94674662013-09-13 15:55:24 +0200723 /* tell server to show all elements even if they have default values */
Tomas Cejka1c3840d2014-01-29 21:08:23 +0100724 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100725 DEBUG("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200726 }
727
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100728 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200729 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200730 if (res == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100731 (*err) = res;
732 } else {
733 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200734 }
735
Radek Krejci8e4632a2012-07-26 13:40:34 +0200736 return (data);
737}
738
Tomas Cejkab4d05872014-02-14 22:44:38 +0100739static 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 +0200740{
741 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200742 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200743
744 /* create requests */
Tomas Cejkab4d05872014-02-14 22:44:38 +0100745 if (source == NC_DATASTORE_CONFIG) {
Tomas Cejka5064c232013-01-17 09:30:58 +0100746 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100747 /* config, url */
748 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100749 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100750 /* config, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100751 rpc = nc_rpc_copyconfig(source, target, config);
752 }
Tomas Cejkab4d05872014-02-14 22:44:38 +0100753 } else if (source == NC_DATASTORE_URL) {
754 if (target == NC_DATASTORE_URL) {
755 /* url, url */
756 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
757 } else {
758 /* url, datastore */
759 rpc = nc_rpc_copyconfig(source, target, uri_src);
760 }
Tomas Cejka5064c232013-01-17 09:30:58 +0100761 } else {
762 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100763 /* datastore, url */
764 rpc = nc_rpc_copyconfig(source, target, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100765 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100766 /* datastore, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100767 rpc = nc_rpc_copyconfig(source, target);
768 }
769 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200770 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100771 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200772 return create_error("Internal: Creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200773 }
774
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100775 res = netconf_op(session_key, rpc, NULL);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200776 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200777
778 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200779}
Radek Krejci035bf4e2012-07-25 10:59:09 +0200780
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100781static 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 +0200782{
783 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200784 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200785
786 /* create requests */
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100787 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200788 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100789 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200790 return create_error("Internal: Creating rpc request failed");
Radek Krejci62ab34b2012-07-26 13:42:05 +0200791 }
792
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100793 res = netconf_op(session_key, rpc, NULL);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200794 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200795
796 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200797}
798
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100799static json_object *netconf_killsession(const char* session_key, const char* sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200800{
801 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200802 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200803
804 /* create requests */
805 rpc = nc_rpc_killsession(sid);
806 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100807 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200808 return create_error("Internal: Creating rpc request failed");
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200809 }
810
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100811 res = netconf_op(session_key, rpc, NULL);
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200812 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200813 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200814}
815
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100816static json_object *netconf_onlytargetop(const char* session_key, NC_DATASTORE target, nc_rpc* (*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +0200817{
818 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200819 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +0200820
821 /* create requests */
Radek Krejci5cd7d422012-07-26 14:50:29 +0200822 rpc = op_func(target);
Radek Krejci2f318372012-07-26 14:22:35 +0200823 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100824 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200825 return create_error("Internal: Creating rpc request failed");
Radek Krejci2f318372012-07-26 14:22:35 +0200826 }
827
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100828 res = netconf_op(session_key, rpc, NULL);
Radek Krejci2f318372012-07-26 14:22:35 +0200829 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200830 return res;
Radek Krejci2f318372012-07-26 14:22:35 +0200831}
832
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100833static json_object *netconf_deleteconfig(const char* session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200834{
Tomas Cejka404d37e2013-04-13 02:31:35 +0200835 nc_rpc *rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200836 json_object *res = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +0200837 if (target != NC_DATASTORE_URL) {
838 rpc = nc_rpc_deleteconfig(target);
839 } else {
Tomas Cejkac7929632013-10-24 19:25:15 +0200840 rpc = nc_rpc_deleteconfig(target, url);
841 }
842 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100843 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200844 return create_error("Internal: Creating rpc request failed");
Tomas Cejka404d37e2013-04-13 02:31:35 +0200845 }
846
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100847 res = netconf_op(session_key, rpc, NULL);
Tomas Cejkac7929632013-10-24 19:25:15 +0200848 nc_rpc_free (rpc);
849 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +0200850}
851
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100852static json_object *netconf_lock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200853{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100854 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200855}
856
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100857static json_object *netconf_unlock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200858{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100859 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200860}
861
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100862static json_object *netconf_generic(const char* session_key, const char* content, char** data)
Radek Krejci80c10d92012-07-30 08:38:50 +0200863{
Tomas Cejka00635972013-06-03 15:10:52 +0200864 nc_rpc* rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200865 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +0200866
867 /* create requests */
868 rpc = nc_rpc_generic(content);
869 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100870 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200871 return create_error("Internal: Creating rpc request failed");
Radek Krejci80c10d92012-07-30 08:38:50 +0200872 }
873
Radek Krejcia332b692012-11-12 16:15:54 +0100874 if (data != NULL) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200875 // TODO ?free(*data);
876 (*data) = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100877 }
Radek Krejci80c10d92012-07-30 08:38:50 +0200878
879 /* get session where send the RPC */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100880 res = netconf_op(session_key, rpc, data);
Tomas Cejkac7929632013-10-24 19:25:15 +0200881 nc_rpc_free (rpc);
882 return res;
Radek Krejci80c10d92012-07-30 08:38:50 +0200883}
884
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200885/**
886 * @}
887 *//* netconf_operations */
888
Radek Krejci7338bde2012-08-10 12:57:30 +0200889void clb_print(NC_VERB_LEVEL level, const char* msg)
Radek Krejci469aab82012-07-22 18:42:20 +0200890{
Radek Krejci7338bde2012-08-10 12:57:30 +0200891 switch (level) {
892 case NC_VERB_ERROR:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100893 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200894 break;
895 case NC_VERB_WARNING:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100896 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200897 break;
898 case NC_VERB_VERBOSE:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100899 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200900 break;
901 case NC_VERB_DEBUG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100902 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200903 break;
904 }
Radek Krejci469aab82012-07-22 18:42:20 +0200905}
906
Tomas Cejka64b87482013-06-03 16:30:53 +0200907/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +0200908 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +0200909 * Caller should free message memory.
910 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +0200911 * \return pointer to message
912 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100913char *get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +0200914{
915 /* read json in chunked framing */
916 unsigned int buffer_size = 0;
917 ssize_t buffer_len = 0;
918 char *buffer = NULL;
919 char c;
920 ssize_t ret;
921 int i, chunk_len;
922 char chunk_len_str[12];
923
924 while (1) {
925 /* read chunk length */
926 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
927 if (buffer != NULL) {
928 free (buffer);
929 buffer = NULL;
930 }
931 break;
932 }
933 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
934 if (buffer != NULL) {
935 free (buffer);
936 buffer = NULL;
937 }
938 break;
939 }
940 i=0;
941 memset (chunk_len_str, 0, 12);
942 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
943 if (i==0 && c == '#') {
944 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
945 /* end but invalid */
946 if (buffer != NULL) {
947 free (buffer);
948 buffer = NULL;
949 }
950 }
951 /* end of message, double-loop break */
952 goto msg_complete;
953 }
954 chunk_len_str[i++] = c;
955 if (i==11) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100956 DEBUG("Message is too long, buffer for length is not big enought!!!!");
Tomas Cejka64b87482013-06-03 16:30:53 +0200957 break;
958 }
959 }
960 if (c != '\n') {
961 if (buffer != NULL) {
962 free (buffer);
963 buffer = NULL;
964 }
965 break;
966 }
967 chunk_len_str[i] = 0;
968 if ((chunk_len = atoi (chunk_len_str)) == 0) {
969 if (buffer != NULL) {
970 free (buffer);
971 buffer = NULL;
972 }
973 break;
974 }
975 buffer_size += chunk_len+1;
976 buffer = realloc (buffer, sizeof(char)*buffer_size);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100977 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
Tomas Cejka64b87482013-06-03 16:30:53 +0200978 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
979 if (buffer != NULL) {
980 free (buffer);
981 buffer = NULL;
982 }
983 break;
984 }
985 buffer_len += ret;
986 }
987msg_complete:
988 return buffer;
989}
990
Tomas Cejkad5b53772013-06-08 23:01:07 +0200991NC_DATASTORE parse_datastore(const char *ds)
992{
993 if (strcmp(ds, "running") == 0) {
994 return NC_DATASTORE_RUNNING;
995 } else if (strcmp(ds, "startup") == 0) {
996 return NC_DATASTORE_STARTUP;
997 } else if (strcmp(ds, "candidate") == 0) {
998 return NC_DATASTORE_CANDIDATE;
Tomas Cejka4003a702013-10-01 00:02:45 +0200999 } else if (strcmp(ds, "url") == 0) {
1000 return NC_DATASTORE_URL;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001001 } else if (strcmp(ds, "config") == 0) {
1002 return NC_DATASTORE_CONFIG;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001003 }
1004 return -1;
1005}
1006
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001007NC_EDIT_TESTOPT_TYPE parse_testopt(const char *t)
1008{
1009 if (strcmp(t, "notset") == 0) {
1010 return NC_EDIT_TESTOPT_NOTSET;
1011 } else if (strcmp(t, "testset") == 0) {
1012 return NC_EDIT_TESTOPT_TESTSET;
1013 } else if (strcmp(t, "set") == 0) {
1014 return NC_EDIT_TESTOPT_SET;
1015 } else if (strcmp(t, "test") == 0) {
1016 return NC_EDIT_TESTOPT_TEST;
1017 }
1018 return NC_EDIT_TESTOPT_ERROR;
1019}
1020
Tomas Cejkad5b53772013-06-08 23:01:07 +02001021json_object *create_error(const char *errmess)
1022{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001023 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001024 json_object *reply = json_object_new_object();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001025 json_object *array = json_object_new_array();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001026 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001027 json_object_array_add(array, json_object_new_string(errmess));
1028 json_object_object_add(reply, "errors", array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001029 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001030 return reply;
1031
1032}
1033
1034json_object *create_data(const char *data)
1035{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001036 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001037 json_object *reply = json_object_new_object();
1038 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1039 json_object_object_add(reply, "data", json_object_new_string(data));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001040 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001041 return reply;
1042}
1043
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001044json_object *create_ok()
1045{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001046 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001047 json_object *reply = json_object_new_object();
1048 reply = json_object_new_object();
1049 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001050 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001051 return reply;
1052}
1053
1054json_object *handle_op_connect(apr_pool_t *pool, json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001055{
1056 const char *host = NULL;
1057 const char *port = NULL;
1058 const char *user = NULL;
1059 const char *pass = NULL;
1060 json_object *capabilities = NULL;
1061 json_object *reply = NULL;
1062 char *session_key_hash = NULL;
1063 struct nc_cpblts* cpblts = NULL;
1064 unsigned int len, i;
1065
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001066 DEBUG("Request: Connect");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001067 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001068 host = json_object_get_string(json_object_object_get((json_object *) request, "host"));
1069 port = json_object_get_string(json_object_object_get((json_object *) request, "port"));
1070 user = json_object_get_string(json_object_object_get((json_object *) request, "user"));
1071 pass = json_object_get_string(json_object_object_get((json_object *) request, "pass"));
1072
1073 capabilities = json_object_object_get((json_object *) request, "capabilities");
1074 if ((capabilities != NULL) && ((len = json_object_array_length(capabilities)) > 0)) {
1075 cpblts = nc_cpblts_new(NULL);
1076 for (i=0; i<len; i++) {
1077 nc_cpblts_add(cpblts, json_object_get_string(json_object_array_get_idx(capabilities, i)));
1078 }
1079 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001080 DEBUG("no capabilities specified");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001081 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001082 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001083
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001084 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001085 if ((host == NULL) || (user == NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001086 DEBUG("Cannot connect - insufficient input.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001087 session_key_hash = NULL;
1088 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001089 session_key_hash = netconf_connect(pool, host, port, user, pass, cpblts);
1090 DEBUG("hash: %s", session_key_hash);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001091 }
1092 if (cpblts != NULL) {
1093 nc_cpblts_free(cpblts);
1094 }
1095
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001096 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001097 if (session_key_hash == NULL) {
1098 /* negative reply */
1099 if (err_reply == NULL) {
1100 reply = json_object_new_object();
1101 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1102 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001103 DEBUG("Connection failed.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001104 } else {
1105 /* use filled err_reply from libnetconf's callback */
1106 reply = err_reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001107 DEBUG("Connect - error from libnetconf's callback.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001108 }
1109 } else {
1110 /* positive reply */
1111 reply = json_object_new_object();
1112 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1113 json_object_object_add(reply, "session", json_object_new_string(session_key_hash));
1114
1115 free(session_key_hash);
1116 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001117 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001118 return reply;
1119}
1120
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001121json_object *handle_op_get(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001122{
1123 const char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001124 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001125 json_object *reply = NULL;
1126
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001127 DEBUG("Request: get (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001128
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001129 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001130 filter = json_object_get_string(json_object_object_get(request, "filter"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001131 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001132
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001133 if ((data = netconf_get(session_key, filter, &reply)) == NULL) {
1134 if (reply == NULL) {
1135 if (err_reply == NULL) {
1136 reply = create_error("Get information failed.");
1137 } else {
1138 /* use filled err_reply from libnetconf's callback */
1139 reply = err_reply;
1140 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001141 }
1142 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001143 reply = create_data(data);
1144 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001145 }
1146 return reply;
1147}
1148
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001149json_object *handle_op_getconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001150{
1151 NC_DATASTORE ds_type_s = -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001152 const char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001153 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001154 const char *source = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001155 json_object *reply = NULL;
1156
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001157 DEBUG("Request: get-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001158
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001159 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001160 filter = json_object_get_string(json_object_object_get(request, "filter"));
1161
Tomas Cejkad5b53772013-06-08 23:01:07 +02001162 if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
1163 ds_type_s = parse_datastore(source);
1164 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001165 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001166 if (ds_type_s == -1) {
1167 return create_error("Invalid source repository type requested.");
1168 }
1169
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001170 if ((data = netconf_getconfig(session_key, ds_type_s, filter, &reply)) == NULL) {
1171 if (reply == NULL) {
1172 if (err_reply == NULL) {
1173 reply = create_error("Get configuration operation failed.");
1174 } else {
1175 /* use filled err_reply from libnetconf's callback */
1176 reply = err_reply;
1177 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001178 }
1179 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001180 reply = create_data(data);
1181 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001182 }
1183 return reply;
1184}
1185
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001186json_object *handle_op_getschema(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001187{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001188 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001189 const char *identifier = NULL;
1190 const char *version = NULL;
1191 const char *format = NULL;
1192 json_object *reply = NULL;
1193
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001194 DEBUG("Request: get-schema (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001195 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001196 identifier = json_object_get_string(json_object_object_get(request, "identifier"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001197 version = json_object_get_string(json_object_object_get(request, "version"));
1198 format = json_object_get_string(json_object_object_get(request, "format"));
1199 pthread_mutex_lock(&json_lock);
1200
Tomas Cejkad5b53772013-06-08 23:01:07 +02001201 if (identifier == NULL) {
1202 return create_error("No identifier for get-schema supplied.");
1203 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001204
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001205 DEBUG("get-schema(version: %s, format: %s)", version, format);
1206 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
1207 if (reply == NULL) {
1208 if (err_reply == NULL) {
1209 reply = create_error("Get models operation failed.");
1210 } else {
1211 /* use filled err_reply from libnetconf's callback */
1212 reply = err_reply;
1213 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001214 }
1215 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001216 reply = create_data(data);
1217 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001218 }
1219 return reply;
1220}
1221
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001222json_object *handle_op_editconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001223{
1224 NC_DATASTORE ds_type_s = -1;
1225 NC_DATASTORE ds_type_t = -1;
1226 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
1227 NC_EDIT_ERROPT_TYPE erropt_type = 0;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001228 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001229 const char *defop = NULL;
1230 const char *erropt = NULL;
1231 const char *config = NULL;
1232 const char *source = NULL;
1233 const char *target = NULL;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001234 const char *testopt = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001235 json_object *reply = NULL;
1236
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001237 DEBUG("Request: edit-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001238
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001239 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001240 defop = json_object_get_string(json_object_object_get(request, "default-operation"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001241 erropt = json_object_get_string(json_object_object_get(request, "error-option"));
1242 /* get parameters */
1243 if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
1244 ds_type_t = parse_datastore(target);
1245 }
1246 if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
1247 ds_type_s = parse_datastore(source);
1248 } else {
1249 /* source is optional, default value is config */
1250 ds_type_s = NC_DATASTORE_CONFIG;
1251 }
1252 config = json_object_get_string(json_object_object_get(request, "config"));
1253 testopt = json_object_get_string(json_object_object_get(request, "test-option"));
1254 pthread_mutex_unlock(&json_lock);
1255
Tomas Cejkad5b53772013-06-08 23:01:07 +02001256 if (defop != NULL) {
1257 if (strcmp(defop, "merge") == 0) {
1258 defop_type = NC_EDIT_DEFOP_MERGE;
1259 } else if (strcmp(defop, "replace") == 0) {
1260 defop_type = NC_EDIT_DEFOP_REPLACE;
1261 } else if (strcmp(defop, "none") == 0) {
1262 defop_type = NC_EDIT_DEFOP_NONE;
1263 } else {
1264 return create_error("Invalid default-operation parameter.");
1265 }
1266 } else {
1267 defop_type = NC_EDIT_DEFOP_NOTSET;
1268 }
1269
Tomas Cejkad5b53772013-06-08 23:01:07 +02001270 if (erropt != NULL) {
1271 if (strcmp(erropt, "continue-on-error") == 0) {
1272 erropt_type = NC_EDIT_ERROPT_CONT;
1273 } else if (strcmp(erropt, "stop-on-error") == 0) {
1274 erropt_type = NC_EDIT_ERROPT_STOP;
1275 } else if (strcmp(erropt, "rollback-on-error") == 0) {
1276 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
1277 } else {
1278 return create_error("Invalid error-option parameter.");
1279 }
1280 } else {
1281 erropt_type = 0;
1282 }
1283
Tomas Cejkad5b53772013-06-08 23:01:07 +02001284 if (ds_type_t == -1) {
1285 return create_error("Invalid target repository type requested.");
1286 }
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001287 if (ds_type_s == NC_DATASTORE_CONFIG) {
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001288 if (config == NULL) {
1289 return create_error("Invalid config data parameter.");
1290 }
1291 } else if (ds_type_s == NC_DATASTORE_URL){
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001292 if (config == NULL) {
1293 config = "";
1294 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001295 }
1296
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001297 if (testopt != NULL) {
1298 testopt_type = parse_testopt(testopt);
1299 } else {
1300 testopt_type = NC_EDIT_TESTOPT_TESTSET;
1301 }
1302
1303 reply = netconf_editconfig(session_key, ds_type_s, ds_type_t, defop_type, erropt_type, testopt_type, config);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001304 if (reply == NULL) {
1305 if (err_reply != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001306 /* use filled err_reply from libnetconf's callback */
1307 reply = err_reply;
1308 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001309 }
1310 return reply;
1311}
1312
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001313json_object *handle_op_copyconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001314{
1315 NC_DATASTORE ds_type_s = -1;
1316 NC_DATASTORE ds_type_t = -1;
1317 const char *config = NULL;
1318 const char *target = NULL;
1319 const char *source = NULL;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001320 const char *uri_src = NULL;
1321 const char *uri_trg = NULL;
1322
Tomas Cejkad5b53772013-06-08 23:01:07 +02001323 json_object *reply = NULL;
1324
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001325 DEBUG("Request: copy-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001326
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001327 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001328 /* get parameters */
1329 if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
1330 ds_type_t = parse_datastore(target);
1331 }
1332 if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
1333 ds_type_s = parse_datastore(source);
1334 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001335 config = json_object_get_string(json_object_object_get(request, "config"));
1336 uri_src = json_object_get_string(json_object_object_get(request, "uri-source"));
1337 uri_trg = json_object_get_string(json_object_object_get(request, "uri-target"));
1338 pthread_mutex_unlock(&json_lock);
1339
Tomas Cejkad5b53772013-06-08 23:01:07 +02001340 if (source == NULL) {
1341 /* no explicit source specified -> use config data */
1342 ds_type_s = NC_DATASTORE_CONFIG;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001343 } else if (ds_type_s == -1) {
1344 /* source datastore specified, but it is invalid */
1345 return create_error("Invalid source repository type requested.");
1346 }
1347
1348 if (ds_type_t == -1) {
1349 /* invalid target datastore specified */
1350 return create_error("Invalid target repository type requested.");
1351 }
1352
Tomas Cejka55c6df52014-02-20 12:59:33 +01001353 /* source can be missing when config is given */
1354 if (source == NULL && config == NULL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001355 reply = create_error("invalid input parameters - source and config is required.");
1356 return reply;
1357 }
1358
1359 if (ds_type_s == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001360 if (uri_src == NULL) {
1361 uri_src = "";
1362 }
1363 }
1364 if (ds_type_t == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001365 if (uri_trg == NULL) {
1366 uri_trg = "";
1367 }
1368 }
1369 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
1370 if (reply == NULL) {
1371 if (err_reply != NULL) {
1372 /* use filled err_reply from libnetconf's callback */
1373 reply = err_reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001374 }
1375 }
1376 return reply;
1377}
1378
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001379json_object *handle_op_generic(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001380{
1381 json_object *reply = NULL;
1382 const char *config = NULL;
1383 char *data = NULL;
1384
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001385 DEBUG("Request: generic request for session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001386
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001387 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001388 config = json_object_get_string(json_object_object_get(request, "content"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001389 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001390
1391 if (config == NULL) {
1392 return create_error("Missing content parameter.");
1393 }
1394
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001395 /* TODO */
1396 reply = netconf_generic(session_key, config, &data);
1397 if (reply == NULL) {
1398 if (err_reply != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001399 /* use filled err_reply from libnetconf's callback */
1400 reply = err_reply;
1401 }
1402 } else {
1403 if (data == NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001404 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001405 reply = json_object_new_object();
1406 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001407 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001408 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001409 reply = create_data(data);
1410 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001411 }
1412 }
1413 return reply;
1414}
1415
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001416json_object *handle_op_disconnect(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001417{
1418 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001419 DEBUG("Request: Disconnect session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001420
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001421 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
1422 if (reply == NULL) {
1423 if (err_reply == NULL) {
1424 reply = create_error("Get configuration information from device failed.");
1425 } else {
1426 /* use filled err_reply from libnetconf's callback */
1427 reply = err_reply;
1428 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001429 }
1430 } else {
1431 reply = json_object_new_object();
1432 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1433 }
1434 return reply;
1435}
1436
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001437json_object *handle_op_kill(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001438{
1439 json_object *reply = NULL;
1440 const char *sid = NULL;
1441
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001442 DEBUG("Request: kill-session, session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001443
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001444 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001445 sid = json_object_get_string(json_object_object_get(request, "session-id"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001446 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001447
1448 if (sid == NULL) {
1449 return create_error("Missing session-id parameter.");
1450 }
1451
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001452 reply = netconf_killsession(session_key, sid);
1453 if (reply == NULL) {
1454 if (err_reply != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001455 /* use filled err_reply from libnetconf's callback */
1456 reply = err_reply;
1457 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001458 }
1459 return reply;
1460}
1461
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001462json_object *handle_op_reloadhello(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001463{
Tomas Cejka47387fd2013-06-10 20:37:46 +02001464 struct nc_session *temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001465 struct session_with_mutex * locked_session = NULL;
1466 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001467 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001468
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001469 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001470 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001471 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001472 return NULL;
1473 }
1474
Tomas Cejkad5b53772013-06-08 23:01:07 +02001475 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1476 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001477 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001478 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001479 DEBUG("creating temporal NC session.");
Tomas Cejka47387fd2013-06-10 20:37:46 +02001480 temp_session = nc_session_connect_channel(locked_session->session, NULL);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001481 if (temp_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001482 prepare_status_message(locked_session, temp_session);
1483 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001484 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001485 temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001486 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001487 DEBUG("Reload hello failed due to channel establishment");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001488 reply = create_error("Reload was unsuccessful, connection failed.");
1489 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001490 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001491 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001492 DEBUG("UNLOCK wrlock %s", __func__);
1493 if (pthread_rwlock_unlock(&session_lock) != 0) {
1494 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1495 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001496 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001497 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001498 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001499 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001500 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001501 reply = create_error("Invalid session identifier.");
1502 }
1503
1504 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
1505 reply = locked_session->hello_message;
1506 }
1507 return reply;
1508}
1509
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001510json_object *handle_op_info(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001511{
1512 json_object *reply = NULL;
Tomas Cejka47387fd2013-06-10 20:37:46 +02001513 struct session_with_mutex * locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001514 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001515
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001516 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001517 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001518 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001519 }
1520
Tomas Cejkad5b53772013-06-08 23:01:07 +02001521 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1522 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001523 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001524 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001525 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001526 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001527 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001528 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001529 if (locked_session->hello_message != NULL) {
1530 reply = locked_session->hello_message;
1531 } else {
1532 reply = create_error("Invalid session identifier.");
1533 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001534 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001535 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001536 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001537 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001538 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001539 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001540 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001541 reply = create_error("Invalid session identifier.");
1542 }
1543
Tomas Cejka47387fd2013-06-10 20:37:46 +02001544
Tomas Cejkad5b53772013-06-08 23:01:07 +02001545 return reply;
1546}
1547
Tomas Cejka6b886e02013-07-05 09:53:17 +02001548void notification_history(time_t eventtime, const char *content)
1549{
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001550 json_object *notif_history_array = (json_object *) pthread_getspecific(notif_history_key);
1551 if (notif_history_array == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001552 DEBUG("No list of notification history found.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001553 return;
1554 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001555 DEBUG("Got notification from history %lu.", (long unsigned) eventtime);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001556 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001557 json_object *notif = json_object_new_object();
1558 if (notif == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001559 DEBUG("Could not allocate memory for notification (json).");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001560 goto failed;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001561 }
1562 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
1563 json_object_object_add(notif, "content", json_object_new_string(content));
1564 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001565failed:
1566 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001567}
1568
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001569json_object *handle_op_ntfgethistory(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001570{
1571 json_object *reply = NULL;
1572 const char *sid = NULL;
1573 struct session_with_mutex *locked_session = NULL;
1574 struct nc_session *temp_session = NULL;
1575 nc_rpc *rpc = NULL;
1576 time_t start = 0;
1577 time_t stop = 0;
1578 int64_t from, to;
1579
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001580 DEBUG("Request: get notification history, session %s", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001581
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001582 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001583 sid = json_object_get_string(json_object_object_get(request, "session"));
1584 from = json_object_get_int64(json_object_object_get(request, "from"));
1585 to = json_object_get_int64(json_object_object_get(request, "to"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001586 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001587
1588 start = time(NULL) + from;
1589 stop = time(NULL) + to;
1590
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001591 DEBUG("notification history interval %li %li", (long int) from, (long int) to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001592
1593 if (sid == NULL) {
1594 return create_error("Missing session parameter.");
1595 }
1596
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001597 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001598 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001599 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001600 return NULL;
1601 }
1602
1603 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1604 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001605 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001606 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001607 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001608 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001609 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001610 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001611 DEBUG("creating temporal NC session.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001612 temp_session = nc_session_connect_channel(locked_session->session, NULL);
1613 if (temp_session != NULL) {
1614 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
1615 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001616 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001617 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001618 DEBUG("notifications: creating an rpc request failed.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001619 return create_error("notifications: creating an rpc request failed.");
1620 }
1621
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001622 DEBUG("Send NC subscribe.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001623 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001624 json_object *res = netconf_unlocked_op(temp_session, rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001625 if (res != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001626 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001627 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001628 DEBUG("Subscription RPC failed.");
Tomas Cejkac7929632013-10-24 19:25:15 +02001629 return res;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001630 }
1631 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
1632
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001633 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001634 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001635 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001636 pthread_mutex_lock(&ntf_history_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001637 pthread_mutex_lock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001638 json_object *notif_history_array = json_object_new_array();
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001639 pthread_mutex_unlock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001640 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001641 DEBUG("notif_history: cannot set thread-specific hash value.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001642 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001643
1644 ncntf_dispatch_receive(temp_session, notification_history);
1645
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001646 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001647 reply = json_object_new_object();
1648 json_object_object_add(reply, "notifications", notif_history_array);
1649 //json_object_put(notif_history_array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001650 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001651
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001652 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001653 pthread_mutex_unlock(&ntf_history_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001654 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001655 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001656 temp_session = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001657 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001658 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001659 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001660 DEBUG("Get history of notification failed due to channel establishment");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001661 reply = create_error("Get history of notification was unsuccessful, connection failed.");
1662 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001663 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001664 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001665 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001666 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001667 }
1668 reply = create_error("Invalid session identifier.");
1669 }
1670
Tomas Cejka4003a702013-10-01 00:02:45 +02001671 return reply;
1672}
1673
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001674json_object *handle_op_validate(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02001675{
1676 json_object *reply = NULL;
1677 const char *sid = NULL;
1678 const char *target = NULL;
1679 const char *url = NULL;
1680 struct session_with_mutex *locked_session = NULL;
1681 nc_rpc *rpc = NULL;
1682 NC_DATASTORE target_ds;
1683
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001684 DEBUG("Request: validate datastore, session %s", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02001685
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001686 pthread_mutex_lock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02001687 sid = json_object_get_string(json_object_object_get(request, "session"));
1688 target = json_object_get_string(json_object_object_get(request, "target"));
1689 url = json_object_get_string(json_object_object_get(request, "url"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001690 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02001691
1692
1693 if ((sid == NULL) || (target == NULL)) {
1694 return create_error("Missing session parameter.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001695 }
Tomas Cejka4003a702013-10-01 00:02:45 +02001696
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001697 /* validation */
1698 target_ds = parse_datastore(target);
1699 if (target_ds == NC_DATASTORE_URL) {
1700 if (url != NULL) {
1701 rpc = nc_rpc_validate(target_ds, url);
Tomas Cejka4003a702013-10-01 00:02:45 +02001702 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001703 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
Tomas Cejka4003a702013-10-01 00:02:45 +02001704 || (target_ds == NC_DATASTORE_CANDIDATE)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001705 rpc = nc_rpc_validate(target_ds);
1706 }
1707 if (rpc == NULL) {
1708 DEBUG("mod_netconf: creating rpc request failed");
1709 reply = create_error("Creation of RPC request failed.");
1710 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka4003a702013-10-01 00:02:45 +02001711 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001712 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02001713 }
1714
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001715 DEBUG("Request: validate datastore");
1716 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001717 if (err_reply == NULL) {
1718 DEBUG("Request: validation ok.");
1719 reply = create_ok();
1720 } else {
1721 /* use filled err_reply from libnetconf's callback */
1722 reply = err_reply;
1723 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001724 }
1725 nc_rpc_free (rpc);
1726
Tomas Cejka6b886e02013-07-05 09:53:17 +02001727 return reply;
1728}
1729
David Kupka8e60a372012-09-04 09:15:20 +02001730void * thread_routine (void * arg)
1731{
1732 void * retval = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001733 struct pollfd fds;
Tomas Cejka00635972013-06-03 15:10:52 +02001734 json_object *request = NULL;
1735 json_object *reply = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001736 int operation;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001737 int status = 0;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001738 const char *msgtext;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001739 const char *session_key;
1740 const char *target = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001741 const char *url = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001742 NC_DATASTORE ds_type_t = -1;
Tomas Cejka64b87482013-06-03 16:30:53 +02001743 char *chunked_out_msg = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001744 apr_pool_t * pool = ((struct pass_to_thread*)arg)->pool;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001745 //server_rec * server = ((struct pass_to_thread*)arg)->server;
David Kupka8e60a372012-09-04 09:15:20 +02001746 int client = ((struct pass_to_thread*)arg)->client;
1747
Tomas Cejka00635972013-06-03 15:10:52 +02001748 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001749
1750 while (!isterminated) {
1751 fds.fd = client;
1752 fds.events = POLLIN;
1753 fds.revents = 0;
1754
1755 status = poll(&fds, 1, 1000);
1756
1757 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
1758 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001759 //DEBUG("poll interrupted");
David Kupka8e60a372012-09-04 09:15:20 +02001760 continue;
1761 } else if (status < 0) {
1762 /* 0: poll time outed
1763 * close socket and ignore this request from the client, it can try it again
1764 * -1: poll failed
1765 * something wrong happend, close this socket and wait for another request
1766 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001767 //DEBUG("poll failed, status %d(%d: %s)", status, errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +02001768 close(client);
1769 break;
1770 }
1771 /* status > 0 */
1772
1773 /* check the status of the socket */
1774
1775 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
1776 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
1777 /* close client's socket (it's probably already closed by client */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001778 //DEBUG("socket error (%d)", fds.revents);
David Kupka8e60a372012-09-04 09:15:20 +02001779 close(client);
1780 break;
1781 }
1782
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001783 DEBUG("Get framed message...");
1784 buffer = get_framed_message(client);
David Kupka8e60a372012-09-04 09:15:20 +02001785
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001786 DEBUG("Check read buffer.");
David Kupka8e60a372012-09-04 09:15:20 +02001787 if (buffer != NULL) {
Tomas Cejka00635972013-06-03 15:10:52 +02001788 enum json_tokener_error jerr;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001789 pthread_mutex_lock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001790 request = json_tokener_parse_verbose(buffer, &jerr);
1791 if (jerr != json_tokener_success) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001792 DEBUG("JSON parsing error");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001793 pthread_mutex_unlock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001794 continue;
1795 }
David Kupka8e60a372012-09-04 09:15:20 +02001796
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001797 operation = json_object_get_int(json_object_object_get(request, "type"));
Tomas Cejka64b87482013-06-03 16:30:53 +02001798 session_key = json_object_get_string(json_object_object_get(request, "session"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001799 pthread_mutex_unlock(&json_lock);
1800
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001801 DEBUG("operation %d session_key %s.", operation, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001802 /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
1803 if (operation != MSG_CONNECT && session_key == NULL) {
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001804 reply = create_error("Missing session specification.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001805 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001806 msgtext = json_object_to_json_string(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001807 pthread_mutex_unlock(&json_lock);
1808
David Kupka8e60a372012-09-04 09:15:20 +02001809 send(client, msgtext, strlen(msgtext) + 1, 0);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001810
1811 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001812 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001813 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001814 /* there is some stupid client, so close the connection to give a chance to some other client */
1815 close(client);
1816 break;
1817 }
1818
David Kupka8e60a372012-09-04 09:15:20 +02001819 /* null global JSON error-reply */
1820 err_reply = NULL;
1821
1822 /* prepare reply envelope */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001823 if (reply != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001824 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001825 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001826 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001827 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001828 reply = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001829
1830 /* process required operation */
1831 switch (operation) {
1832 case MSG_CONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001833 reply = handle_op_connect(pool, request);
David Kupka8e60a372012-09-04 09:15:20 +02001834 break;
1835 case MSG_GET:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001836 reply = handle_op_get(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001837 break;
1838 case MSG_GETCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001839 reply = handle_op_getconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001840 break;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001841 case MSG_GETSCHEMA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001842 reply = handle_op_getschema(pool, request, session_key);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001843 break;
David Kupka8e60a372012-09-04 09:15:20 +02001844 case MSG_EDITCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001845 reply = handle_op_editconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001846 break;
1847 case MSG_COPYCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001848 reply = handle_op_copyconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001849 break;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001850
David Kupka8e60a372012-09-04 09:15:20 +02001851 case MSG_DELETECONFIG:
David Kupka8e60a372012-09-04 09:15:20 +02001852 case MSG_LOCK:
David Kupka8e60a372012-09-04 09:15:20 +02001853 case MSG_UNLOCK:
Tomas Cejkad5b53772013-06-08 23:01:07 +02001854 /* get parameters */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001855 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001856 if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
1857 ds_type_t = parse_datastore(target);
1858 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001859 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001860
1861 if (ds_type_t == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001862 reply = create_error("Invalid target repository type requested.");
David Kupka8e60a372012-09-04 09:15:20 +02001863 break;
1864 }
David Kupka8e60a372012-09-04 09:15:20 +02001865 switch(operation) {
1866 case MSG_DELETECONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001867 DEBUG("Request: delete-config (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001868 pthread_mutex_lock(&json_lock);
Tomas Cejkac7929632013-10-24 19:25:15 +02001869 url = json_object_get_string(json_object_object_get(request, "url"));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001870 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001871 reply = netconf_deleteconfig(session_key, ds_type_t, url);
David Kupka8e60a372012-09-04 09:15:20 +02001872 break;
1873 case MSG_LOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001874 DEBUG("Request: lock (session %s)", session_key);
1875 reply = netconf_lock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001876 break;
1877 case MSG_UNLOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001878 DEBUG("Request: unlock (session %s)", session_key);
1879 reply = netconf_unlock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001880 break;
1881 default:
Tomas Cejkac7929632013-10-24 19:25:15 +02001882 reply = create_error("Internal: Unknown request type.");
David Kupka8e60a372012-09-04 09:15:20 +02001883 break;
1884 }
1885
Tomas Cejkac7929632013-10-24 19:25:15 +02001886 if (reply == NULL) {
David Kupka8e60a372012-09-04 09:15:20 +02001887 if (err_reply == NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001888 /** \todo more clever error message wanted */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001889 pthread_mutex_lock(&json_lock);
Tomas Cejkac7929632013-10-24 19:25:15 +02001890 reply = json_object_new_object();
1891 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001892 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001893 } else {
1894 /* use filled err_reply from libnetconf's callback */
David Kupka8e60a372012-09-04 09:15:20 +02001895 reply = err_reply;
1896 }
David Kupka8e60a372012-09-04 09:15:20 +02001897 }
1898 break;
1899 case MSG_KILL:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001900 reply = handle_op_kill(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001901 break;
1902 case MSG_DISCONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001903 reply = handle_op_disconnect(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001904 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001905 case MSG_RELOADHELLO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001906 reply = handle_op_reloadhello(pool, request, session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001907 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001908 case MSG_INFO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001909 reply = handle_op_info(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001910 break;
1911 case MSG_GENERIC:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001912 reply = handle_op_generic(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001913 break;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001914 case MSG_NTF_GETHISTORY:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001915 reply = handle_op_ntfgethistory(pool, request, session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001916 break;
Tomas Cejka4003a702013-10-01 00:02:45 +02001917 case MSG_VALIDATE:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001918 reply = handle_op_validate(pool, request, session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02001919 break;
David Kupka8e60a372012-09-04 09:15:20 +02001920 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001921 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001922 reply = create_error("Operation not supported.");
David Kupka8e60a372012-09-04 09:15:20 +02001923 break;
1924 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001925 DEBUG("Clean request json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001926 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001927 json_object_put(request);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001928 DEBUG("Send reply json object.");
David Kupka1e3e4c82012-09-04 09:32:15 +02001929 /* send reply to caller */
1930 if (reply != NULL) {
1931 msgtext = json_object_to_json_string(reply);
Tomas Cejka64b87482013-06-03 16:30:53 +02001932 if (asprintf (&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
Tomas Cejka00635972013-06-03 15:10:52 +02001933 if (buffer != NULL) {
1934 free(buffer);
1935 buffer = NULL;
1936 }
David Kupka8e60a372012-09-04 09:15:20 +02001937 break;
1938 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001939 pthread_mutex_unlock(&json_lock);
1940
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001941 DEBUG("Send framed reply json object.");
Tomas Cejka64b87482013-06-03 16:30:53 +02001942 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001943 DEBUG("Clean reply json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001944 pthread_mutex_lock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02001945 json_object_put(reply);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001946 reply = NULL;
1947 DEBUG("Clean message buffer.");
Tomas Cejka64b87482013-06-03 16:30:53 +02001948 free(chunked_out_msg);
1949 chunked_out_msg = NULL;
Tomas Cejka00635972013-06-03 15:10:52 +02001950 if (buffer != NULL) {
1951 free(buffer);
1952 buffer = NULL;
1953 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001954 if (err_reply != NULL) {
1955 json_object_put(err_reply);
1956 err_reply = NULL;
1957 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001958 pthread_mutex_unlock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02001959 } else {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001960 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001961 DEBUG("Reply is NULL, shouldn't be...");
1962 continue;
David Kupka8e60a372012-09-04 09:15:20 +02001963 }
1964 }
1965 }
David Kupka8e60a372012-09-04 09:15:20 +02001966 free (arg);
1967
1968 return retval;
1969}
1970
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001971/**
1972 * \brief Close all open NETCONF sessions.
1973 *
1974 * During termination of mod_netconf, it is useful to close all remaining
1975 * sessions. This function iterates over the list of sessions and close them
1976 * all.
1977 *
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001978 * \param[in] p apr pool needed for hash table iterating
1979 * \param[in] ht hash table of session_with_mutex structs
1980 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001981static void close_all_nc_sessions(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001982{
1983 apr_hash_index_t *hi;
1984 void *val = NULL;
1985 struct session_with_mutex *swm = NULL;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001986 const char *hashed_key = NULL;
1987 apr_ssize_t hashed_key_length;
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001988 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001989
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001990 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001991 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001992 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001993 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001994 return;
1995 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001996 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001997 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001998 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001999 swm = (struct session_with_mutex *) val;
2000 if (swm != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002001 DEBUG("LOCK mutex %s", __func__);
2002 pthread_mutex_lock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002003 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002004 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002005 pthread_mutex_unlock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002006
2007 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002008 close_and_free_session(swm);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002009 }
2010 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002011 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002012 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002013 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002014 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002015 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002016}
2017
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002018static void check_timeout_and_close(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002019{
2020 apr_hash_index_t *hi;
2021 void *val = NULL;
2022 struct nc_session *ns = NULL;
2023 struct session_with_mutex *swm = NULL;
2024 const char *hashed_key = NULL;
2025 apr_ssize_t hashed_key_length;
2026 apr_time_t current_time = apr_time_now();
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002027 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002028
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002029 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002030//DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002031 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002032 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002033 return;
2034 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002035 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002036 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
2037 swm = (struct session_with_mutex *) val;
2038 if (swm == NULL) {
2039 continue;
2040 }
2041 ns = swm->session;
2042 if (ns == NULL) {
2043 continue;
2044 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002045//DEBUG("LOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002046 pthread_mutex_lock(&swm->lock);
2047 if ((current_time - swm->last_activity) > apr_time_from_sec(ACTIVITY_TIMEOUT)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002048 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002049 /* remove session from the active sessions list */
Tomas Cejka47387fd2013-06-10 20:37:46 +02002050 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002051//DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002052 pthread_mutex_unlock(&swm->lock);
2053
2054 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002055 close_and_free_session(swm);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002056 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002057//DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002058 pthread_mutex_unlock(&swm->lock);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002059 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002060 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002061 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002062//DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002063 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002064 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002065 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002066}
2067
2068
2069/**
Radek Krejcif23850c2012-07-23 16:14:17 +02002070 * This is actually implementation of NETCONF client
2071 * - requests are received from UNIX socket in the predefined format
2072 * - results are replied through the same way
2073 * - the daemon run as a separate process, but it is started and stopped
2074 * automatically by Apache.
2075 *
2076 */
Radek Krejci469aab82012-07-22 18:42:20 +02002077static void forked_proc(apr_pool_t * pool, server_rec * server)
2078{
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002079 struct timeval tv;
Radek Krejci469aab82012-07-22 18:42:20 +02002080 struct sockaddr_un local, remote;
David Kupka8e60a372012-09-04 09:15:20 +02002081 int lsock, client, ret, i, pthread_count = 0;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002082 unsigned int olds = 0, timediff = 0;
David Kupka8e60a372012-09-04 09:15:20 +02002083 socklen_t len;
Radek Krejciae021c12012-07-25 18:03:52 +02002084 mod_netconf_cfg *cfg;
David Kupka8e60a372012-09-04 09:15:20 +02002085 struct pass_to_thread * arg;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002086 pthread_t * ptids = calloc(1, sizeof(pthread_t));
David Kupka8e60a372012-09-04 09:15:20 +02002087 struct timespec maxtime;
2088 pthread_rwlockattr_t lock_attrs;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002089 char *sockname = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +02002090 #ifdef WITH_NOTIFICATIONS
2091 char use_notifications = 0;
2092 #endif
David Kupka8e60a372012-09-04 09:15:20 +02002093
Tomas Cejka6b886e02013-07-05 09:53:17 +02002094 http_server = server;
2095
Tomas Cejka404d37e2013-04-13 02:31:35 +02002096 /* wait at most 5 seconds for every thread to terminate */
David Kupka8e60a372012-09-04 09:15:20 +02002097 maxtime.tv_sec = 5;
2098 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02002099
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002100 #ifndef HTTPD_INDEPENDENT
Radek Krejcif23850c2012-07-23 16:14:17 +02002101 /* change uid and gid of process for security reasons */
Radek Krejci469aab82012-07-22 18:42:20 +02002102 unixd_setup_child();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002103 #endif
Radek Krejci469aab82012-07-22 18:42:20 +02002104
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002105 if (server != NULL) {
2106 cfg = ap_get_module_config(server->module_config, &netconf_module);
2107 if (cfg == NULL) {
2108 DEBUG("Getting mod_netconf configuration failed");
2109 return;
2110 }
2111 sockname = cfg->sockname;
2112 } else {
2113 sockname = SOCKET_FILENAME;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002114 }
Radek Krejci469aab82012-07-22 18:42:20 +02002115
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002116 /* create listening UNIX socket to accept incoming connections */
Radek Krejci469aab82012-07-22 18:42:20 +02002117 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002118 DEBUG("Creating socket failed (%s)", strerror(errno));
2119 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002120 }
2121
2122 local.sun_family = AF_UNIX;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002123 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
Radek Krejci469aab82012-07-22 18:42:20 +02002124 unlink(local.sun_path);
2125 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
2126
2127 if (bind(lsock, (struct sockaddr *) &local, len) == -1) {
2128 if (errno == EADDRINUSE) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002129 DEBUG("mod_netconf socket address already in use");
2130 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002131 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002132 DEBUG("Binding socket failed (%s)", strerror(errno));
2133 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002134 }
2135
2136 if (listen(lsock, MAX_SOCKET_CL) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002137 DEBUG("Setting up listen socket failed (%s)", strerror(errno));
2138 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002139 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002140 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02002141
Tomas Cejkaba21b382013-04-13 02:37:32 +02002142 /* prepare internal lists */
2143 netconf_sessions_list = apr_hash_make(pool);
2144
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002145 #ifdef WITH_NOTIFICATIONS
Tomas Cejka47387fd2013-06-10 20:37:46 +02002146 if (notification_init(pool, server) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002147 DEBUG("libwebsockets initialization failed");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002148 use_notifications = 0;
2149 } else {
2150 use_notifications = 1;
2151 }
2152 #endif
2153
Radek Krejci469aab82012-07-22 18:42:20 +02002154 /* setup libnetconf's callbacks */
2155 nc_verbosity(NC_VERB_DEBUG);
Radek Krejci469aab82012-07-22 18:42:20 +02002156 nc_callback_print(clb_print);
2157 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
2158 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
2159 nc_callback_sshauth_password(netconf_callback_sshauth_password);
Radek Krejcic11fd862012-07-26 12:41:21 +02002160 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02002161
2162 /* disable publickey authentication */
2163 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
2164
David Kupka8e60a372012-09-04 09:15:20 +02002165 /* create mutex protecting session list */
2166 pthread_rwlockattr_init(&lock_attrs);
2167 /* rwlock is shared only with threads in this process */
2168 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
2169 /* create rw lock */
2170 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002171 DEBUG("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
2172 goto error_exit;
David Kupka8e60a372012-09-04 09:15:20 +02002173 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002174 pthread_mutex_init(&ntf_history_lock, NULL);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002175 pthread_mutex_init(&json_lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002176 DEBUG("init of notif_history_key.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002177 if (pthread_key_create(&notif_history_key, NULL) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002178 DEBUG("init of notif_history_key failed");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002179 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02002180
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002181 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002182 while (isterminated == 0) {
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002183 gettimeofday(&tv, NULL);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002184 timediff = (unsigned int)tv.tv_sec - olds;
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002185 #ifdef WITH_NOTIFICATIONS
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002186 if (timediff > 60) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002187 DEBUG("handling notifications");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002188 }
2189 if (use_notifications == 1) {
2190 notification_handle();
2191 }
2192 #endif
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002193 if (timediff > ACTIVITY_CHECK_INTERVAL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002194 check_timeout_and_close(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002195 }
Radek Krejci469aab82012-07-22 18:42:20 +02002196
2197 /* open incoming connection if any */
David Kupka8e60a372012-09-04 09:15:20 +02002198 len = sizeof(remote);
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002199 if (((unsigned int)tv.tv_sec - olds) > 60) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002200 DEBUG("accepting another client");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002201 olds = tv.tv_sec;
2202 }
David Kupka8e60a372012-09-04 09:15:20 +02002203 client = accept(lsock, (struct sockaddr *) &remote, &len);
Radek Krejci469aab82012-07-22 18:42:20 +02002204 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
2205 apr_sleep(SLEEP_TIME);
2206 continue;
2207 } else if (client == -1 && (errno == EINTR)) {
2208 continue;
2209 } else if (client == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002210 DEBUG("Accepting mod_netconf client connection failed (%s)", strerror(errno));
Radek Krejci469aab82012-07-22 18:42:20 +02002211 continue;
2212 }
Radek Krejci469aab82012-07-22 18:42:20 +02002213
2214 /* set client's socket as non-blocking */
Radek Krejcif23850c2012-07-23 16:14:17 +02002215 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002216
David Kupka8e60a372012-09-04 09:15:20 +02002217 arg = malloc (sizeof(struct pass_to_thread));
2218 arg->client = client;
2219 arg->pool = pool;
2220 arg->server = server;
2221 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02002222
David Kupka8e60a372012-09-04 09:15:20 +02002223 /* start new thread. It will serve this particular request and then terminate */
2224 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void*)arg)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002225 DEBUG("Creating POSIX thread failed: %d\n", ret);
David Kupka8e60a372012-09-04 09:15:20 +02002226 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002227 DEBUG("Thread %lu created", ptids[pthread_count]);
David Kupka8e60a372012-09-04 09:15:20 +02002228 pthread_count++;
2229 ptids = realloc (ptids, sizeof(pthread_t)*(pthread_count+1));
2230 ptids[pthread_count] = 0;
2231 }
Radek Krejci469aab82012-07-22 18:42:20 +02002232
David Kupka8e60a372012-09-04 09:15:20 +02002233 /* check if some thread already terminated, free some resources by joining it */
2234 for (i=0; i<pthread_count; i++) {
2235 if (pthread_tryjoin_np (ptids[i], (void**)&arg) == 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002236 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
David Kupka8e60a372012-09-04 09:15:20 +02002237 pthread_count--;
2238 if (pthread_count > 0) {
2239 /* place last Thread ID on the place of joined one */
2240 ptids[i] = ptids[pthread_count];
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002241 }
Radek Krejci469aab82012-07-22 18:42:20 +02002242 }
2243 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002244 DEBUG("Running %d threads", pthread_count);
Radek Krejci469aab82012-07-22 18:42:20 +02002245 }
2246
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002247 DEBUG("mod_netconf terminating...");
David Kupka8e60a372012-09-04 09:15:20 +02002248 /* join all threads */
2249 for (i=0; i<pthread_count; i++) {
2250 pthread_timedjoin_np (ptids[i], (void**)&arg, &maxtime);
2251 }
Radek Krejci469aab82012-07-22 18:42:20 +02002252
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002253 #ifdef WITH_NOTIFICATIONS
2254 notification_close();
2255 #endif
2256
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002257 /* close all NETCONF sessions */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002258 close_all_nc_sessions(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002259
David Kupka8e60a372012-09-04 09:15:20 +02002260 /* destroy rwlock */
2261 pthread_rwlock_destroy(&session_lock);
2262 pthread_rwlockattr_destroy(&lock_attrs);
2263
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002264 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02002265
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002266 free(ptids);
2267 close(lsock);
Radek Krejci469aab82012-07-22 18:42:20 +02002268 exit(APR_SUCCESS);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002269 return;
2270error_exit:
2271 close(lsock);
2272 free(ptids);
2273 return;
Radek Krejci469aab82012-07-22 18:42:20 +02002274}
2275
Radek Krejcif23850c2012-07-23 16:14:17 +02002276static void *mod_netconf_create_conf(apr_pool_t * pool, server_rec * s)
Radek Krejci469aab82012-07-22 18:42:20 +02002277{
Radek Krejcif23850c2012-07-23 16:14:17 +02002278 mod_netconf_cfg *config = apr_pcalloc(pool, sizeof(mod_netconf_cfg));
2279 apr_pool_create(&config->pool, pool);
2280 config->forkproc = NULL;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002281 config->sockname = SOCKET_FILENAME;
Radek Krejcif23850c2012-07-23 16:14:17 +02002282
2283 return (void *)config;
Radek Krejci469aab82012-07-22 18:42:20 +02002284}
2285
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002286#ifndef HTTPD_INDEPENDENT
Radek Krejci469aab82012-07-22 18:42:20 +02002287static int mod_netconf_master_init(apr_pool_t * pconf, apr_pool_t * ptemp,
2288 apr_pool_t * plog, server_rec * s)
2289{
Radek Krejcif23850c2012-07-23 16:14:17 +02002290 mod_netconf_cfg *config;
2291 apr_status_t res;
2292
Radek Krejci469aab82012-07-22 18:42:20 +02002293 /* These two help ensure that we only init once. */
Radek Krejcia332b692012-11-12 16:15:54 +01002294 void *data = NULL;
Radek Krejcif23850c2012-07-23 16:14:17 +02002295 const char *userdata_key = "netconf_ipc_init";
Radek Krejci469aab82012-07-22 18:42:20 +02002296
2297 /*
2298 * The following checks if this routine has been called before.
2299 * This is necessary because the parent process gets initialized
2300 * a couple of times as the server starts up.
2301 */
2302 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2303 if (!data) {
2304 apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
2305 return (OK);
2306 }
2307
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002308 DEBUG("creating mod_netconf daemon");
Radek Krejcif23850c2012-07-23 16:14:17 +02002309 config = ap_get_module_config(s->module_config, &netconf_module);
Radek Krejcidfaa6ea2012-07-23 09:04:43 +02002310
Radek Krejcif23850c2012-07-23 16:14:17 +02002311 if (config && config->forkproc == NULL) {
2312 config->forkproc = apr_pcalloc(config->pool, sizeof(apr_proc_t));
2313 res = apr_proc_fork(config->forkproc, config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002314 switch (res) {
2315 case APR_INCHILD:
2316 /* set signal handler */
Radek Krejcif23850c2012-07-23 16:14:17 +02002317 apr_signal_init(config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002318 apr_signal(SIGTERM, signal_handler);
2319
2320 /* log start of the separated NETCONF communication process */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002321 DEBUG("mod_netconf daemon started (PID %d)", getpid());
Radek Krejci469aab82012-07-22 18:42:20 +02002322
2323 /* start main loop providing NETCONF communication */
Radek Krejcif23850c2012-07-23 16:14:17 +02002324 forked_proc(config->pool, s);
Radek Krejci469aab82012-07-22 18:42:20 +02002325
Radek Krejcif23850c2012-07-23 16:14:17 +02002326 /* I never should be here, wtf?!? */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002327 DEBUG("mod_netconf daemon unexpectedly stopped");
Radek Krejci469aab82012-07-22 18:42:20 +02002328 exit(APR_EGENERAL);
2329 break;
2330 case APR_INPARENT:
2331 /* register child to be killed (SIGTERM) when the module config's pool dies */
Radek Krejcif23850c2012-07-23 16:14:17 +02002332 apr_pool_note_subprocess(config->pool, config->forkproc, APR_KILL_AFTER_TIMEOUT);
Radek Krejci469aab82012-07-22 18:42:20 +02002333 break;
2334 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002335 DEBUG("apr_proc_fork() failed");
Radek Krejci469aab82012-07-22 18:42:20 +02002336 break;
2337 }
Radek Krejcif23850c2012-07-23 16:14:17 +02002338 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002339 DEBUG("mod_netconf misses configuration structure");
Radek Krejci469aab82012-07-22 18:42:20 +02002340 }
2341
2342 return OK;
2343}
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002344#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002345
Radek Krejci469aab82012-07-22 18:42:20 +02002346/**
2347 * Register module hooks
2348 */
2349static void mod_netconf_register_hooks(apr_pool_t * p)
2350{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002351#ifndef HTTPD_INDEPENDENT
Radek Krejcif23850c2012-07-23 16:14:17 +02002352 ap_hook_post_config(mod_netconf_master_init, NULL, NULL, APR_HOOK_LAST);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002353#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002354}
2355
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002356static const char* cfg_set_socket_path(cmd_parms* cmd, void* cfg, const char* arg)
2357{
2358 ((mod_netconf_cfg*)cfg)->sockname = apr_pstrdup(cmd->pool, arg);
2359 return NULL;
2360}
2361
2362static const command_rec netconf_cmds[] = {
2363 AP_INIT_TAKE1("NetconfSocket", cfg_set_socket_path, NULL, OR_ALL, "UNIX socket path for mod_netconf communication."),
2364 {NULL}
2365};
2366
Radek Krejci469aab82012-07-22 18:42:20 +02002367/* Dispatch list for API hooks */
2368module AP_MODULE_DECLARE_DATA netconf_module = {
2369 STANDARD20_MODULE_STUFF,
2370 NULL, /* create per-dir config structures */
2371 NULL, /* merge per-dir config structures */
Radek Krejcif23850c2012-07-23 16:14:17 +02002372 mod_netconf_create_conf, /* create per-server config structures */
Radek Krejci469aab82012-07-22 18:42:20 +02002373 NULL, /* merge per-server config structures */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002374 netconf_cmds, /* table of config file commands */
Radek Krejci469aab82012-07-22 18:42:20 +02002375 mod_netconf_register_hooks /* register hooks */
2376};
Radek Krejcia332b692012-11-12 16:15:54 +01002377
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002378int main(int argc, char **argv)
2379{
2380 apr_pool_t *pool;
2381 apr_app_initialize(&argc, (char const *const **) &argv, NULL);
2382 apr_signal(SIGTERM, signal_handler);
2383 apr_signal(SIGINT, signal_handler);
2384 apr_pool_create(&pool, NULL);
2385 forked_proc(pool, NULL);
2386 apr_pool_destroy(pool);
2387 apr_terminate();
2388 DEBUG("Terminated");
2389 return 0;
2390}