blob: 09ca131e1b325fd7b86f8684c4918d7943e9b54f [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>
Tomas Cejka04e08f42014-03-27 19:52:34 +010053#include <pwd.h>
54#include <grp.h>
David Kupka8e60a372012-09-04 09:15:20 +020055#include <pthread.h>
56#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020057
58#include <unixd.h>
59#include <httpd.h>
60#include <http_log.h>
61#include <http_config.h>
62
63#include <apr_sha1.h>
64#include <apr_hash.h>
65#include <apr_signal.h>
66#include <apr_strings.h>
Radek Krejci469aab82012-07-22 18:42:20 +020067
Radek Krejci8fd1f5e2012-07-24 17:33:36 +020068#include <json/json.h>
69
Radek Krejci469aab82012-07-22 18:42:20 +020070#include <libnetconf.h>
Tomas Cejkab3cc64f2013-05-03 19:44:54 +020071#include <libnetconf_ssh.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020072
Tomas Cejka04e08f42014-03-27 19:52:34 +010073#include "../config.h"
74
Tomas Cejkad340dbf2013-03-24 20:36:57 +010075#ifdef WITH_NOTIFICATIONS
76#include "notification_module.h"
77#endif
78
Tomas Cejka94da2c52013-01-08 18:20:30 +010079#include "message_type.h"
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020080#include "mod_netconf.h"
Radek Krejci469aab82012-07-22 18:42:20 +020081
82#define MAX_PROCS 5
Tomas Cejka86f0fc12014-09-17 15:09:38 +020083#define SOCKET_FILENAME "/var/run/mod_netconf.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020084#define MAX_SOCKET_CL 10
85#define BUFFER_SIZE 4096
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020086#define NOTIFICATION_QUEUE_SIZE 10
87#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
Tomas Cejka04e39952013-04-19 11:49:38 +020088#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020089
90/* sleep in master process for non-blocking socket reading */
91#define SLEEP_TIME 200
92
93#ifndef offsetof
94#define offsetof(type, member) ((size_t) ((type *) 0)->member)
95#endif
96
Tomas Cejkaef531ee2013-11-12 16:07:00 +010097server_rec *http_server = NULL;
98
Tomas Cejka027f3bc2012-11-10 20:28:36 +010099/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +0200100struct timeval timeout = { 1, 0 };
101
Tomas Cejka5064c232013-01-17 09:30:58 +0100102#define NCWITHDEFAULTS NCWD_MODE_NOTSET
103
104
Radek Krejci469aab82012-07-22 18:42:20 +0200105#define MSG_OK 0
106#define MSG_OPEN 1
107#define MSG_DATA 2
108#define MSG_CLOSE 3
109#define MSG_ERROR 4
110#define MSG_UNKNOWN 5
111
Radek Krejci469aab82012-07-22 18:42:20 +0200112module AP_MODULE_DECLARE_DATA netconf_module;
113
Tomas Cejka47387fd2013-06-10 20:37:46 +0200114pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200115pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100116pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100117pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
118
Tomas Cejka47387fd2013-06-10 20:37:46 +0200119apr_hash_t *netconf_sessions_list = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200120
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200121static pthread_key_t notif_history_key;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200122
Tomas Cejka442258e2014-04-01 18:17:18 +0200123pthread_key_t err_reply_key;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100124
Radek Krejci469aab82012-07-22 18:42:20 +0200125volatile int isterminated = 0;
126
127static char* password;
128
Radek Krejci469aab82012-07-22 18:42:20 +0200129static void signal_handler(int sign)
130{
131 switch (sign) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100132 case SIGINT:
Radek Krejci469aab82012-07-22 18:42:20 +0200133 case SIGTERM:
134 isterminated = 1;
Radek Krejci469aab82012-07-22 18:42:20 +0200135 break;
136 }
137}
138
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200139static char* gen_ncsession_hash( const char* hostname, const char* port, const char* sid)
Radek Krejci469aab82012-07-22 18:42:20 +0200140{
Radek Krejcif23850c2012-07-23 16:14:17 +0200141 unsigned char hash_raw[APR_SHA1_DIGESTSIZE];
142 int i;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200143 char* hash;
Radek Krejcif23850c2012-07-23 16:14:17 +0200144
Radek Krejci469aab82012-07-22 18:42:20 +0200145 apr_sha1_ctx_t sha1_ctx;
146 apr_sha1_init(&sha1_ctx);
147 apr_sha1_update(&sha1_ctx, hostname, strlen(hostname));
148 apr_sha1_update(&sha1_ctx, port, strlen(port));
149 apr_sha1_update(&sha1_ctx, sid, strlen(sid));
Radek Krejcif23850c2012-07-23 16:14:17 +0200150 apr_sha1_final(hash_raw, &sha1_ctx);
Radek Krejci469aab82012-07-22 18:42:20 +0200151
Radek Krejcif23850c2012-07-23 16:14:17 +0200152 /* convert binary hash into hex string, which is printable */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200153 hash = malloc(sizeof(char) * ((2*APR_SHA1_DIGESTSIZE)+1));
Radek Krejcif23850c2012-07-23 16:14:17 +0200154 for (i = 0; i < APR_SHA1_DIGESTSIZE; i++) {
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200155 snprintf(hash + (2*i), 3, "%02x", hash_raw[i]);
Radek Krejcif23850c2012-07-23 16:14:17 +0200156 }
157 //hash[2*APR_SHA1_DIGESTSIZE] = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200158
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200159 return (hash);
Radek Krejci469aab82012-07-22 18:42:20 +0200160}
161
Tomas Cejka8254dba2014-06-22 17:58:59 +0200162int netconf_callback_ssh_hostkey_check (const char* hostname, LIBSSH2_SESSION *session)
Radek Krejci469aab82012-07-22 18:42:20 +0200163{
164 /* always approve */
165 return (EXIT_SUCCESS);
166}
167
168char* netconf_callback_sshauth_password (const char* username, const char* hostname)
169{
170 char* buf;
171
172 buf = malloc ((strlen(password) + 1) * sizeof(char));
173 apr_cpystrn(buf, password, strlen(password) + 1);
174
175 return (buf);
176}
177
178void netconf_callback_sshauth_interactive (const char* name,
179 int name_len,
180 const char* instruction,
181 int instruction_len,
182 int num_prompts,
183 const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts,
184 LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses,
185 void** abstract)
186{
187 int i;
188
189 for (i=0; i<num_prompts; i++) {
190 responses[i].text = malloc ((strlen(password) + 1) * sizeof(char));
191 apr_cpystrn(responses[i].text, password, strlen(password) + 1);
192 responses[i].length = strlen(responses[i].text) + 1;
193 }
194
195 return;
196}
197
Radek Krejcic11fd862012-07-26 12:41:21 +0200198void netconf_callback_error_process(const char* tag,
199 const char* type,
200 const char* severity,
201 const char* apptag,
202 const char* path,
203 const char* message,
204 const char* attribute,
205 const char* element,
206 const char* ns,
207 const char* sid)
208{
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100209 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
Tomas Cejka442258e2014-04-01 18:17:18 +0200210 if (err_reply_p == NULL) {
211 DEBUG("Error message was not allocated. %s", __func__);
212 return;
213 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100214 json_object *err_reply = *err_reply_p;
215
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100216 json_object *array = NULL;
217 if (err_reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100218 DEBUG("error calback: empty error list");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100219 pthread_mutex_lock(&json_lock);
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100220 err_reply = json_object_new_object();
221 array = json_object_new_array();
222 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
223 json_object_object_add(err_reply, "errors", array);
224 if (message != NULL) {
225 json_object_array_add(array, json_object_new_string(message));
226 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100227 pthread_mutex_unlock(&json_lock);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100228 (*err_reply_p) = err_reply;
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100229 } else {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100230 DEBUG("error calback: nonempty error list");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100231 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +0200232 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100233 if (message != NULL) {
234 json_object_array_add(array, json_object_new_string(message));
235 }
236 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100237 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100238 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100239 pthread_setspecific(err_reply_key, err_reply_p);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100240 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200241}
242
Tomas Cejka47387fd2013-06-10 20:37:46 +0200243/**
244 * should be used in locked area
245 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100246void prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200247{
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200248 json_object *json_obj = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +0200249 json_object *js_tmp = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100250 char *old_sid = NULL;
251 const char *j_old_sid = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200252 const char *cpbltstr;
253 struct nc_cpblts* cpblts = NULL;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200254
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200255 if (s == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100256 DEBUG("No session given.");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200257 return;
258 }
259
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100260 pthread_mutex_lock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200261 if (s->hello_message != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100262 DEBUG("clean previous hello message");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200263 //json_object_put(s->hello_message);
Tomas Cejka09629492014-07-10 15:58:06 +0200264 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
265 j_old_sid = json_object_get_string(js_tmp);
266 if (j_old_sid != NULL) {
267 old_sid = strdup(j_old_sid);
268 }
269 json_object_put(s->hello_message);
270 json_object_put(js_tmp);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100271 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200272 s->hello_message = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200273 }
Tomas Cejkadd5cb452014-03-26 15:22:00 +0100274 s->hello_message = json_object_get(json_object_new_object());
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200275 if (session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100276 if (old_sid != NULL) {
277 /* use previous sid */
278 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
279 free(old_sid);
Tomas Cejkaa3ffdba2014-03-27 15:12:21 +0100280 old_sid = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100281 } else {
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200282 /* we don't have old sid */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100283 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
284 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200285 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
286 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
287 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
288 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
289 cpblts = nc_session_get_cpblts (session);
290 if (cpblts != NULL) {
291 json_obj = json_object_new_array();
292 nc_cpblts_iter_start (cpblts);
293 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
294 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
295 }
296 json_object_object_add(s->hello_message, "capabilities", json_obj);
297 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100298 DEBUG("%s", json_object_to_json_string(s->hello_message));
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200299 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100300 DEBUG("Session was not given.");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200301 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
302 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
303 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100304 DEBUG("Status info from hello message prepared");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100305 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200306
307}
308
Tomas Cejka442258e2014-04-01 18:17:18 +0200309void create_err_reply_p()
310{
311 json_object **err_reply = calloc(1, sizeof(json_object **));
312 if (err_reply == NULL) {
313 DEBUG("Allocation of err_reply storage failed!");
314 return;
315 }
316 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
317 DEBUG("cannot set thread-specific value.");
318 }
319}
320
321void clean_err_reply()
322{
323 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
324 if (err_reply != NULL) {
325 if (*err_reply != NULL) {
326 pthread_mutex_lock(&json_lock);
327 json_object_put(*err_reply);
328 pthread_mutex_unlock(&json_lock);
329 }
330 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
331 DEBUG("Cannot set thread-specific hash value.");
332 }
333 }
334}
335
336void free_err_reply()
337{
338 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
339 if (err_reply != NULL) {
340 if (*err_reply != NULL) {
341 pthread_mutex_lock(&json_lock);
342 json_object_put(*err_reply);
343 pthread_mutex_unlock(&json_lock);
344 }
345 free(err_reply);
346 err_reply = NULL;
347 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
348 DEBUG("Cannot set thread-specific hash value.");
349 }
350 }
351}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200352
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200353/**
354 * \defgroup netconf_operations NETCONF operations
355 * The list of NETCONF operations that mod_netconf supports.
356 * @{
357 */
358
359/**
360 * \brief Connect to NETCONF server
361 *
362 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
363 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100364static 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 +0200365{
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200366 struct nc_session* session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200367 struct session_with_mutex * locked_session;
Radek Krejcif23850c2012-07-23 16:14:17 +0200368 char *session_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200369
370 /* connect to the requested NETCONF server */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200371 password = (char*)pass;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100372 DEBUG("prepare to connect %s@%s:%s", user, host, port);
David Kupka8e60a372012-09-04 09:15:20 +0200373 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100374 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +0200375
Radek Krejci469aab82012-07-22 18:42:20 +0200376 /* if connected successful, add session to the list */
377 if (session != NULL) {
378 /* generate hash for the session */
Radek Krejcic11fd862012-07-26 12:41:21 +0200379 session_key = gen_ncsession_hash(
380 (host==NULL) ? "localhost" : host,
381 (port==NULL) ? "830" : port,
Radek Krejcia282bed2012-07-27 14:43:45 +0200382 nc_session_get_id(session));
David Kupka8e60a372012-09-04 09:15:20 +0200383
Tomas Cejkaba21b382013-04-13 02:37:32 +0200384 /** \todo allocate from apr_pool */
Tomas Cejka73496bf2014-03-26 15:31:09 +0100385 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 +0200386 nc_session_free(session);
Tomas Cejka73496bf2014-03-26 15:31:09 +0100387 session = NULL;
388 free(locked_session);
389 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100390 DEBUG("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200391 return NULL;
392 }
393 locked_session->session = session;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200394 locked_session->last_activity = apr_time_now();
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200395 locked_session->hello_message = NULL;
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200396 locked_session->closed = 0;
David Kupka8e60a372012-09-04 09:15:20 +0200397 pthread_mutex_init (&locked_session->lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100398 DEBUG("Before session_lock");
David Kupka8e60a372012-09-04 09:15:20 +0200399 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100400 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka866f2282014-09-18 15:20:26 +0200401 if (pthread_rwlock_wrlock(&session_lock) != 0) {
David Kupka8e60a372012-09-04 09:15:20 +0200402 nc_session_free(session);
403 free (locked_session);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100404 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200405 return NULL;
406 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200407 locked_session->notifications = apr_array_make(pool, NOTIFICATION_QUEUE_SIZE, sizeof(notification_t));
Tomas Cejka654f84e2013-04-19 11:55:01 +0200408 locked_session->ntfc_subscribed = 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100409 DEBUG("Add connection to the list");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200410 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 +0100411 DEBUG("Before session_unlock");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200412
Tomas Cejka47387fd2013-06-10 20:37:46 +0200413 /* lock session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100414 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200415 pthread_mutex_lock(&locked_session->lock);
416
417 /* unlock session list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100418 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200419 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100420 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +0200421 }
422
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200423 /* store information about session from hello message for future usage */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100424 prepare_status_message(locked_session, session);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200425
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100426 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200427 pthread_mutex_unlock(&locked_session->lock);
428
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100429 DEBUG("NETCONF session established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200430 return (session_key);
Radek Krejci469aab82012-07-22 18:42:20 +0200431 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100432 DEBUG("Connection could not be established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200433 return (NULL);
Radek Krejci469aab82012-07-22 18:42:20 +0200434 }
435
Radek Krejci469aab82012-07-22 18:42:20 +0200436}
437
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100438static int close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +0200439{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100440 DEBUG("lock private lock.");
441 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200442 if (pthread_mutex_lock(&locked_session->lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100443 DEBUG("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200444 }
445 locked_session->ntfc_subscribed = 0;
446 locked_session->closed = 1;
Tomas Cejka73496bf2014-03-26 15:31:09 +0100447 if (locked_session->session != NULL) {
448 nc_session_free(locked_session->session);
449 locked_session->session = NULL;
450 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100451 DEBUG("session closed.");
452 DEBUG("unlock private lock.");
453 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200454 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100455 DEBUG("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200456 }
457
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100458 DEBUG("unlock session lock.");
459 DEBUG("closed session, disabled notif(?), wait 2s");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200460 usleep(500000); /* let notification thread stop */
461
462 /* session shouldn't be used by now */
463 /** \todo free all notifications from queue */
464 apr_array_clear(locked_session->notifications);
465 pthread_mutex_destroy(&locked_session->lock);
466 if (locked_session->hello_message != NULL) {
467 /** \todo free hello_message */
468 //json_object_put(locked_session->hello_message);
469 locked_session->hello_message = NULL;
470 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200471 locked_session->session = NULL;
Tomas Cejkaaecc5f72013-10-01 00:03:50 +0200472 free(locked_session);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200473 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100474 DEBUG("NETCONF session closed, everything cleared.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200475 return (EXIT_SUCCESS);
476}
477
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100478static int netconf_close(const char* session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +0200479{
David Kupka8e60a372012-09-04 09:15:20 +0200480 struct session_with_mutex * locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +0200481
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100482 DEBUG("Key in hash to close: %s", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200483
David Kupka8e60a372012-09-04 09:15:20 +0200484 /* get exclusive (write) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100485 DEBUG("lock session lock.");
486 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200487 if (pthread_rwlock_wrlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100488 DEBUG("Error while locking rwlock");
489 (*reply) = create_error("Internal: Error while locking.");
David Kupka8e60a372012-09-04 09:15:20 +0200490 return EXIT_FAILURE;
491 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200492 /* get session to close */
493 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
494 /* remove session from the active sessions list -> nobody new can now work with session */
495 apr_hash_set(netconf_sessions_list, session_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200496
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100497 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200498 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100499 DEBUG("Error while unlocking rwlock");
500 (*reply) = create_error("Internal: Error while unlocking.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200501 }
502
503 if ((locked_session != NULL) && (locked_session->session != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100504 return close_and_free_session(locked_session);
Radek Krejci469aab82012-07-22 18:42:20 +0200505 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100506 DEBUG("Unknown session to close");
507 (*reply) = create_error("Internal: Unkown session to close.");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200508 return (EXIT_FAILURE);
Radek Krejci469aab82012-07-22 18:42:20 +0200509 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100510 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +0200511}
512
Tomas Cejkac7929632013-10-24 19:25:15 +0200513/**
514 * Test reply message type and return error message.
515 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200516 * \param[in] session nc_session internal struct
517 * \param[in] session_key session key, NULL to disable disconnect on error
518 * \param[in] msgt RPC-REPLY message type
519 * \param[out] data
520 * \return NULL on success
521 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100522json_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 +0200523{
524 NC_REPLY_TYPE replyt;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100525 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200526
527 /* process the result of the operation */
528 switch (msgt) {
529 case NC_MSG_UNKNOWN:
530 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100531 DEBUG("mod_netconf: receiving rpc-reply failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200532 if (session_key != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100533 netconf_close(session_key, &err);
534 }
535 if (err != NULL) {
536 return err;
Tomas Cejkac7929632013-10-24 19:25:15 +0200537 }
538 return create_error("Internal: Receiving RPC-REPLY failed.");
539 }
540 case NC_MSG_NONE:
541 /* there is error handled by callback */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100542 if (data != NULL) {
543 free(*data);
Tomas Cejka7b1e3bd2014-04-08 14:34:28 +0200544 (*data) = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100545 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200546 return NULL;
547 case NC_MSG_REPLY:
548 switch (replyt = nc_reply_get_type(reply)) {
549 case NC_REPLY_OK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100550 if ((data != NULL) && (*data != NULL)) {
551 free(*data);
552 (*data) = NULL;
553 }
554 return create_ok();
Tomas Cejkac7929632013-10-24 19:25:15 +0200555 case NC_REPLY_DATA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100556 if (((*data) = nc_reply_get_data(reply)) == NULL) {
557 DEBUG("mod_netconf: no data from reply");
Tomas Cejkac7929632013-10-24 19:25:15 +0200558 return create_error("Internal: No data from reply received.");
559 } else {
560 return NULL;
561 }
562 break;
563 case NC_REPLY_ERROR:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100564 DEBUG("mod_netconf: unexpected rpc-reply (%d)", replyt);
565 if (data != NULL) {
566 free(*data);
567 (*data) = NULL;
568 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200569 return create_error(nc_reply_get_errormsg(reply));
570 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100571 DEBUG("mod_netconf: unexpected rpc-reply (%d)", replyt);
572 if (data != NULL) {
573 free(*data);
574 (*data) = NULL;
575 }
Tomas Cejka60885252014-03-26 15:45:47 +0100576 return create_error("Unknown type of NETCONF reply.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200577 }
578 break;
579 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100580 DEBUG("mod_netconf: unexpected reply message received (%d)", msgt);
581 if (data != NULL) {
582 free(*data);
583 (*data) = NULL;
584 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200585 return create_error("Internal: Unexpected RPC-REPLY message type.");
586 }
587}
588
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100589json_object *netconf_unlocked_op(struct nc_session *session, nc_rpc* rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +0200590{
591 nc_reply* reply = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200592 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200593
594 /* check requests */
595 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100596 DEBUG("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200597 return create_error("Internal error: RPC is not created");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200598 }
599
600 if (session != NULL) {
601 /* send the request and get the reply */
602 msgt = nc_session_send_recv(session, rpc, &reply);
603 /* process the result of the operation */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100604 return netconf_test_reply(session, NULL, msgt, reply, NULL);
Tomas Cejka6b886e02013-07-05 09:53:17 +0200605 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100606 DEBUG("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200607 return create_error("Internal error: Unknown session to process.");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200608 }
609}
610
Tomas Cejkac7929632013-10-24 19:25:15 +0200611/**
612 * Perform RPC method that returns data.
613 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200614 * \param[in] session_key session identifier
615 * \param[in] rpc RPC message to perform
616 * \param[out] received_data received data string, can be NULL when no data expected, value can be set to NULL if no data received
617 * \return NULL on success, json object with error otherwise
618 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100619static json_object *netconf_op(const char* session_key, nc_rpc* rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +0200620{
621 struct nc_session *session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200622 struct session_with_mutex * locked_session;
Tomas Cejka00635972013-06-03 15:10:52 +0200623 nc_reply* reply = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200624 json_object *res = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100625 char *data = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100626 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200627
Radek Krejci8e4632a2012-07-26 13:40:34 +0200628 /* check requests */
629 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100630 DEBUG("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200631 res = create_error("Internal: RPC could not be created.");
632 data = NULL;
633 goto finished;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200634 }
635
David Kupka8e60a372012-09-04 09:15:20 +0200636 /* get non-exclusive (read) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100637 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka866f2282014-09-18 15:20:26 +0200638 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100639 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200640 res = create_error("Internal: Lock failed.");
641 data = NULL;
642 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200643 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200644 /* get session where send the RPC */
Tomas Cejka47387fd2013-06-10 20:37:46 +0200645 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 +0200646 if (locked_session != NULL) {
647 session = locked_session->session;
648 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200649 if (session != NULL) {
David Kupka8e60a372012-09-04 09:15:20 +0200650 /* get exclusive access to session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100651 DEBUG("LOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200652 if (pthread_mutex_lock(&locked_session->lock) != 0) {
653 /* unlock before returning error */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100654 DEBUG("UNLOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200655 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200656
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100657 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200658 res = create_error("Internal: Could not unlock.");
659 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200660 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200661 res = create_error("Internal: Could not unlock.");
David Kupka8e60a372012-09-04 09:15:20 +0200662 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100663 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200664 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200665
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100666 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200667 res = create_error("Internal: Could not unlock.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200668 }
669
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200670 locked_session->last_activity = apr_time_now();
Tomas Cejkac7929632013-10-24 19:25:15 +0200671
Radek Krejci035bf4e2012-07-25 10:59:09 +0200672 /* send the request and get the reply */
Radek Krejcia332b692012-11-12 16:15:54 +0100673 msgt = nc_session_send_recv(session, rpc, &reply);
674
David Kupka8e60a372012-09-04 09:15:20 +0200675 /* first release exclusive lock for this session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100676 DEBUG("UNLOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200677 pthread_mutex_unlock(&locked_session->lock);
678 /* end of critical section */
Radek Krejci035bf4e2012-07-25 10:59:09 +0200679
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100680 res = netconf_test_reply(session, session_key, msgt, reply, &data);
Radek Krejci035bf4e2012-07-25 10:59:09 +0200681 } else {
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100682 /* release lock on failure */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100683 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka866f2282014-09-18 15:20:26 +0200684 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100685 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100686 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100687 DEBUG("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200688 res = create_error("Unknown session to process.");
689 data = NULL;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200690 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200691finished:
692 nc_reply_free(reply);
693 if (received_data != NULL) {
694 (*received_data) = data;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100695 } else {
696 if (data != NULL) {
697 free(data);
698 data = NULL;
699 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200700 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200701 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200702}
703
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100704static char* netconf_getconfig(const char* session_key, NC_DATASTORE source, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200705{
706 nc_rpc* rpc;
707 struct nc_filter *f = NULL;
708 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200709 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200710
711 /* create filter if set */
712 if (filter != NULL) {
713 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
714 }
715
716 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100717 rpc = nc_rpc_getconfig (source, f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200718 nc_filter_free(f);
719 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100720 DEBUG("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200721 return (NULL);
722 }
723
Tomas Cejka94674662013-09-13 15:55:24 +0200724 /* tell server to show all elements even if they have default values */
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100725#ifdef HAVE_WITHDEFAULTS_TAGGED
Tomas Cejka1c3840d2014-01-29 21:08:23 +0100726 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100727#else
Tomas Cejkac0e166b2014-07-08 16:04:58 +0200728 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
729 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100730#endif
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100731 DEBUG("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200732 }
733
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100734 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200735 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100736 if (res != NULL) {
737 (*err) = res;
738 } else {
739 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200740 }
741
Radek Krejci8e4632a2012-07-26 13:40:34 +0200742 return (data);
743}
744
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100745static 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 +0100746{
747 nc_rpc* rpc;
748 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200749 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100750
751 /* create requests */
Tomas Cejka94da2c52013-01-08 18:20:30 +0100752 rpc = nc_rpc_getschema(identifier, version, format);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100753 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100754 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100755 return (NULL);
756 }
757
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100758 res = netconf_op(session_key, rpc, &data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100759 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100760 if (res != NULL) {
761 (*err) = res;
762 } else {
763 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200764 }
765
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100766 return (data);
767}
768
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100769static char* netconf_get(const char* session_key, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200770{
771 nc_rpc* rpc;
772 struct nc_filter *f = NULL;
773 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200774 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200775
776 /* create filter if set */
777 if (filter != NULL) {
778 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
779 }
780
781 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100782 rpc = nc_rpc_get (f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200783 nc_filter_free(f);
784 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100785 DEBUG("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200786 return (NULL);
787 }
788
Tomas Cejka94674662013-09-13 15:55:24 +0200789 /* tell server to show all elements even if they have default values */
Tomas Cejkac0e166b2014-07-08 16:04:58 +0200790 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
791 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
792 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100793 DEBUG("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200794 }
795
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100796 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200797 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200798 if (res == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100799 (*err) = res;
800 } else {
801 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200802 }
803
Radek Krejci8e4632a2012-07-26 13:40:34 +0200804 return (data);
805}
806
Tomas Cejkab4d05872014-02-14 22:44:38 +0100807static 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 +0200808{
809 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200810 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200811
812 /* create requests */
Tomas Cejkab4d05872014-02-14 22:44:38 +0100813 if (source == NC_DATASTORE_CONFIG) {
Tomas Cejka5064c232013-01-17 09:30:58 +0100814 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100815 /* config, url */
816 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100817 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100818 /* config, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100819 rpc = nc_rpc_copyconfig(source, target, config);
820 }
Tomas Cejkab4d05872014-02-14 22:44:38 +0100821 } else if (source == NC_DATASTORE_URL) {
822 if (target == NC_DATASTORE_URL) {
823 /* url, url */
824 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
825 } else {
826 /* url, datastore */
827 rpc = nc_rpc_copyconfig(source, target, uri_src);
828 }
Tomas Cejka5064c232013-01-17 09:30:58 +0100829 } else {
830 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100831 /* datastore, url */
832 rpc = nc_rpc_copyconfig(source, target, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100833 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100834 /* datastore, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100835 rpc = nc_rpc_copyconfig(source, target);
836 }
837 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200838 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100839 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200840 return create_error("Internal: Creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200841 }
842
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100843 res = netconf_op(session_key, rpc, NULL);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200844 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200845
846 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200847}
Radek Krejci035bf4e2012-07-25 10:59:09 +0200848
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100849static 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 +0200850{
851 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200852 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200853
854 /* create requests */
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100855 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200856 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100857 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200858 return create_error("Internal: Creating rpc request failed");
Radek Krejci62ab34b2012-07-26 13:42:05 +0200859 }
860
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100861 res = netconf_op(session_key, rpc, NULL);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200862 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200863
864 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200865}
866
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100867static json_object *netconf_killsession(const char* session_key, const char* sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200868{
869 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200870 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200871
872 /* create requests */
873 rpc = nc_rpc_killsession(sid);
874 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100875 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200876 return create_error("Internal: Creating rpc request failed");
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200877 }
878
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100879 res = netconf_op(session_key, rpc, NULL);
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200880 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200881 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200882}
883
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100884static json_object *netconf_onlytargetop(const char* session_key, NC_DATASTORE target, nc_rpc* (*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +0200885{
886 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200887 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +0200888
889 /* create requests */
Radek Krejci5cd7d422012-07-26 14:50:29 +0200890 rpc = op_func(target);
Radek Krejci2f318372012-07-26 14:22:35 +0200891 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100892 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200893 return create_error("Internal: Creating rpc request failed");
Radek Krejci2f318372012-07-26 14:22:35 +0200894 }
895
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100896 res = netconf_op(session_key, rpc, NULL);
Radek Krejci2f318372012-07-26 14:22:35 +0200897 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200898 return res;
Radek Krejci2f318372012-07-26 14:22:35 +0200899}
900
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100901static json_object *netconf_deleteconfig(const char* session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200902{
Tomas Cejka404d37e2013-04-13 02:31:35 +0200903 nc_rpc *rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200904 json_object *res = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +0200905 if (target != NC_DATASTORE_URL) {
906 rpc = nc_rpc_deleteconfig(target);
907 } else {
Tomas Cejkac7929632013-10-24 19:25:15 +0200908 rpc = nc_rpc_deleteconfig(target, url);
909 }
910 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100911 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200912 return create_error("Internal: Creating rpc request failed");
Tomas Cejka404d37e2013-04-13 02:31:35 +0200913 }
914
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100915 res = netconf_op(session_key, rpc, NULL);
Tomas Cejkac7929632013-10-24 19:25:15 +0200916 nc_rpc_free (rpc);
917 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +0200918}
919
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100920static json_object *netconf_lock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200921{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100922 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200923}
924
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100925static json_object *netconf_unlock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200926{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100927 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200928}
929
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100930static json_object *netconf_generic(const char* session_key, const char* content, char** data)
Radek Krejci80c10d92012-07-30 08:38:50 +0200931{
Tomas Cejka00635972013-06-03 15:10:52 +0200932 nc_rpc* rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200933 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +0200934
935 /* create requests */
936 rpc = nc_rpc_generic(content);
937 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100938 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200939 return create_error("Internal: Creating rpc request failed");
Radek Krejci80c10d92012-07-30 08:38:50 +0200940 }
941
Radek Krejcia332b692012-11-12 16:15:54 +0100942 if (data != NULL) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200943 // TODO ?free(*data);
944 (*data) = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100945 }
Radek Krejci80c10d92012-07-30 08:38:50 +0200946
947 /* get session where send the RPC */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100948 res = netconf_op(session_key, rpc, data);
Tomas Cejkac7929632013-10-24 19:25:15 +0200949 nc_rpc_free (rpc);
950 return res;
Radek Krejci80c10d92012-07-30 08:38:50 +0200951}
952
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200953/**
954 * @}
955 *//* netconf_operations */
956
Radek Krejci7338bde2012-08-10 12:57:30 +0200957void clb_print(NC_VERB_LEVEL level, const char* msg)
Radek Krejci469aab82012-07-22 18:42:20 +0200958{
Tomas Cejka8a86cc22014-09-18 15:35:07 +0200959#define FOREACH(I) \
960 I(NC_VERB_ERROR) I(NC_VERB_WARNING) \
961 I(NC_VERB_VERBOSE) I(NC_VERB_DEBUG)
962#define CASE(VAL) case VAL: DEBUG("%s: %s", #VAL, msg); \
963 break;
964
Radek Krejci7338bde2012-08-10 12:57:30 +0200965 switch (level) {
Tomas Cejka8a86cc22014-09-18 15:35:07 +0200966 FOREACH(CASE)
967 }
968 if (level == NC_VERB_ERROR) {
969 /* return global error */
970 netconf_callback_error_process(NULL /* tag */, NULL /* type */,
971 NULL /* severity */, NULL /* apptag */,
972 NULL /* path */, msg, NULL /* attribute */,
973 NULL /* element */, NULL /* ns */, NULL /* sid */);
Radek Krejci7338bde2012-08-10 12:57:30 +0200974 }
Radek Krejci469aab82012-07-22 18:42:20 +0200975}
976
Tomas Cejka64b87482013-06-03 16:30:53 +0200977/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +0200978 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +0200979 * Caller should free message memory.
980 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +0200981 * \return pointer to message
982 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100983char *get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +0200984{
985 /* read json in chunked framing */
986 unsigned int buffer_size = 0;
987 ssize_t buffer_len = 0;
988 char *buffer = NULL;
989 char c;
990 ssize_t ret;
991 int i, chunk_len;
992 char chunk_len_str[12];
993
994 while (1) {
995 /* read chunk length */
996 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
997 if (buffer != NULL) {
998 free (buffer);
999 buffer = NULL;
1000 }
1001 break;
1002 }
1003 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
1004 if (buffer != NULL) {
1005 free (buffer);
1006 buffer = NULL;
1007 }
1008 break;
1009 }
1010 i=0;
1011 memset (chunk_len_str, 0, 12);
1012 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
1013 if (i==0 && c == '#') {
1014 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
1015 /* end but invalid */
1016 if (buffer != NULL) {
1017 free (buffer);
1018 buffer = NULL;
1019 }
1020 }
1021 /* end of message, double-loop break */
1022 goto msg_complete;
1023 }
1024 chunk_len_str[i++] = c;
1025 if (i==11) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001026 DEBUG("Message is too long, buffer for length is not big enought!!!!");
Tomas Cejka64b87482013-06-03 16:30:53 +02001027 break;
1028 }
1029 }
1030 if (c != '\n') {
1031 if (buffer != NULL) {
1032 free (buffer);
1033 buffer = NULL;
1034 }
1035 break;
1036 }
1037 chunk_len_str[i] = 0;
1038 if ((chunk_len = atoi (chunk_len_str)) == 0) {
1039 if (buffer != NULL) {
1040 free (buffer);
1041 buffer = NULL;
1042 }
1043 break;
1044 }
1045 buffer_size += chunk_len+1;
1046 buffer = realloc (buffer, sizeof(char)*buffer_size);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001047 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
Tomas Cejka64b87482013-06-03 16:30:53 +02001048 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
1049 if (buffer != NULL) {
1050 free (buffer);
1051 buffer = NULL;
1052 }
1053 break;
1054 }
1055 buffer_len += ret;
1056 }
1057msg_complete:
1058 return buffer;
1059}
1060
Tomas Cejkad5b53772013-06-08 23:01:07 +02001061NC_DATASTORE parse_datastore(const char *ds)
1062{
1063 if (strcmp(ds, "running") == 0) {
1064 return NC_DATASTORE_RUNNING;
1065 } else if (strcmp(ds, "startup") == 0) {
1066 return NC_DATASTORE_STARTUP;
1067 } else if (strcmp(ds, "candidate") == 0) {
1068 return NC_DATASTORE_CANDIDATE;
Tomas Cejka4003a702013-10-01 00:02:45 +02001069 } else if (strcmp(ds, "url") == 0) {
1070 return NC_DATASTORE_URL;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001071 } else if (strcmp(ds, "config") == 0) {
1072 return NC_DATASTORE_CONFIG;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001073 }
1074 return -1;
1075}
1076
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001077NC_EDIT_TESTOPT_TYPE parse_testopt(const char *t)
1078{
1079 if (strcmp(t, "notset") == 0) {
1080 return NC_EDIT_TESTOPT_NOTSET;
1081 } else if (strcmp(t, "testset") == 0) {
1082 return NC_EDIT_TESTOPT_TESTSET;
1083 } else if (strcmp(t, "set") == 0) {
1084 return NC_EDIT_TESTOPT_SET;
1085 } else if (strcmp(t, "test") == 0) {
1086 return NC_EDIT_TESTOPT_TEST;
1087 }
1088 return NC_EDIT_TESTOPT_ERROR;
1089}
1090
Tomas Cejkad5b53772013-06-08 23:01:07 +02001091json_object *create_error(const char *errmess)
1092{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001093 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001094 json_object *reply = json_object_new_object();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001095 json_object *array = json_object_new_array();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001096 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001097 json_object_array_add(array, json_object_new_string(errmess));
1098 json_object_object_add(reply, "errors", array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001099 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001100 return reply;
1101
1102}
1103
1104json_object *create_data(const char *data)
1105{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001106 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001107 json_object *reply = json_object_new_object();
1108 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1109 json_object_object_add(reply, "data", json_object_new_string(data));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001110 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001111 return reply;
1112}
1113
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001114json_object *create_ok()
1115{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001116 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001117 json_object *reply = json_object_new_object();
1118 reply = json_object_new_object();
1119 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001120 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001121 return reply;
1122}
1123
Tomas Cejka09629492014-07-10 15:58:06 +02001124char *get_param_string(json_object *data, const char *name)
1125{
1126 json_object *js_tmp = NULL;
1127 char *res = NULL;
1128 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
1129 res = strdup(json_object_get_string(js_tmp));
1130 json_object_put(js_tmp);
1131 }
1132 return res;
1133}
1134
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001135json_object *handle_op_connect(apr_pool_t *pool, json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001136{
Tomas Cejka09629492014-07-10 15:58:06 +02001137 char *host = NULL;
1138 char *port = NULL;
1139 char *user = NULL;
1140 char *pass = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001141 json_object *capabilities = NULL;
1142 json_object *reply = NULL;
1143 char *session_key_hash = NULL;
1144 struct nc_cpblts* cpblts = NULL;
1145 unsigned int len, i;
1146
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001147 DEBUG("Request: Connect");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001148 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001149
Tomas Cejka09629492014-07-10 15:58:06 +02001150 host = get_param_string(request, "host");
1151 port = get_param_string(request, "port");
1152 user = get_param_string(request, "user");
1153 pass = get_param_string(request, "pass");
1154
1155 if (json_object_object_get_ex(request, "capabilities", &capabilities) == TRUE) {
1156 if ((capabilities != NULL) && ((len = json_object_array_length(capabilities)) > 0)) {
1157 cpblts = nc_cpblts_new(NULL);
1158 for (i=0; i<len; i++) {
1159 nc_cpblts_add(cpblts, json_object_get_string(json_object_array_get_idx(capabilities, i)));
1160 }
1161 } else {
1162 DEBUG("no capabilities specified");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001163 }
Tomas Cejka09629492014-07-10 15:58:06 +02001164 json_object_put(capabilities);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001165 }
Tomas Cejka09629492014-07-10 15:58:06 +02001166
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001167 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001168
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001169 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001170 if ((host == NULL) || (user == NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001171 DEBUG("Cannot connect - insufficient input.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001172 session_key_hash = NULL;
1173 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001174 session_key_hash = netconf_connect(pool, host, port, user, pass, cpblts);
1175 DEBUG("hash: %s", session_key_hash);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001176 }
1177 if (cpblts != NULL) {
1178 nc_cpblts_free(cpblts);
1179 }
1180
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001181 GETSPEC_ERR_REPLY
1182
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001183 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001184 if (session_key_hash == NULL) {
1185 /* negative reply */
1186 if (err_reply == NULL) {
1187 reply = json_object_new_object();
1188 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1189 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001190 DEBUG("Connection failed.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001191 } else {
1192 /* use filled err_reply from libnetconf's callback */
1193 reply = err_reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001194 DEBUG("Connect - error from libnetconf's callback.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001195 }
1196 } else {
1197 /* positive reply */
1198 reply = json_object_new_object();
1199 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1200 json_object_object_add(reply, "session", json_object_new_string(session_key_hash));
1201
1202 free(session_key_hash);
1203 }
Tomas Cejka09629492014-07-10 15:58:06 +02001204 memset(pass, 0, strlen(pass));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001205 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001206 CHECK_AND_FREE(host);
1207 CHECK_AND_FREE(user);
1208 CHECK_AND_FREE(port);
1209 CHECK_AND_FREE(pass);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001210 return reply;
1211}
1212
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001213json_object *handle_op_get(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001214{
Tomas Cejka09629492014-07-10 15:58:06 +02001215 char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001216 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001217 json_object *reply = NULL;
1218
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001219 DEBUG("Request: get (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001220
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001221 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001222 filter = get_param_string(request, "filter");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001223 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001224
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001225 if ((data = netconf_get(session_key, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001226 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001227 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001228 reply = create_data(data);
1229 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001230 }
1231 return reply;
1232}
1233
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001234json_object *handle_op_getconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001235{
1236 NC_DATASTORE ds_type_s = -1;
Tomas Cejka09629492014-07-10 15:58:06 +02001237 char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001238 char *data = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001239 char *source = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001240 json_object *reply = NULL;
1241
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001242 DEBUG("Request: get-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001243
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001244 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001245 filter = get_param_string(request, "filter");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001246
Tomas Cejka09629492014-07-10 15:58:06 +02001247 source = get_param_string(request, "source");
1248 if (source != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001249 ds_type_s = parse_datastore(source);
1250 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001251 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001252
Tomas Cejkad5b53772013-06-08 23:01:07 +02001253 if (ds_type_s == -1) {
Tomas Cejka09629492014-07-10 15:58:06 +02001254 reply = create_error("Invalid source repository type requested.");
1255 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001256 }
1257
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001258 if ((data = netconf_getconfig(session_key, ds_type_s, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001259 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001260 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001261 reply = create_data(data);
1262 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001263 }
Tomas Cejka09629492014-07-10 15:58:06 +02001264finalize:
1265 CHECK_AND_FREE(filter);
1266 CHECK_AND_FREE(source);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001267 return reply;
1268}
1269
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001270json_object *handle_op_getschema(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001271{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001272 char *data = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001273 char *identifier = NULL;
1274 char *version = NULL;
1275 char *format = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001276 json_object *reply = NULL;
1277
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001278 DEBUG("Request: get-schema (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001279 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001280 identifier = get_param_string(request, "identifier");
1281 version = get_param_string(request, "version");
1282 format = get_param_string(request, "format");
Tomas Cejkab9e7efe2014-03-28 21:09:04 +01001283 pthread_mutex_unlock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001284
Tomas Cejkad5b53772013-06-08 23:01:07 +02001285 if (identifier == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001286 reply = create_error("No identifier for get-schema supplied.");
1287 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001288 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001289
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001290 DEBUG("get-schema(version: %s, format: %s)", version, format);
1291 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001292 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001293 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001294 reply = create_data(data);
1295 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001296 }
Tomas Cejka09629492014-07-10 15:58:06 +02001297finalize:
1298 CHECK_AND_FREE(identifier);
1299 CHECK_AND_FREE(version);
1300 CHECK_AND_FREE(format);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001301 return reply;
1302}
1303
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001304json_object *handle_op_editconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001305{
1306 NC_DATASTORE ds_type_s = -1;
1307 NC_DATASTORE ds_type_t = -1;
1308 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
1309 NC_EDIT_ERROPT_TYPE erropt_type = 0;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001310 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
Tomas Cejka09629492014-07-10 15:58:06 +02001311 char *defop = NULL;
1312 char *erropt = NULL;
1313 char *config = NULL;
1314 char *source = NULL;
1315 char *target = NULL;
1316 char *testopt = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001317 json_object *reply = NULL;
1318
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001319 DEBUG("Request: edit-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001320
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001321 pthread_mutex_lock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001322 /* get parameters */
Tomas Cejka09629492014-07-10 15:58:06 +02001323 defop = get_param_string(request, "default-operation");
1324 erropt = get_param_string(request, "error-option");
1325 target = get_param_string(request, "target");
1326 source = get_param_string(request, "source");
1327 config = get_param_string(request, "config");
1328 testopt = get_param_string(request, "test-option");
1329 pthread_mutex_unlock(&json_lock);
1330
1331 if (target != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001332 ds_type_t = parse_datastore(target);
1333 }
Tomas Cejka09629492014-07-10 15:58:06 +02001334 if (source != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001335 ds_type_s = parse_datastore(source);
1336 } else {
1337 /* source is optional, default value is config */
1338 ds_type_s = NC_DATASTORE_CONFIG;
1339 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001340
Tomas Cejkad5b53772013-06-08 23:01:07 +02001341 if (defop != NULL) {
1342 if (strcmp(defop, "merge") == 0) {
1343 defop_type = NC_EDIT_DEFOP_MERGE;
1344 } else if (strcmp(defop, "replace") == 0) {
1345 defop_type = NC_EDIT_DEFOP_REPLACE;
1346 } else if (strcmp(defop, "none") == 0) {
1347 defop_type = NC_EDIT_DEFOP_NONE;
1348 } else {
Tomas Cejka09629492014-07-10 15:58:06 +02001349 reply = create_error("Invalid default-operation parameter.");
1350 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001351 }
1352 } else {
1353 defop_type = NC_EDIT_DEFOP_NOTSET;
1354 }
1355
Tomas Cejkad5b53772013-06-08 23:01:07 +02001356 if (erropt != NULL) {
1357 if (strcmp(erropt, "continue-on-error") == 0) {
1358 erropt_type = NC_EDIT_ERROPT_CONT;
1359 } else if (strcmp(erropt, "stop-on-error") == 0) {
1360 erropt_type = NC_EDIT_ERROPT_STOP;
1361 } else if (strcmp(erropt, "rollback-on-error") == 0) {
1362 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
1363 } else {
Tomas Cejka09629492014-07-10 15:58:06 +02001364 reply = create_error("Invalid error-option parameter.");
1365 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001366 }
1367 } else {
1368 erropt_type = 0;
1369 }
1370
Tomas Cejkad5b53772013-06-08 23:01:07 +02001371 if (ds_type_t == -1) {
Tomas Cejka09629492014-07-10 15:58:06 +02001372 reply = create_error("Invalid target repository type requested.");
1373 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001374 }
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001375 if (ds_type_s == NC_DATASTORE_CONFIG) {
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001376 if (config == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001377 reply = create_error("Invalid config data parameter.");
1378 goto finalize;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001379 }
1380 } else if (ds_type_s == NC_DATASTORE_URL){
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001381 if (config == NULL) {
1382 config = "";
1383 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001384 }
1385
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001386 if (testopt != NULL) {
1387 testopt_type = parse_testopt(testopt);
1388 } else {
1389 testopt_type = NC_EDIT_TESTOPT_TESTSET;
1390 }
1391
1392 reply = netconf_editconfig(session_key, ds_type_s, ds_type_t, defop_type, erropt_type, testopt_type, config);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001393
1394 CHECK_ERR_SET_REPLY
Tomas Cejka09629492014-07-10 15:58:06 +02001395finalize:
1396 CHECK_AND_FREE(defop);
1397 CHECK_AND_FREE(erropt);
1398 CHECK_AND_FREE(config);
1399 CHECK_AND_FREE(source);
1400 CHECK_AND_FREE(target);
1401 CHECK_AND_FREE(testopt);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001402 return reply;
1403}
1404
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001405json_object *handle_op_copyconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001406{
1407 NC_DATASTORE ds_type_s = -1;
1408 NC_DATASTORE ds_type_t = -1;
Tomas Cejka09629492014-07-10 15:58:06 +02001409 char *config = NULL;
1410 char *target = NULL;
1411 char *source = NULL;
1412 char *uri_src = NULL;
1413 char *uri_trg = NULL;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001414
Tomas Cejkad5b53772013-06-08 23:01:07 +02001415 json_object *reply = NULL;
1416
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001417 DEBUG("Request: copy-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001418
1419 /* get parameters */
Tomas Cejka09629492014-07-10 15:58:06 +02001420 pthread_mutex_lock(&json_lock);
1421 target = get_param_string(request, "target");
1422 source = get_param_string(request, "source");
1423 config = get_param_string(request, "config");
1424 uri_src = get_param_string(request, "uri-source");
1425 uri_trg = get_param_string(request, "uri-target");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001426 pthread_mutex_unlock(&json_lock);
1427
Tomas Cejka09629492014-07-10 15:58:06 +02001428 if (target != NULL) {
1429 ds_type_t = parse_datastore(target);
1430 }
1431 if (source != NULL) {
1432 ds_type_s = parse_datastore(source);
1433 } else {
1434 /* source == NULL *//* no explicit source specified -> use config data */
Tomas Cejkad5b53772013-06-08 23:01:07 +02001435 ds_type_s = NC_DATASTORE_CONFIG;
Tomas Cejka09629492014-07-10 15:58:06 +02001436 }
1437 if (ds_type_s == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001438 /* source datastore specified, but it is invalid */
Tomas Cejka09629492014-07-10 15:58:06 +02001439 reply = create_error("Invalid source repository type requested.");
1440 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001441 }
1442
1443 if (ds_type_t == -1) {
1444 /* invalid target datastore specified */
Tomas Cejka09629492014-07-10 15:58:06 +02001445 reply = create_error("Invalid target repository type requested.");
1446 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001447 }
1448
Tomas Cejka55c6df52014-02-20 12:59:33 +01001449 /* source can be missing when config is given */
1450 if (source == NULL && config == NULL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001451 reply = create_error("invalid input parameters - source and config is required.");
Tomas Cejka09629492014-07-10 15:58:06 +02001452 goto finalize;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001453 }
1454
1455 if (ds_type_s == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001456 if (uri_src == NULL) {
1457 uri_src = "";
1458 }
1459 }
1460 if (ds_type_t == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001461 if (uri_trg == NULL) {
1462 uri_trg = "";
1463 }
1464 }
1465 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001466
1467 CHECK_ERR_SET_REPLY
1468
Tomas Cejka09629492014-07-10 15:58:06 +02001469finalize:
1470 CHECK_AND_FREE(config);
1471 CHECK_AND_FREE(target);
1472 CHECK_AND_FREE(source);
1473 CHECK_AND_FREE(uri_src);
1474 CHECK_AND_FREE(uri_trg);
1475
Tomas Cejkad5b53772013-06-08 23:01:07 +02001476 return reply;
1477}
1478
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001479json_object *handle_op_generic(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001480{
1481 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001482 char *config = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001483 char *data = NULL;
1484
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001485 DEBUG("Request: generic request for session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001486
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001487 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001488 config = get_param_string(request, "content");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001489 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001490
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001491 reply = netconf_generic(session_key, config, &data);
1492 if (reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001493 GETSPEC_ERR_REPLY
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001494 if (err_reply != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001495 /* use filled err_reply from libnetconf's callback */
1496 reply = err_reply;
1497 }
1498 } else {
1499 if (data == NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001500 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001501 reply = json_object_new_object();
1502 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001503 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001504 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001505 reply = create_data(data);
1506 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001507 }
1508 }
Tomas Cejka09629492014-07-10 15:58:06 +02001509 CHECK_AND_FREE(config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001510 return reply;
1511}
1512
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001513json_object *handle_op_disconnect(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001514{
1515 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001516 DEBUG("Request: Disconnect session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001517
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001518 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001519 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001520 } else {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001521 reply = create_ok();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001522 }
1523 return reply;
1524}
1525
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001526json_object *handle_op_kill(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001527{
1528 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001529 char *sid = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001530
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001531 DEBUG("Request: kill-session, session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001532
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001533 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001534 sid = get_param_string(request, "session-id");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001535 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001536
1537 if (sid == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001538 reply = create_error("Missing session-id parameter.");
1539 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001540 }
1541
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001542 reply = netconf_killsession(session_key, sid);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001543
1544 CHECK_ERR_SET_REPLY
1545
Tomas Cejka09629492014-07-10 15:58:06 +02001546finalize:
1547 CHECK_AND_FREE(sid);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001548 return reply;
1549}
1550
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001551json_object *handle_op_reloadhello(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001552{
Tomas Cejka47387fd2013-06-10 20:37:46 +02001553 struct nc_session *temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001554 struct session_with_mutex * locked_session = NULL;
1555 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001556 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001557
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001558 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001559 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001560 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001561 return NULL;
1562 }
1563
Tomas Cejka09629492014-07-10 15:58:06 +02001564 locked_session = (struct session_with_mutex *) apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001565 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001566 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001567 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001568 DEBUG("creating temporal NC session.");
Tomas Cejka47387fd2013-06-10 20:37:46 +02001569 temp_session = nc_session_connect_channel(locked_session->session, NULL);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001570 if (temp_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001571 prepare_status_message(locked_session, temp_session);
1572 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001573 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001574 temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001575 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001576 DEBUG("Reload hello failed due to channel establishment");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001577 reply = create_error("Reload was unsuccessful, connection failed.");
1578 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001579 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001580 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001581 DEBUG("UNLOCK wrlock %s", __func__);
1582 if (pthread_rwlock_unlock(&session_lock) != 0) {
1583 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1584 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001585 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001586 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001587 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001588 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001589 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001590 reply = create_error("Invalid session identifier.");
1591 }
1592
1593 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
1594 reply = locked_session->hello_message;
1595 }
1596 return reply;
1597}
1598
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001599json_object *handle_op_info(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001600{
1601 json_object *reply = NULL;
Tomas Cejka47387fd2013-06-10 20:37:46 +02001602 struct session_with_mutex * locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001603 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001604
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001605 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001606 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001607 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001608 }
1609
Tomas Cejka09629492014-07-10 15:58:06 +02001610 locked_session = (struct session_with_mutex *) apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001611 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001612 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001613 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001614 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001615 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001616 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001617 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001618 if (locked_session->hello_message != NULL) {
1619 reply = locked_session->hello_message;
1620 } else {
1621 reply = create_error("Invalid session identifier.");
1622 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001623 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001624 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001625 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001626 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001627 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001628 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001629 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001630 reply = create_error("Invalid session identifier.");
1631 }
1632
Tomas Cejka47387fd2013-06-10 20:37:46 +02001633
Tomas Cejkad5b53772013-06-08 23:01:07 +02001634 return reply;
1635}
1636
Tomas Cejka6b886e02013-07-05 09:53:17 +02001637void notification_history(time_t eventtime, const char *content)
1638{
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001639 json_object *notif_history_array = (json_object *) pthread_getspecific(notif_history_key);
1640 if (notif_history_array == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001641 DEBUG("No list of notification history found.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001642 return;
1643 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001644 DEBUG("Got notification from history %lu.", (long unsigned) eventtime);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001645 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001646 json_object *notif = json_object_new_object();
1647 if (notif == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001648 DEBUG("Could not allocate memory for notification (json).");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001649 goto failed;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001650 }
1651 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
1652 json_object_object_add(notif, "content", json_object_new_string(content));
1653 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001654failed:
1655 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001656}
1657
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001658json_object *handle_op_ntfgethistory(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001659{
1660 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001661 json_object *js_tmp = NULL;
1662 char *sid = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001663 struct session_with_mutex *locked_session = NULL;
1664 struct nc_session *temp_session = NULL;
1665 nc_rpc *rpc = NULL;
1666 time_t start = 0;
1667 time_t stop = 0;
Tomas Cejka09629492014-07-10 15:58:06 +02001668 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001669
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001670 DEBUG("Request: get notification history, session %s", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001671
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001672 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001673 sid = get_param_string(request, "session");
1674
1675 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
1676 from = json_object_get_int64(js_tmp);
1677 json_object_put(js_tmp);
1678 }
1679 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
1680 to = json_object_get_int64(js_tmp);
1681 json_object_put(js_tmp);
1682 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001683 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001684
1685 start = time(NULL) + from;
1686 stop = time(NULL) + to;
1687
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001688 DEBUG("notification history interval %li %li", (long int) from, (long int) to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001689
1690 if (sid == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001691 reply = create_error("Missing session parameter.");
1692 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001693 }
1694
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001695 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001696 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001697 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka09629492014-07-10 15:58:06 +02001698 reply = create_error("Internal lock failed.");
1699 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001700 }
1701
1702 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1703 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001704 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001705 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001706 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001707 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001708 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001709 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001710 DEBUG("creating temporal NC session.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001711 temp_session = nc_session_connect_channel(locked_session->session, NULL);
1712 if (temp_session != NULL) {
1713 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
1714 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001715 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001716 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001717 DEBUG("notifications: creating an rpc request failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001718 reply = create_error("notifications: creating an rpc request failed.");
1719 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001720 }
1721
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001722 DEBUG("Send NC subscribe.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001723 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001724 json_object *res = netconf_unlocked_op(temp_session, rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001725 if (res != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001726 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001727 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001728 DEBUG("Subscription RPC failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001729 reply = res;
1730 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001731 }
1732 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
1733
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001734 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001735 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001736 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001737 pthread_mutex_lock(&ntf_history_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001738 pthread_mutex_lock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001739 json_object *notif_history_array = json_object_new_array();
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001740 pthread_mutex_unlock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001741 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001742 DEBUG("notif_history: cannot set thread-specific hash value.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001743 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001744
1745 ncntf_dispatch_receive(temp_session, notification_history);
1746
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001747 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001748 reply = json_object_new_object();
1749 json_object_object_add(reply, "notifications", notif_history_array);
1750 //json_object_put(notif_history_array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001751 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001752
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001753 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001754 pthread_mutex_unlock(&ntf_history_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001755 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001756 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001757 temp_session = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001758 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001759 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001760 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001761 DEBUG("Get history of notification failed due to channel establishment");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001762 reply = create_error("Get history of notification was unsuccessful, connection failed.");
1763 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001764 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001765 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001766 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001767 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001768 }
1769 reply = create_error("Invalid session identifier.");
1770 }
1771
Tomas Cejka09629492014-07-10 15:58:06 +02001772finalize:
1773 CHECK_AND_FREE(sid);
Tomas Cejka4003a702013-10-01 00:02:45 +02001774 return reply;
1775}
1776
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001777json_object *handle_op_validate(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02001778{
1779 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001780 char *sid = NULL;
1781 char *target = NULL;
1782 char *url = NULL;
Tomas Cejka4003a702013-10-01 00:02:45 +02001783 nc_rpc *rpc = NULL;
1784 NC_DATASTORE target_ds;
1785
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001786 DEBUG("Request: validate datastore, session %s", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02001787
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001788 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001789 sid = get_param_string(request, "session");
1790 target = get_param_string(request, "target");
1791 url = get_param_string(request, "url");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001792 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02001793
1794
1795 if ((sid == NULL) || (target == NULL)) {
Tomas Cejka09629492014-07-10 15:58:06 +02001796 reply = create_error("Missing session parameter.");
1797 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001798 }
Tomas Cejka4003a702013-10-01 00:02:45 +02001799
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001800 /* validation */
1801 target_ds = parse_datastore(target);
1802 if (target_ds == NC_DATASTORE_URL) {
1803 if (url != NULL) {
1804 rpc = nc_rpc_validate(target_ds, url);
Tomas Cejka4003a702013-10-01 00:02:45 +02001805 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001806 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
Tomas Cejka4003a702013-10-01 00:02:45 +02001807 || (target_ds == NC_DATASTORE_CANDIDATE)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001808 rpc = nc_rpc_validate(target_ds);
1809 }
1810 if (rpc == NULL) {
1811 DEBUG("mod_netconf: creating rpc request failed");
1812 reply = create_error("Creation of RPC request failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001813 goto finalize;
Tomas Cejka4003a702013-10-01 00:02:45 +02001814 }
1815
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001816 DEBUG("Request: validate datastore");
1817 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001818
1819 CHECK_ERR_SET_REPLY
1820
1821 if (reply == NULL) {
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001822 DEBUG("Request: validation ok.");
1823 reply = create_ok();
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001824 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001825 }
1826 nc_rpc_free (rpc);
Tomas Cejka09629492014-07-10 15:58:06 +02001827finalize:
1828 CHECK_AND_FREE(sid);
1829 CHECK_AND_FREE(target);
1830 CHECK_AND_FREE(url);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001831 return reply;
1832}
1833
David Kupka8e60a372012-09-04 09:15:20 +02001834void * thread_routine (void * arg)
1835{
1836 void * retval = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001837 struct pollfd fds;
Tomas Cejka00635972013-06-03 15:10:52 +02001838 json_object *request = NULL;
1839 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001840 json_object *js_tmp = NULL;
1841 int operation = (-1);
1842 NC_DATASTORE ds_type_t = -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001843 int status = 0;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001844 const char *msgtext;
Tomas Cejka09629492014-07-10 15:58:06 +02001845 char *session_key = NULL;
1846 char *target = NULL;
1847 char *url = NULL;
Tomas Cejka64b87482013-06-03 16:30:53 +02001848 char *chunked_out_msg = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001849 apr_pool_t * pool = ((struct pass_to_thread*)arg)->pool;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001850 //server_rec * server = ((struct pass_to_thread*)arg)->server;
David Kupka8e60a372012-09-04 09:15:20 +02001851 int client = ((struct pass_to_thread*)arg)->client;
1852
Tomas Cejka00635972013-06-03 15:10:52 +02001853 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001854
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001855 /* init thread specific err_reply memory */
Tomas Cejka442258e2014-04-01 18:17:18 +02001856 create_err_reply_p();
1857
David Kupka8e60a372012-09-04 09:15:20 +02001858 while (!isterminated) {
1859 fds.fd = client;
1860 fds.events = POLLIN;
1861 fds.revents = 0;
1862
1863 status = poll(&fds, 1, 1000);
1864
1865 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
1866 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001867 //DEBUG("poll interrupted");
David Kupka8e60a372012-09-04 09:15:20 +02001868 continue;
1869 } else if (status < 0) {
1870 /* 0: poll time outed
1871 * close socket and ignore this request from the client, it can try it again
1872 * -1: poll failed
1873 * something wrong happend, close this socket and wait for another request
1874 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001875 //DEBUG("poll failed, status %d(%d: %s)", status, errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +02001876 close(client);
1877 break;
1878 }
1879 /* status > 0 */
1880
1881 /* check the status of the socket */
1882
1883 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
1884 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
1885 /* close client's socket (it's probably already closed by client */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001886 //DEBUG("socket error (%d)", fds.revents);
David Kupka8e60a372012-09-04 09:15:20 +02001887 close(client);
1888 break;
1889 }
1890
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001891 DEBUG("Get framed message...");
1892 buffer = get_framed_message(client);
David Kupka8e60a372012-09-04 09:15:20 +02001893
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001894 DEBUG("Check read buffer.");
David Kupka8e60a372012-09-04 09:15:20 +02001895 if (buffer != NULL) {
Tomas Cejka00635972013-06-03 15:10:52 +02001896 enum json_tokener_error jerr;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001897 pthread_mutex_lock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001898 request = json_tokener_parse_verbose(buffer, &jerr);
1899 if (jerr != json_tokener_success) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001900 DEBUG("JSON parsing error");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001901 pthread_mutex_unlock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001902 continue;
1903 }
David Kupka8e60a372012-09-04 09:15:20 +02001904
Tomas Cejka09629492014-07-10 15:58:06 +02001905 session_key = get_param_string(request, "session");
1906 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
1907 operation = json_object_get_int(js_tmp);
1908 json_object_put(js_tmp);
1909 js_tmp = NULL;
1910 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001911 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001912 if (operation == -1) {
1913 reply = create_error("Missing operation type form frontend.");
1914 goto send_reply;
1915 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001916
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001917 DEBUG("operation %d session_key %s.", operation, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001918 /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
1919 if (operation != MSG_CONNECT && session_key == NULL) {
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001920 reply = create_error("Missing session specification.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001921 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001922 msgtext = json_object_to_json_string(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001923 pthread_mutex_unlock(&json_lock);
1924
David Kupka8e60a372012-09-04 09:15:20 +02001925 send(client, msgtext, strlen(msgtext) + 1, 0);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001926
1927 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001928 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001929 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001930 /* there is some stupid client, so close the connection to give a chance to some other client */
1931 close(client);
1932 break;
1933 }
1934
David Kupka8e60a372012-09-04 09:15:20 +02001935 /* null global JSON error-reply */
Tomas Cejka442258e2014-04-01 18:17:18 +02001936 clean_err_reply();
David Kupka8e60a372012-09-04 09:15:20 +02001937
1938 /* prepare reply envelope */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001939 if (reply != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001940 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001941 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001942 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001943 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001944 reply = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001945
1946 /* process required operation */
1947 switch (operation) {
1948 case MSG_CONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001949 reply = handle_op_connect(pool, request);
David Kupka8e60a372012-09-04 09:15:20 +02001950 break;
1951 case MSG_GET:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001952 reply = handle_op_get(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001953 break;
1954 case MSG_GETCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001955 reply = handle_op_getconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001956 break;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001957 case MSG_GETSCHEMA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001958 reply = handle_op_getschema(pool, request, session_key);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001959 break;
David Kupka8e60a372012-09-04 09:15:20 +02001960 case MSG_EDITCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001961 reply = handle_op_editconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001962 break;
1963 case MSG_COPYCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001964 reply = handle_op_copyconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001965 break;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001966
David Kupka8e60a372012-09-04 09:15:20 +02001967 case MSG_DELETECONFIG:
David Kupka8e60a372012-09-04 09:15:20 +02001968 case MSG_LOCK:
David Kupka8e60a372012-09-04 09:15:20 +02001969 case MSG_UNLOCK:
Tomas Cejkad5b53772013-06-08 23:01:07 +02001970 /* get parameters */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001971 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001972 target = get_param_string(request, "target");
1973 pthread_mutex_unlock(&json_lock);
1974 if (target != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001975 ds_type_t = parse_datastore(target);
1976 }
David Kupka8e60a372012-09-04 09:15:20 +02001977
1978 if (ds_type_t == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001979 reply = create_error("Invalid target repository type requested.");
David Kupka8e60a372012-09-04 09:15:20 +02001980 break;
1981 }
David Kupka8e60a372012-09-04 09:15:20 +02001982 switch(operation) {
1983 case MSG_DELETECONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001984 DEBUG("Request: delete-config (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001985 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001986 url = get_param_string(request, "url");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001987 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001988 reply = netconf_deleteconfig(session_key, ds_type_t, url);
David Kupka8e60a372012-09-04 09:15:20 +02001989 break;
1990 case MSG_LOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001991 DEBUG("Request: lock (session %s)", session_key);
1992 reply = netconf_lock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001993 break;
1994 case MSG_UNLOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001995 DEBUG("Request: unlock (session %s)", session_key);
1996 reply = netconf_unlock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001997 break;
1998 default:
Tomas Cejkac7929632013-10-24 19:25:15 +02001999 reply = create_error("Internal: Unknown request type.");
David Kupka8e60a372012-09-04 09:15:20 +02002000 break;
2001 }
2002
Tomas Cejka442258e2014-04-01 18:17:18 +02002003 CHECK_ERR_SET_REPLY
Tomas Cejkac7929632013-10-24 19:25:15 +02002004 if (reply == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02002005 reply = create_ok();
David Kupka8e60a372012-09-04 09:15:20 +02002006 }
2007 break;
2008 case MSG_KILL:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002009 reply = handle_op_kill(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002010 break;
2011 case MSG_DISCONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002012 reply = handle_op_disconnect(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002013 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02002014 case MSG_RELOADHELLO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002015 reply = handle_op_reloadhello(pool, request, session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002016 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02002017 case MSG_INFO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002018 reply = handle_op_info(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002019 break;
2020 case MSG_GENERIC:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002021 reply = handle_op_generic(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002022 break;
Tomas Cejka6b886e02013-07-05 09:53:17 +02002023 case MSG_NTF_GETHISTORY:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002024 reply = handle_op_ntfgethistory(pool, request, session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02002025 break;
Tomas Cejka4003a702013-10-01 00:02:45 +02002026 case MSG_VALIDATE:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002027 reply = handle_op_validate(pool, request, session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02002028 break;
David Kupka8e60a372012-09-04 09:15:20 +02002029 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002030 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002031 reply = create_error("Operation not supported.");
David Kupka8e60a372012-09-04 09:15:20 +02002032 break;
2033 }
Tomas Cejka09629492014-07-10 15:58:06 +02002034 /* free parameters */
2035 CHECK_AND_FREE(session_key);
2036 CHECK_AND_FREE(url);
2037 CHECK_AND_FREE(target);
2038 request = NULL;
2039 operation = (-1);
2040 ds_type_t = (-1);
2041
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002042 DEBUG("Clean request json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002043 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002044 if (request != NULL) {
2045 json_object_put(request);
2046 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002047 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02002048
2049
2050send_reply:
David Kupka1e3e4c82012-09-04 09:32:15 +02002051 /* send reply to caller */
2052 if (reply != NULL) {
2053 msgtext = json_object_to_json_string(reply);
Tomas Cejka866f2282014-09-18 15:20:26 +02002054 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int) strlen(msgtext), msgtext) == -1) {
Tomas Cejka00635972013-06-03 15:10:52 +02002055 if (buffer != NULL) {
2056 free(buffer);
2057 buffer = NULL;
2058 }
David Kupka8e60a372012-09-04 09:15:20 +02002059 break;
2060 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002061 pthread_mutex_unlock(&json_lock);
2062
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002063 DEBUG("Send framed reply json object.");
Tomas Cejka64b87482013-06-03 16:30:53 +02002064 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002065 DEBUG("Clean reply json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002066 pthread_mutex_lock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02002067 json_object_put(reply);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002068 reply = NULL;
2069 DEBUG("Clean message buffer.");
Tomas Cejka09629492014-07-10 15:58:06 +02002070 CHECK_AND_FREE(chunked_out_msg);
Tomas Cejka64b87482013-06-03 16:30:53 +02002071 chunked_out_msg = NULL;
Tomas Cejka00635972013-06-03 15:10:52 +02002072 if (buffer != NULL) {
2073 free(buffer);
2074 buffer = NULL;
2075 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002076 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02002077 clean_err_reply();
David Kupka1e3e4c82012-09-04 09:32:15 +02002078 } else {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002079 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002080 DEBUG("Reply is NULL, shouldn't be...");
2081 continue;
David Kupka8e60a372012-09-04 09:15:20 +02002082 }
2083 }
2084 }
David Kupka8e60a372012-09-04 09:15:20 +02002085 free (arg);
2086
Tomas Cejka442258e2014-04-01 18:17:18 +02002087 free_err_reply();
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002088
David Kupka8e60a372012-09-04 09:15:20 +02002089 return retval;
2090}
2091
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002092/**
2093 * \brief Close all open NETCONF sessions.
2094 *
2095 * During termination of mod_netconf, it is useful to close all remaining
2096 * sessions. This function iterates over the list of sessions and close them
2097 * all.
2098 *
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002099 * \param[in] p apr pool needed for hash table iterating
2100 * \param[in] ht hash table of session_with_mutex structs
2101 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002102static void close_all_nc_sessions(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002103{
2104 apr_hash_index_t *hi;
2105 void *val = NULL;
2106 struct session_with_mutex *swm = NULL;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002107 const char *hashed_key = NULL;
2108 apr_ssize_t hashed_key_length;
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002109 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002110
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002111 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002112 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002113 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002114 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002115 return;
2116 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002117 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002118 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002119 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002120 swm = (struct session_with_mutex *) val;
2121 if (swm != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002122 DEBUG("LOCK mutex %s", __func__);
2123 pthread_mutex_lock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002124 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002125 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002126 pthread_mutex_unlock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002127
2128 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002129 close_and_free_session(swm);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002130 }
2131 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002132 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002133 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002134 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002135 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002136 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002137}
2138
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002139static void check_timeout_and_close(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002140{
2141 apr_hash_index_t *hi;
2142 void *val = NULL;
2143 struct nc_session *ns = NULL;
2144 struct session_with_mutex *swm = NULL;
2145 const char *hashed_key = NULL;
2146 apr_ssize_t hashed_key_length;
2147 apr_time_t current_time = apr_time_now();
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002148 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002149
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002150 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002151//DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka866f2282014-09-18 15:20:26 +02002152 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002153 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002154 return;
2155 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002156 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002157 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
2158 swm = (struct session_with_mutex *) val;
2159 if (swm == NULL) {
2160 continue;
2161 }
2162 ns = swm->session;
2163 if (ns == NULL) {
2164 continue;
2165 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002166//DEBUG("LOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002167 pthread_mutex_lock(&swm->lock);
2168 if ((current_time - swm->last_activity) > apr_time_from_sec(ACTIVITY_TIMEOUT)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002169 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002170 /* remove session from the active sessions list */
Tomas Cejka47387fd2013-06-10 20:37:46 +02002171 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002172//DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002173 pthread_mutex_unlock(&swm->lock);
2174
2175 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002176 close_and_free_session(swm);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002177 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002178//DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002179 pthread_mutex_unlock(&swm->lock);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002180 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002181 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002182 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002183//DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka866f2282014-09-18 15:20:26 +02002184 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002185 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002186 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002187}
2188
2189
2190/**
Radek Krejcif23850c2012-07-23 16:14:17 +02002191 * This is actually implementation of NETCONF client
2192 * - requests are received from UNIX socket in the predefined format
2193 * - results are replied through the same way
2194 * - the daemon run as a separate process, but it is started and stopped
2195 * automatically by Apache.
2196 *
2197 */
Radek Krejci469aab82012-07-22 18:42:20 +02002198static void forked_proc(apr_pool_t * pool, server_rec * server)
2199{
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002200 struct timeval tv;
Radek Krejci469aab82012-07-22 18:42:20 +02002201 struct sockaddr_un local, remote;
David Kupka8e60a372012-09-04 09:15:20 +02002202 int lsock, client, ret, i, pthread_count = 0;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002203 unsigned int olds = 0, timediff = 0;
David Kupka8e60a372012-09-04 09:15:20 +02002204 socklen_t len;
Radek Krejciae021c12012-07-25 18:03:52 +02002205 mod_netconf_cfg *cfg;
David Kupka8e60a372012-09-04 09:15:20 +02002206 struct pass_to_thread * arg;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002207 pthread_t * ptids = calloc(1, sizeof(pthread_t));
David Kupka8e60a372012-09-04 09:15:20 +02002208 struct timespec maxtime;
2209 pthread_rwlockattr_t lock_attrs;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002210 char *sockname = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +02002211 #ifdef WITH_NOTIFICATIONS
2212 char use_notifications = 0;
2213 #endif
David Kupka8e60a372012-09-04 09:15:20 +02002214
Tomas Cejka6b886e02013-07-05 09:53:17 +02002215 http_server = server;
2216
Tomas Cejka404d37e2013-04-13 02:31:35 +02002217 /* wait at most 5 seconds for every thread to terminate */
David Kupka8e60a372012-09-04 09:15:20 +02002218 maxtime.tv_sec = 5;
2219 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02002220
Tomas Cejka04e08f42014-03-27 19:52:34 +01002221#ifdef HAVE_UNIXD_SETUP_CHILD
Radek Krejcif23850c2012-07-23 16:14:17 +02002222 /* change uid and gid of process for security reasons */
Radek Krejci469aab82012-07-22 18:42:20 +02002223 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01002224#else
2225# ifdef SU_GROUP
2226 if (strlen(SU_GROUP) > 0) {
2227 struct group *g = getgrnam(SU_GROUP);
2228 if (g == NULL) {
2229 DEBUG("GID (%s) was not found.", SU_GROUP);
2230 return;
2231 }
2232 if (setgid(g->gr_gid) != 0) {
2233
2234 DEBUG("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
2235 return;
2236 }
2237 }
2238# else
2239 DEBUG("no SU_GROUP");
2240# endif
2241# ifdef SU_USER
2242 if (strlen(SU_USER) > 0) {
2243 struct passwd *p = getpwnam(SU_USER);
2244 if (p == NULL) {
2245 DEBUG("UID (%s) was not found.", SU_USER);
2246 return;
2247 }
2248 if (setuid(p->pw_uid) != 0) {
2249 DEBUG("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
2250 return;
2251 }
2252 }
2253# else
2254 DEBUG("no SU_USER");
2255# endif
2256#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002257
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002258 if (server != NULL) {
2259 cfg = ap_get_module_config(server->module_config, &netconf_module);
2260 if (cfg == NULL) {
2261 DEBUG("Getting mod_netconf configuration failed");
2262 return;
2263 }
2264 sockname = cfg->sockname;
2265 } else {
2266 sockname = SOCKET_FILENAME;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002267 }
Radek Krejci469aab82012-07-22 18:42:20 +02002268
Tomas Cejka04e08f42014-03-27 19:52:34 +01002269 /* try to remove if exists */
2270 unlink(sockname);
2271
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002272 /* create listening UNIX socket to accept incoming connections */
Radek Krejci469aab82012-07-22 18:42:20 +02002273 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002274 DEBUG("Creating socket failed (%s)", strerror(errno));
2275 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002276 }
2277
2278 local.sun_family = AF_UNIX;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002279 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
Radek Krejci469aab82012-07-22 18:42:20 +02002280 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
2281
2282 if (bind(lsock, (struct sockaddr *) &local, len) == -1) {
2283 if (errno == EADDRINUSE) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002284 DEBUG("mod_netconf socket address already in use");
2285 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002286 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002287 DEBUG("Binding socket failed (%s)", strerror(errno));
2288 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002289 }
2290
2291 if (listen(lsock, MAX_SOCKET_CL) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002292 DEBUG("Setting up listen socket failed (%s)", strerror(errno));
2293 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002294 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002295 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02002296
Tomas Cejka04e08f42014-03-27 19:52:34 +01002297 uid_t user = -1;
2298 if (strlen(CHOWN_USER) > 0) {
2299 struct passwd *p = getpwnam(CHOWN_USER);
2300 if (p != NULL) {
2301 user = p->pw_uid;
2302 }
2303 }
2304 gid_t group = -1;
2305 if (strlen(CHOWN_GROUP) > 0) {
2306 struct group *g = getgrnam(CHOWN_GROUP);
2307 if (g != NULL) {
2308 group = g->gr_gid;
2309 }
2310 }
2311 if (chown(sockname, user, group) == -1) {
2312 DEBUG("Chown on socket file failed (%s).", strerror(errno));
2313 }
2314
Tomas Cejkaba21b382013-04-13 02:37:32 +02002315 /* prepare internal lists */
2316 netconf_sessions_list = apr_hash_make(pool);
2317
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002318 #ifdef WITH_NOTIFICATIONS
Tomas Cejka47387fd2013-06-10 20:37:46 +02002319 if (notification_init(pool, server) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002320 DEBUG("libwebsockets initialization failed");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002321 use_notifications = 0;
2322 } else {
2323 use_notifications = 1;
2324 }
2325 #endif
2326
Radek Krejci469aab82012-07-22 18:42:20 +02002327 /* setup libnetconf's callbacks */
2328 nc_verbosity(NC_VERB_DEBUG);
Radek Krejci469aab82012-07-22 18:42:20 +02002329 nc_callback_print(clb_print);
2330 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
2331 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
2332 nc_callback_sshauth_password(netconf_callback_sshauth_password);
Radek Krejcic11fd862012-07-26 12:41:21 +02002333 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02002334
2335 /* disable publickey authentication */
2336 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
2337
David Kupka8e60a372012-09-04 09:15:20 +02002338 /* create mutex protecting session list */
2339 pthread_rwlockattr_init(&lock_attrs);
2340 /* rwlock is shared only with threads in this process */
2341 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
2342 /* create rw lock */
2343 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002344 DEBUG("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
2345 goto error_exit;
David Kupka8e60a372012-09-04 09:15:20 +02002346 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002347 pthread_mutex_init(&ntf_history_lock, NULL);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002348 pthread_mutex_init(&json_lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002349 DEBUG("init of notif_history_key.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002350 if (pthread_key_create(&notif_history_key, NULL) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002351 DEBUG("init of notif_history_key failed");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002352 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002353 DEBUG("init of err_reply_key.");
2354 if (pthread_key_create(&err_reply_key, NULL) != 0) {
2355 DEBUG("init of err_reply_key failed");
2356 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02002357
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002358 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002359 while (isterminated == 0) {
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002360 gettimeofday(&tv, NULL);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002361 timediff = (unsigned int)tv.tv_sec - olds;
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002362 #ifdef WITH_NOTIFICATIONS
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002363 if (timediff > 60) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002364 DEBUG("handling notifications");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002365 }
2366 if (use_notifications == 1) {
2367 notification_handle();
2368 }
2369 #endif
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002370 if (timediff > ACTIVITY_CHECK_INTERVAL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002371 check_timeout_and_close(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002372 }
Radek Krejci469aab82012-07-22 18:42:20 +02002373
2374 /* open incoming connection if any */
David Kupka8e60a372012-09-04 09:15:20 +02002375 len = sizeof(remote);
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002376 if (((unsigned int)tv.tv_sec - olds) > 60) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002377 DEBUG("accepting another client");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002378 olds = tv.tv_sec;
2379 }
David Kupka8e60a372012-09-04 09:15:20 +02002380 client = accept(lsock, (struct sockaddr *) &remote, &len);
Radek Krejci469aab82012-07-22 18:42:20 +02002381 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
2382 apr_sleep(SLEEP_TIME);
2383 continue;
2384 } else if (client == -1 && (errno == EINTR)) {
2385 continue;
2386 } else if (client == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002387 DEBUG("Accepting mod_netconf client connection failed (%s)", strerror(errno));
Radek Krejci469aab82012-07-22 18:42:20 +02002388 continue;
2389 }
Radek Krejci469aab82012-07-22 18:42:20 +02002390
2391 /* set client's socket as non-blocking */
Radek Krejcif23850c2012-07-23 16:14:17 +02002392 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002393
David Kupka8e60a372012-09-04 09:15:20 +02002394 arg = malloc (sizeof(struct pass_to_thread));
2395 arg->client = client;
2396 arg->pool = pool;
2397 arg->server = server;
2398 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02002399
David Kupka8e60a372012-09-04 09:15:20 +02002400 /* start new thread. It will serve this particular request and then terminate */
2401 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void*)arg)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002402 DEBUG("Creating POSIX thread failed: %d\n", ret);
David Kupka8e60a372012-09-04 09:15:20 +02002403 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002404 DEBUG("Thread %lu created", ptids[pthread_count]);
David Kupka8e60a372012-09-04 09:15:20 +02002405 pthread_count++;
2406 ptids = realloc (ptids, sizeof(pthread_t)*(pthread_count+1));
2407 ptids[pthread_count] = 0;
2408 }
Radek Krejci469aab82012-07-22 18:42:20 +02002409
David Kupka8e60a372012-09-04 09:15:20 +02002410 /* check if some thread already terminated, free some resources by joining it */
2411 for (i=0; i<pthread_count; i++) {
2412 if (pthread_tryjoin_np (ptids[i], (void**)&arg) == 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002413 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
David Kupka8e60a372012-09-04 09:15:20 +02002414 pthread_count--;
2415 if (pthread_count > 0) {
2416 /* place last Thread ID on the place of joined one */
2417 ptids[i] = ptids[pthread_count];
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002418 }
Radek Krejci469aab82012-07-22 18:42:20 +02002419 }
2420 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002421 DEBUG("Running %d threads", pthread_count);
Radek Krejci469aab82012-07-22 18:42:20 +02002422 }
2423
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002424 DEBUG("mod_netconf terminating...");
David Kupka8e60a372012-09-04 09:15:20 +02002425 /* join all threads */
2426 for (i=0; i<pthread_count; i++) {
2427 pthread_timedjoin_np (ptids[i], (void**)&arg, &maxtime);
2428 }
Radek Krejci469aab82012-07-22 18:42:20 +02002429
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002430 #ifdef WITH_NOTIFICATIONS
2431 notification_close();
2432 #endif
2433
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002434 /* close all NETCONF sessions */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002435 close_all_nc_sessions(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002436
David Kupka8e60a372012-09-04 09:15:20 +02002437 /* destroy rwlock */
2438 pthread_rwlock_destroy(&session_lock);
2439 pthread_rwlockattr_destroy(&lock_attrs);
2440
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002441 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02002442
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002443 free(ptids);
2444 close(lsock);
Radek Krejci469aab82012-07-22 18:42:20 +02002445 exit(APR_SUCCESS);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002446 return;
2447error_exit:
2448 close(lsock);
2449 free(ptids);
2450 return;
Radek Krejci469aab82012-07-22 18:42:20 +02002451}
2452
Radek Krejcif23850c2012-07-23 16:14:17 +02002453static void *mod_netconf_create_conf(apr_pool_t * pool, server_rec * s)
Radek Krejci469aab82012-07-22 18:42:20 +02002454{
Radek Krejcif23850c2012-07-23 16:14:17 +02002455 mod_netconf_cfg *config = apr_pcalloc(pool, sizeof(mod_netconf_cfg));
2456 apr_pool_create(&config->pool, pool);
2457 config->forkproc = NULL;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002458 config->sockname = SOCKET_FILENAME;
Radek Krejcif23850c2012-07-23 16:14:17 +02002459
2460 return (void *)config;
Radek Krejci469aab82012-07-22 18:42:20 +02002461}
2462
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002463#ifndef HTTPD_INDEPENDENT
Radek Krejci469aab82012-07-22 18:42:20 +02002464static int mod_netconf_master_init(apr_pool_t * pconf, apr_pool_t * ptemp,
2465 apr_pool_t * plog, server_rec * s)
2466{
Radek Krejcif23850c2012-07-23 16:14:17 +02002467 mod_netconf_cfg *config;
2468 apr_status_t res;
2469
Radek Krejci469aab82012-07-22 18:42:20 +02002470 /* These two help ensure that we only init once. */
Radek Krejcia332b692012-11-12 16:15:54 +01002471 void *data = NULL;
Radek Krejcif23850c2012-07-23 16:14:17 +02002472 const char *userdata_key = "netconf_ipc_init";
Radek Krejci469aab82012-07-22 18:42:20 +02002473
2474 /*
2475 * The following checks if this routine has been called before.
2476 * This is necessary because the parent process gets initialized
2477 * a couple of times as the server starts up.
2478 */
2479 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2480 if (!data) {
2481 apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
2482 return (OK);
2483 }
2484
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002485 DEBUG("creating mod_netconf daemon");
Radek Krejcif23850c2012-07-23 16:14:17 +02002486 config = ap_get_module_config(s->module_config, &netconf_module);
Radek Krejcidfaa6ea2012-07-23 09:04:43 +02002487
Radek Krejcif23850c2012-07-23 16:14:17 +02002488 if (config && config->forkproc == NULL) {
2489 config->forkproc = apr_pcalloc(config->pool, sizeof(apr_proc_t));
2490 res = apr_proc_fork(config->forkproc, config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002491 switch (res) {
2492 case APR_INCHILD:
2493 /* set signal handler */
Radek Krejcif23850c2012-07-23 16:14:17 +02002494 apr_signal_init(config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002495 apr_signal(SIGTERM, signal_handler);
2496
2497 /* log start of the separated NETCONF communication process */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002498 DEBUG("mod_netconf daemon started (PID %d)", getpid());
Radek Krejci469aab82012-07-22 18:42:20 +02002499
2500 /* start main loop providing NETCONF communication */
Radek Krejcif23850c2012-07-23 16:14:17 +02002501 forked_proc(config->pool, s);
Radek Krejci469aab82012-07-22 18:42:20 +02002502
Radek Krejcif23850c2012-07-23 16:14:17 +02002503 /* I never should be here, wtf?!? */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002504 DEBUG("mod_netconf daemon unexpectedly stopped");
Radek Krejci469aab82012-07-22 18:42:20 +02002505 exit(APR_EGENERAL);
2506 break;
2507 case APR_INPARENT:
2508 /* register child to be killed (SIGTERM) when the module config's pool dies */
Radek Krejcif23850c2012-07-23 16:14:17 +02002509 apr_pool_note_subprocess(config->pool, config->forkproc, APR_KILL_AFTER_TIMEOUT);
Radek Krejci469aab82012-07-22 18:42:20 +02002510 break;
2511 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002512 DEBUG("apr_proc_fork() failed");
Radek Krejci469aab82012-07-22 18:42:20 +02002513 break;
2514 }
Radek Krejcif23850c2012-07-23 16:14:17 +02002515 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002516 DEBUG("mod_netconf misses configuration structure");
Radek Krejci469aab82012-07-22 18:42:20 +02002517 }
2518
2519 return OK;
2520}
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002521#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002522
Radek Krejci469aab82012-07-22 18:42:20 +02002523/**
2524 * Register module hooks
2525 */
2526static void mod_netconf_register_hooks(apr_pool_t * p)
2527{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002528#ifndef HTTPD_INDEPENDENT
Radek Krejcif23850c2012-07-23 16:14:17 +02002529 ap_hook_post_config(mod_netconf_master_init, NULL, NULL, APR_HOOK_LAST);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002530#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002531}
2532
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002533static const char* cfg_set_socket_path(cmd_parms* cmd, void* cfg, const char* arg)
2534{
2535 ((mod_netconf_cfg*)cfg)->sockname = apr_pstrdup(cmd->pool, arg);
2536 return NULL;
2537}
2538
2539static const command_rec netconf_cmds[] = {
2540 AP_INIT_TAKE1("NetconfSocket", cfg_set_socket_path, NULL, OR_ALL, "UNIX socket path for mod_netconf communication."),
2541 {NULL}
2542};
2543
Radek Krejci469aab82012-07-22 18:42:20 +02002544/* Dispatch list for API hooks */
2545module AP_MODULE_DECLARE_DATA netconf_module = {
2546 STANDARD20_MODULE_STUFF,
2547 NULL, /* create per-dir config structures */
2548 NULL, /* merge per-dir config structures */
Radek Krejcif23850c2012-07-23 16:14:17 +02002549 mod_netconf_create_conf, /* create per-server config structures */
Radek Krejci469aab82012-07-22 18:42:20 +02002550 NULL, /* merge per-server config structures */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002551 netconf_cmds, /* table of config file commands */
Radek Krejci469aab82012-07-22 18:42:20 +02002552 mod_netconf_register_hooks /* register hooks */
2553};
Radek Krejcia332b692012-11-12 16:15:54 +01002554
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002555int main(int argc, char **argv)
2556{
2557 apr_pool_t *pool;
2558 apr_app_initialize(&argc, (char const *const **) &argv, NULL);
2559 apr_signal(SIGTERM, signal_handler);
2560 apr_signal(SIGINT, signal_handler);
2561 apr_pool_create(&pool, NULL);
2562 forked_proc(pool, NULL);
2563 apr_pool_destroy(pool);
2564 apr_terminate();
2565 DEBUG("Terminated");
2566 return 0;
2567}