blob: f2e8a034ae4ab1f8da43e50a5b95d48098cc728e [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) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200211 ERROR("Error message was not allocated. %s", __func__);
Tomas Cejka442258e2014-04-01 18:17:18 +0200212 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 Cejkacf44e522015-04-24 17:29:21 +0200218 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200230 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200256 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200262 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200300 ERROR("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) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200313 ERROR("Allocation of err_reply storage failed!");
Tomas Cejka442258e2014-04-01 18:17:18 +0200314 return;
315 }
316 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200317 ERROR("cannot set thread-specific value.");
Tomas Cejka442258e2014-04-01 18:17:18 +0200318 }
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) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200331 ERROR("Cannot set thread-specific hash value.");
Tomas Cejka442258e2014-04-01 18:17:18 +0200332 }
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) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200348 ERROR("Cannot set thread-specific hash value.");
Tomas Cejka442258e2014-04-01 18:17:18 +0200349 }
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 Cejkacf44e522015-04-24 17:29:21 +0200390 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200404 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200420 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200432 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200443 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200455 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200488 ERROR("Error while locking rwlock");
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100489 (*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 Cejkacf44e522015-04-24 17:29:21 +0200499 ERROR("Error while unlocking rwlock");
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100500 (*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 Cejkacf44e522015-04-24 17:29:21 +0200506 ERROR("Unknown session to close");
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100507 (*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 Cejkacf44e522015-04-24 17:29:21 +0200531 ERROR("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) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200557 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200564 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100565 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 Cejkacf44e522015-04-24 17:29:21 +0200571 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100572 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 Cejkacf44e522015-04-24 17:29:21 +0200580 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100581 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 Cejkacf44e522015-04-24 17:29:21 +0200596 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200606 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200630 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200639 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200657 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200666 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200685 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100686 }
Tomas Cejkacf44e522015-04-24 17:29:21 +0200687 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200720 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200731 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200754 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200785 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200793 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200839 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200857 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200875 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200892 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200911 ERROR("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 Cejkacf44e522015-04-24 17:29:21 +0200938 ERROR("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) \
Tomas Cejkacf44e522015-04-24 17:29:21 +0200960 I(NC_VERB_ERROR) I(NC_VERB_WARNING)
961
962#define CASE(VAL) case VAL: ERROR("%s: %s", #VAL, msg); \
Tomas Cejka8a86cc22014-09-18 15:35:07 +0200963 break;
964
Radek Krejci7338bde2012-08-10 12:57:30 +0200965 switch (level) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200966 FOREACH(CASE);
967 case NC_VERB_VERBOSE:
968 case NC_VERB_DEBUG:
969 DEBUG("DEBUG: %s", msg);
970 break;
Tomas Cejka8a86cc22014-09-18 15:35:07 +0200971 }
972 if (level == NC_VERB_ERROR) {
973 /* return global error */
974 netconf_callback_error_process(NULL /* tag */, NULL /* type */,
975 NULL /* severity */, NULL /* apptag */,
976 NULL /* path */, msg, NULL /* attribute */,
977 NULL /* element */, NULL /* ns */, NULL /* sid */);
Radek Krejci7338bde2012-08-10 12:57:30 +0200978 }
Radek Krejci469aab82012-07-22 18:42:20 +0200979}
980
Tomas Cejka64b87482013-06-03 16:30:53 +0200981/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +0200982 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +0200983 * Caller should free message memory.
984 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +0200985 * \return pointer to message
986 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100987char *get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +0200988{
989 /* read json in chunked framing */
990 unsigned int buffer_size = 0;
991 ssize_t buffer_len = 0;
992 char *buffer = NULL;
993 char c;
994 ssize_t ret;
995 int i, chunk_len;
996 char chunk_len_str[12];
997
998 while (1) {
999 /* read chunk length */
1000 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
1001 if (buffer != NULL) {
1002 free (buffer);
1003 buffer = NULL;
1004 }
1005 break;
1006 }
1007 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
1008 if (buffer != NULL) {
1009 free (buffer);
1010 buffer = NULL;
1011 }
1012 break;
1013 }
1014 i=0;
1015 memset (chunk_len_str, 0, 12);
1016 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
1017 if (i==0 && c == '#') {
1018 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
1019 /* end but invalid */
1020 if (buffer != NULL) {
1021 free (buffer);
1022 buffer = NULL;
1023 }
1024 }
1025 /* end of message, double-loop break */
1026 goto msg_complete;
1027 }
1028 chunk_len_str[i++] = c;
1029 if (i==11) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001030 ERROR("Message is too long, buffer for length is not big enought!!!!");
Tomas Cejka64b87482013-06-03 16:30:53 +02001031 break;
1032 }
1033 }
1034 if (c != '\n') {
1035 if (buffer != NULL) {
1036 free (buffer);
1037 buffer = NULL;
1038 }
1039 break;
1040 }
1041 chunk_len_str[i] = 0;
1042 if ((chunk_len = atoi (chunk_len_str)) == 0) {
1043 if (buffer != NULL) {
1044 free (buffer);
1045 buffer = NULL;
1046 }
1047 break;
1048 }
1049 buffer_size += chunk_len+1;
1050 buffer = realloc (buffer, sizeof(char)*buffer_size);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001051 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
Tomas Cejka64b87482013-06-03 16:30:53 +02001052 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
1053 if (buffer != NULL) {
1054 free (buffer);
1055 buffer = NULL;
1056 }
1057 break;
1058 }
1059 buffer_len += ret;
1060 }
1061msg_complete:
1062 return buffer;
1063}
1064
Tomas Cejkad5b53772013-06-08 23:01:07 +02001065NC_DATASTORE parse_datastore(const char *ds)
1066{
1067 if (strcmp(ds, "running") == 0) {
1068 return NC_DATASTORE_RUNNING;
1069 } else if (strcmp(ds, "startup") == 0) {
1070 return NC_DATASTORE_STARTUP;
1071 } else if (strcmp(ds, "candidate") == 0) {
1072 return NC_DATASTORE_CANDIDATE;
Tomas Cejka4003a702013-10-01 00:02:45 +02001073 } else if (strcmp(ds, "url") == 0) {
1074 return NC_DATASTORE_URL;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001075 } else if (strcmp(ds, "config") == 0) {
1076 return NC_DATASTORE_CONFIG;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001077 }
1078 return -1;
1079}
1080
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001081NC_EDIT_TESTOPT_TYPE parse_testopt(const char *t)
1082{
1083 if (strcmp(t, "notset") == 0) {
1084 return NC_EDIT_TESTOPT_NOTSET;
1085 } else if (strcmp(t, "testset") == 0) {
1086 return NC_EDIT_TESTOPT_TESTSET;
1087 } else if (strcmp(t, "set") == 0) {
1088 return NC_EDIT_TESTOPT_SET;
1089 } else if (strcmp(t, "test") == 0) {
1090 return NC_EDIT_TESTOPT_TEST;
1091 }
1092 return NC_EDIT_TESTOPT_ERROR;
1093}
1094
Tomas Cejkad5b53772013-06-08 23:01:07 +02001095json_object *create_error(const char *errmess)
1096{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001097 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001098 json_object *reply = json_object_new_object();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001099 json_object *array = json_object_new_array();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001100 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001101 json_object_array_add(array, json_object_new_string(errmess));
1102 json_object_object_add(reply, "errors", array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001103 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001104 return reply;
1105
1106}
1107
1108json_object *create_data(const char *data)
1109{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001110 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001111 json_object *reply = json_object_new_object();
1112 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1113 json_object_object_add(reply, "data", json_object_new_string(data));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001114 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001115 return reply;
1116}
1117
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001118json_object *create_ok()
1119{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001120 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001121 json_object *reply = json_object_new_object();
1122 reply = json_object_new_object();
1123 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001124 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001125 return reply;
1126}
1127
Tomas Cejka09629492014-07-10 15:58:06 +02001128char *get_param_string(json_object *data, const char *name)
1129{
1130 json_object *js_tmp = NULL;
1131 char *res = NULL;
1132 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
1133 res = strdup(json_object_get_string(js_tmp));
1134 json_object_put(js_tmp);
1135 }
1136 return res;
1137}
1138
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001139json_object *handle_op_connect(apr_pool_t *pool, json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001140{
Tomas Cejka09629492014-07-10 15:58:06 +02001141 char *host = NULL;
1142 char *port = NULL;
1143 char *user = NULL;
1144 char *pass = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001145 json_object *capabilities = NULL;
1146 json_object *reply = NULL;
1147 char *session_key_hash = NULL;
1148 struct nc_cpblts* cpblts = NULL;
1149 unsigned int len, i;
1150
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001151 DEBUG("Request: Connect");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001152 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001153
Tomas Cejka09629492014-07-10 15:58:06 +02001154 host = get_param_string(request, "host");
1155 port = get_param_string(request, "port");
1156 user = get_param_string(request, "user");
1157 pass = get_param_string(request, "pass");
1158
1159 if (json_object_object_get_ex(request, "capabilities", &capabilities) == TRUE) {
1160 if ((capabilities != NULL) && ((len = json_object_array_length(capabilities)) > 0)) {
1161 cpblts = nc_cpblts_new(NULL);
1162 for (i=0; i<len; i++) {
1163 nc_cpblts_add(cpblts, json_object_get_string(json_object_array_get_idx(capabilities, i)));
1164 }
1165 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001166 ERROR("no capabilities specified");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001167 }
Tomas Cejka09629492014-07-10 15:58:06 +02001168 json_object_put(capabilities);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001169 }
Tomas Cejka09629492014-07-10 15:58:06 +02001170
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001171 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001172
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001173 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001174 if ((host == NULL) || (user == NULL)) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001175 ERROR("Cannot connect - insufficient input.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001176 session_key_hash = NULL;
1177 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001178 session_key_hash = netconf_connect(pool, host, port, user, pass, cpblts);
1179 DEBUG("hash: %s", session_key_hash);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001180 }
1181 if (cpblts != NULL) {
1182 nc_cpblts_free(cpblts);
1183 }
1184
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001185 GETSPEC_ERR_REPLY
1186
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001187 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001188 if (session_key_hash == NULL) {
1189 /* negative reply */
1190 if (err_reply == NULL) {
1191 reply = json_object_new_object();
1192 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1193 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
Tomas Cejkacf44e522015-04-24 17:29:21 +02001194 ERROR("Connection failed.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001195 } else {
1196 /* use filled err_reply from libnetconf's callback */
1197 reply = err_reply;
Tomas Cejkacf44e522015-04-24 17:29:21 +02001198 ERROR("Connect - error from libnetconf's callback.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001199 }
1200 } else {
1201 /* positive reply */
1202 reply = json_object_new_object();
1203 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1204 json_object_object_add(reply, "session", json_object_new_string(session_key_hash));
1205
1206 free(session_key_hash);
1207 }
Tomas Cejka09629492014-07-10 15:58:06 +02001208 memset(pass, 0, strlen(pass));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001209 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001210 CHECK_AND_FREE(host);
1211 CHECK_AND_FREE(user);
1212 CHECK_AND_FREE(port);
1213 CHECK_AND_FREE(pass);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001214 return reply;
1215}
1216
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001217json_object *handle_op_get(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001218{
Tomas Cejka09629492014-07-10 15:58:06 +02001219 char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001220 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001221 json_object *reply = NULL;
1222
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001223 DEBUG("Request: get (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001224
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001225 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001226 filter = get_param_string(request, "filter");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001227 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001228
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001229 if ((data = netconf_get(session_key, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001230 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001231 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001232 reply = create_data(data);
1233 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001234 }
1235 return reply;
1236}
1237
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001238json_object *handle_op_getconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001239{
1240 NC_DATASTORE ds_type_s = -1;
Tomas Cejka09629492014-07-10 15:58:06 +02001241 char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001242 char *data = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001243 char *source = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001244 json_object *reply = NULL;
1245
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001246 DEBUG("Request: get-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001247
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001248 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001249 filter = get_param_string(request, "filter");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001250
Tomas Cejka09629492014-07-10 15:58:06 +02001251 source = get_param_string(request, "source");
1252 if (source != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001253 ds_type_s = parse_datastore(source);
1254 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001255 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001256
Tomas Cejkad5b53772013-06-08 23:01:07 +02001257 if (ds_type_s == -1) {
Tomas Cejka09629492014-07-10 15:58:06 +02001258 reply = create_error("Invalid source repository type requested.");
1259 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001260 }
1261
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001262 if ((data = netconf_getconfig(session_key, ds_type_s, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001263 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001264 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001265 reply = create_data(data);
1266 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001267 }
Tomas Cejka09629492014-07-10 15:58:06 +02001268finalize:
1269 CHECK_AND_FREE(filter);
1270 CHECK_AND_FREE(source);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001271 return reply;
1272}
1273
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001274json_object *handle_op_getschema(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001275{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001276 char *data = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001277 char *identifier = NULL;
1278 char *version = NULL;
1279 char *format = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001280 json_object *reply = NULL;
1281
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001282 DEBUG("Request: get-schema (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001283 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001284 identifier = get_param_string(request, "identifier");
1285 version = get_param_string(request, "version");
1286 format = get_param_string(request, "format");
Tomas Cejkab9e7efe2014-03-28 21:09:04 +01001287 pthread_mutex_unlock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001288
Tomas Cejkad5b53772013-06-08 23:01:07 +02001289 if (identifier == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001290 reply = create_error("No identifier for get-schema supplied.");
1291 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001292 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001293
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001294 DEBUG("get-schema(version: %s, format: %s)", version, format);
1295 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001296 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001297 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001298 reply = create_data(data);
1299 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001300 }
Tomas Cejka09629492014-07-10 15:58:06 +02001301finalize:
1302 CHECK_AND_FREE(identifier);
1303 CHECK_AND_FREE(version);
1304 CHECK_AND_FREE(format);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001305 return reply;
1306}
1307
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001308json_object *handle_op_editconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001309{
1310 NC_DATASTORE ds_type_s = -1;
1311 NC_DATASTORE ds_type_t = -1;
1312 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
1313 NC_EDIT_ERROPT_TYPE erropt_type = 0;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001314 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
Tomas Cejka09629492014-07-10 15:58:06 +02001315 char *defop = NULL;
1316 char *erropt = NULL;
1317 char *config = NULL;
1318 char *source = NULL;
1319 char *target = NULL;
1320 char *testopt = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001321 json_object *reply = NULL;
1322
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001323 DEBUG("Request: edit-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001324
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001325 pthread_mutex_lock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001326 /* get parameters */
Tomas Cejka09629492014-07-10 15:58:06 +02001327 defop = get_param_string(request, "default-operation");
1328 erropt = get_param_string(request, "error-option");
1329 target = get_param_string(request, "target");
1330 source = get_param_string(request, "source");
1331 config = get_param_string(request, "config");
1332 testopt = get_param_string(request, "test-option");
1333 pthread_mutex_unlock(&json_lock);
1334
1335 if (target != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001336 ds_type_t = parse_datastore(target);
1337 }
Tomas Cejka09629492014-07-10 15:58:06 +02001338 if (source != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001339 ds_type_s = parse_datastore(source);
1340 } else {
1341 /* source is optional, default value is config */
1342 ds_type_s = NC_DATASTORE_CONFIG;
1343 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001344
Tomas Cejkad5b53772013-06-08 23:01:07 +02001345 if (defop != NULL) {
1346 if (strcmp(defop, "merge") == 0) {
1347 defop_type = NC_EDIT_DEFOP_MERGE;
1348 } else if (strcmp(defop, "replace") == 0) {
1349 defop_type = NC_EDIT_DEFOP_REPLACE;
1350 } else if (strcmp(defop, "none") == 0) {
1351 defop_type = NC_EDIT_DEFOP_NONE;
1352 } else {
Tomas Cejka09629492014-07-10 15:58:06 +02001353 reply = create_error("Invalid default-operation parameter.");
1354 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001355 }
1356 } else {
1357 defop_type = NC_EDIT_DEFOP_NOTSET;
1358 }
1359
Tomas Cejkad5b53772013-06-08 23:01:07 +02001360 if (erropt != NULL) {
1361 if (strcmp(erropt, "continue-on-error") == 0) {
1362 erropt_type = NC_EDIT_ERROPT_CONT;
1363 } else if (strcmp(erropt, "stop-on-error") == 0) {
1364 erropt_type = NC_EDIT_ERROPT_STOP;
1365 } else if (strcmp(erropt, "rollback-on-error") == 0) {
1366 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
1367 } else {
Tomas Cejka09629492014-07-10 15:58:06 +02001368 reply = create_error("Invalid error-option parameter.");
1369 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001370 }
1371 } else {
1372 erropt_type = 0;
1373 }
1374
Tomas Cejkad5b53772013-06-08 23:01:07 +02001375 if (ds_type_t == -1) {
Tomas Cejka09629492014-07-10 15:58:06 +02001376 reply = create_error("Invalid target repository type requested.");
1377 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001378 }
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001379 if (ds_type_s == NC_DATASTORE_CONFIG) {
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001380 if (config == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001381 reply = create_error("Invalid config data parameter.");
1382 goto finalize;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001383 }
1384 } else if (ds_type_s == NC_DATASTORE_URL){
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001385 if (config == NULL) {
1386 config = "";
1387 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001388 }
1389
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001390 if (testopt != NULL) {
1391 testopt_type = parse_testopt(testopt);
1392 } else {
1393 testopt_type = NC_EDIT_TESTOPT_TESTSET;
1394 }
1395
1396 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 +01001397
1398 CHECK_ERR_SET_REPLY
Tomas Cejka09629492014-07-10 15:58:06 +02001399finalize:
1400 CHECK_AND_FREE(defop);
1401 CHECK_AND_FREE(erropt);
1402 CHECK_AND_FREE(config);
1403 CHECK_AND_FREE(source);
1404 CHECK_AND_FREE(target);
1405 CHECK_AND_FREE(testopt);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001406 return reply;
1407}
1408
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001409json_object *handle_op_copyconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001410{
1411 NC_DATASTORE ds_type_s = -1;
1412 NC_DATASTORE ds_type_t = -1;
Tomas Cejka09629492014-07-10 15:58:06 +02001413 char *config = NULL;
1414 char *target = NULL;
1415 char *source = NULL;
1416 char *uri_src = NULL;
1417 char *uri_trg = NULL;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001418
Tomas Cejkad5b53772013-06-08 23:01:07 +02001419 json_object *reply = NULL;
1420
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001421 DEBUG("Request: copy-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001422
1423 /* get parameters */
Tomas Cejka09629492014-07-10 15:58:06 +02001424 pthread_mutex_lock(&json_lock);
1425 target = get_param_string(request, "target");
1426 source = get_param_string(request, "source");
1427 config = get_param_string(request, "config");
1428 uri_src = get_param_string(request, "uri-source");
1429 uri_trg = get_param_string(request, "uri-target");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001430 pthread_mutex_unlock(&json_lock);
1431
Tomas Cejka09629492014-07-10 15:58:06 +02001432 if (target != NULL) {
1433 ds_type_t = parse_datastore(target);
1434 }
1435 if (source != NULL) {
1436 ds_type_s = parse_datastore(source);
1437 } else {
1438 /* source == NULL *//* no explicit source specified -> use config data */
Tomas Cejkad5b53772013-06-08 23:01:07 +02001439 ds_type_s = NC_DATASTORE_CONFIG;
Tomas Cejka09629492014-07-10 15:58:06 +02001440 }
1441 if (ds_type_s == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001442 /* source datastore specified, but it is invalid */
Tomas Cejka09629492014-07-10 15:58:06 +02001443 reply = create_error("Invalid source repository type requested.");
1444 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001445 }
1446
1447 if (ds_type_t == -1) {
1448 /* invalid target datastore specified */
Tomas Cejka09629492014-07-10 15:58:06 +02001449 reply = create_error("Invalid target repository type requested.");
1450 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001451 }
1452
Tomas Cejka55c6df52014-02-20 12:59:33 +01001453 /* source can be missing when config is given */
1454 if (source == NULL && config == NULL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001455 reply = create_error("invalid input parameters - source and config is required.");
Tomas Cejka09629492014-07-10 15:58:06 +02001456 goto finalize;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001457 }
1458
1459 if (ds_type_s == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001460 if (uri_src == NULL) {
1461 uri_src = "";
1462 }
1463 }
1464 if (ds_type_t == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001465 if (uri_trg == NULL) {
1466 uri_trg = "";
1467 }
1468 }
1469 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001470
1471 CHECK_ERR_SET_REPLY
1472
Tomas Cejka09629492014-07-10 15:58:06 +02001473finalize:
1474 CHECK_AND_FREE(config);
1475 CHECK_AND_FREE(target);
1476 CHECK_AND_FREE(source);
1477 CHECK_AND_FREE(uri_src);
1478 CHECK_AND_FREE(uri_trg);
1479
Tomas Cejkad5b53772013-06-08 23:01:07 +02001480 return reply;
1481}
1482
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001483json_object *handle_op_generic(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001484{
1485 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001486 char *config = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001487 char *data = NULL;
1488
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001489 DEBUG("Request: generic request for session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001490
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001491 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001492 config = get_param_string(request, "content");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001493 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001494
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001495 reply = netconf_generic(session_key, config, &data);
1496 if (reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001497 GETSPEC_ERR_REPLY
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001498 if (err_reply != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001499 /* use filled err_reply from libnetconf's callback */
1500 reply = err_reply;
1501 }
1502 } else {
1503 if (data == NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001504 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001505 reply = json_object_new_object();
1506 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001507 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001508 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001509 reply = create_data(data);
1510 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001511 }
1512 }
Tomas Cejka09629492014-07-10 15:58:06 +02001513 CHECK_AND_FREE(config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001514 return reply;
1515}
1516
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001517json_object *handle_op_disconnect(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001518{
1519 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001520 DEBUG("Request: Disconnect session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001521
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001522 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001523 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001524 } else {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001525 reply = create_ok();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001526 }
1527 return reply;
1528}
1529
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001530json_object *handle_op_kill(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001531{
1532 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001533 char *sid = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001534
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001535 DEBUG("Request: kill-session, session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001536
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001537 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001538 sid = get_param_string(request, "session-id");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001539 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001540
1541 if (sid == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001542 reply = create_error("Missing session-id parameter.");
1543 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001544 }
1545
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001546 reply = netconf_killsession(session_key, sid);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001547
1548 CHECK_ERR_SET_REPLY
1549
Tomas Cejka09629492014-07-10 15:58:06 +02001550finalize:
1551 CHECK_AND_FREE(sid);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001552 return reply;
1553}
1554
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001555json_object *handle_op_reloadhello(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001556{
Tomas Cejka47387fd2013-06-10 20:37:46 +02001557 struct nc_session *temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001558 struct session_with_mutex * locked_session = NULL;
1559 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001560 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001561
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001562 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001563 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001564 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001565 return NULL;
1566 }
1567
Tomas Cejka09629492014-07-10 15:58:06 +02001568 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 +02001569 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001570 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001571 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001572 DEBUG("creating temporal NC session.");
Tomas Cejka47387fd2013-06-10 20:37:46 +02001573 temp_session = nc_session_connect_channel(locked_session->session, NULL);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001574 if (temp_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001575 prepare_status_message(locked_session, temp_session);
1576 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001577 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001578 temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001579 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001580 DEBUG("Reload hello failed due to channel establishment");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001581 reply = create_error("Reload was unsuccessful, connection failed.");
1582 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001583 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001584 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001585 DEBUG("UNLOCK wrlock %s", __func__);
1586 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001587 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka93b0e492014-03-26 15:47:45 +01001588 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001589 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001590 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001591 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001592 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001593 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001594 reply = create_error("Invalid session identifier.");
1595 }
1596
1597 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
1598 reply = locked_session->hello_message;
1599 }
1600 return reply;
1601}
1602
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001603json_object *handle_op_info(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001604{
1605 json_object *reply = NULL;
Tomas Cejka47387fd2013-06-10 20:37:46 +02001606 struct session_with_mutex * locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001607 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001608
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001609 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001610 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001611 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001612 }
1613
Tomas Cejka09629492014-07-10 15:58:06 +02001614 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 +02001615 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001616 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001617 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001618 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001619 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001620 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001621 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001622 if (locked_session->hello_message != NULL) {
1623 reply = locked_session->hello_message;
1624 } else {
1625 reply = create_error("Invalid session identifier.");
1626 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001627 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001628 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001629 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001630 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001631 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001632 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001633 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001634 reply = create_error("Invalid session identifier.");
1635 }
1636
Tomas Cejka47387fd2013-06-10 20:37:46 +02001637
Tomas Cejkad5b53772013-06-08 23:01:07 +02001638 return reply;
1639}
1640
Tomas Cejka6b886e02013-07-05 09:53:17 +02001641void notification_history(time_t eventtime, const char *content)
1642{
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001643 json_object *notif_history_array = (json_object *) pthread_getspecific(notif_history_key);
1644 if (notif_history_array == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001645 ERROR("No list of notification history found.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001646 return;
1647 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001648 DEBUG("Got notification from history %lu.", (long unsigned) eventtime);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001649 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001650 json_object *notif = json_object_new_object();
1651 if (notif == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001652 ERROR("Could not allocate memory for notification (json).");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001653 goto failed;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001654 }
1655 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
1656 json_object_object_add(notif, "content", json_object_new_string(content));
1657 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001658failed:
1659 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001660}
1661
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001662json_object *handle_op_ntfgethistory(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001663{
1664 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001665 json_object *js_tmp = NULL;
1666 char *sid = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001667 struct session_with_mutex *locked_session = NULL;
1668 struct nc_session *temp_session = NULL;
1669 nc_rpc *rpc = NULL;
1670 time_t start = 0;
1671 time_t stop = 0;
Tomas Cejka09629492014-07-10 15:58:06 +02001672 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001673
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001674 DEBUG("Request: get notification history, session %s", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001675
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001676 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001677 sid = get_param_string(request, "session");
1678
1679 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
1680 from = json_object_get_int64(js_tmp);
1681 json_object_put(js_tmp);
1682 }
1683 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
1684 to = json_object_get_int64(js_tmp);
1685 json_object_put(js_tmp);
1686 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001687 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001688
1689 start = time(NULL) + from;
1690 stop = time(NULL) + to;
1691
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001692 DEBUG("notification history interval %li %li", (long int) from, (long int) to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001693
1694 if (sid == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001695 reply = create_error("Missing session parameter.");
1696 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001697 }
1698
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001699 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001700 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001701 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka09629492014-07-10 15:58:06 +02001702 reply = create_error("Internal lock failed.");
1703 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001704 }
1705
1706 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1707 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001708 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001709 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001710 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001711 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001712 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001713 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001714 DEBUG("creating temporal NC session.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001715 temp_session = nc_session_connect_channel(locked_session->session, NULL);
1716 if (temp_session != NULL) {
1717 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
1718 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001719 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001720 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001721 DEBUG("notifications: creating an rpc request failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001722 reply = create_error("notifications: creating an rpc request failed.");
1723 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001724 }
1725
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001726 DEBUG("Send NC subscribe.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001727 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001728 json_object *res = netconf_unlocked_op(temp_session, rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001729 if (res != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001730 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001731 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001732 DEBUG("Subscription RPC failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001733 reply = res;
1734 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001735 }
1736 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
1737
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001738 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001739 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001740 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001741 pthread_mutex_lock(&ntf_history_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001742 pthread_mutex_lock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001743 json_object *notif_history_array = json_object_new_array();
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001744 pthread_mutex_unlock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001745 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001746 ERROR("notif_history: cannot set thread-specific hash value.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001747 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001748
1749 ncntf_dispatch_receive(temp_session, notification_history);
1750
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001751 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001752 reply = json_object_new_object();
1753 json_object_object_add(reply, "notifications", notif_history_array);
1754 //json_object_put(notif_history_array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001755 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001756
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001757 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001758 pthread_mutex_unlock(&ntf_history_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001759 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001760 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001761 temp_session = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001762 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001763 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001764 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001765 DEBUG("Get history of notification failed due to channel establishment");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001766 reply = create_error("Get history of notification was unsuccessful, connection failed.");
1767 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001768 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001769 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001770 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001771 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001772 }
1773 reply = create_error("Invalid session identifier.");
1774 }
1775
Tomas Cejka09629492014-07-10 15:58:06 +02001776finalize:
1777 CHECK_AND_FREE(sid);
Tomas Cejka4003a702013-10-01 00:02:45 +02001778 return reply;
1779}
1780
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001781json_object *handle_op_validate(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02001782{
1783 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001784 char *sid = NULL;
1785 char *target = NULL;
1786 char *url = NULL;
Tomas Cejka4003a702013-10-01 00:02:45 +02001787 nc_rpc *rpc = NULL;
1788 NC_DATASTORE target_ds;
1789
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001790 DEBUG("Request: validate datastore, session %s", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02001791
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001792 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001793 sid = get_param_string(request, "session");
1794 target = get_param_string(request, "target");
1795 url = get_param_string(request, "url");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001796 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02001797
1798
1799 if ((sid == NULL) || (target == NULL)) {
Tomas Cejka09629492014-07-10 15:58:06 +02001800 reply = create_error("Missing session parameter.");
1801 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001802 }
Tomas Cejka4003a702013-10-01 00:02:45 +02001803
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001804 /* validation */
1805 target_ds = parse_datastore(target);
1806 if (target_ds == NC_DATASTORE_URL) {
1807 if (url != NULL) {
1808 rpc = nc_rpc_validate(target_ds, url);
Tomas Cejka4003a702013-10-01 00:02:45 +02001809 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001810 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
Tomas Cejka4003a702013-10-01 00:02:45 +02001811 || (target_ds == NC_DATASTORE_CANDIDATE)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001812 rpc = nc_rpc_validate(target_ds);
1813 }
1814 if (rpc == NULL) {
1815 DEBUG("mod_netconf: creating rpc request failed");
1816 reply = create_error("Creation of RPC request failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001817 goto finalize;
Tomas Cejka4003a702013-10-01 00:02:45 +02001818 }
1819
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001820 DEBUG("Request: validate datastore");
1821 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001822
1823 CHECK_ERR_SET_REPLY
1824
1825 if (reply == NULL) {
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001826 DEBUG("Request: validation ok.");
1827 reply = create_ok();
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001828 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001829 }
1830 nc_rpc_free (rpc);
Tomas Cejka09629492014-07-10 15:58:06 +02001831finalize:
1832 CHECK_AND_FREE(sid);
1833 CHECK_AND_FREE(target);
1834 CHECK_AND_FREE(url);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001835 return reply;
1836}
1837
David Kupka8e60a372012-09-04 09:15:20 +02001838void * thread_routine (void * arg)
1839{
1840 void * retval = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001841 struct pollfd fds;
Tomas Cejka00635972013-06-03 15:10:52 +02001842 json_object *request = NULL;
1843 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001844 json_object *js_tmp = NULL;
1845 int operation = (-1);
1846 NC_DATASTORE ds_type_t = -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001847 int status = 0;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001848 const char *msgtext;
Tomas Cejka09629492014-07-10 15:58:06 +02001849 char *session_key = NULL;
1850 char *target = NULL;
1851 char *url = NULL;
Tomas Cejka64b87482013-06-03 16:30:53 +02001852 char *chunked_out_msg = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001853 apr_pool_t * pool = ((struct pass_to_thread*)arg)->pool;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001854 //server_rec * server = ((struct pass_to_thread*)arg)->server;
David Kupka8e60a372012-09-04 09:15:20 +02001855 int client = ((struct pass_to_thread*)arg)->client;
1856
Tomas Cejka00635972013-06-03 15:10:52 +02001857 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001858
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001859 /* init thread specific err_reply memory */
Tomas Cejka442258e2014-04-01 18:17:18 +02001860 create_err_reply_p();
1861
David Kupka8e60a372012-09-04 09:15:20 +02001862 while (!isterminated) {
1863 fds.fd = client;
1864 fds.events = POLLIN;
1865 fds.revents = 0;
1866
1867 status = poll(&fds, 1, 1000);
1868
1869 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
1870 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
David Kupka8e60a372012-09-04 09:15:20 +02001871 continue;
1872 } else if (status < 0) {
1873 /* 0: poll time outed
1874 * close socket and ignore this request from the client, it can try it again
1875 * -1: poll failed
1876 * something wrong happend, close this socket and wait for another request
1877 */
David Kupka8e60a372012-09-04 09:15:20 +02001878 close(client);
1879 break;
1880 }
1881 /* status > 0 */
1882
1883 /* check the status of the socket */
1884
1885 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
1886 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
1887 /* close client's socket (it's probably already closed by client */
David Kupka8e60a372012-09-04 09:15:20 +02001888 close(client);
1889 break;
1890 }
1891
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001892 DEBUG("Get framed message...");
1893 buffer = get_framed_message(client);
David Kupka8e60a372012-09-04 09:15:20 +02001894
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001895 DEBUG("Check read buffer.");
David Kupka8e60a372012-09-04 09:15:20 +02001896 if (buffer != NULL) {
Tomas Cejka00635972013-06-03 15:10:52 +02001897 enum json_tokener_error jerr;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001898 pthread_mutex_lock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001899 request = json_tokener_parse_verbose(buffer, &jerr);
1900 if (jerr != json_tokener_success) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001901 ERROR("JSON parsing error");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001902 pthread_mutex_unlock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001903 continue;
1904 }
David Kupka8e60a372012-09-04 09:15:20 +02001905
Tomas Cejka09629492014-07-10 15:58:06 +02001906 session_key = get_param_string(request, "session");
1907 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
1908 operation = json_object_get_int(js_tmp);
1909 json_object_put(js_tmp);
1910 js_tmp = NULL;
1911 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001912 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001913 if (operation == -1) {
1914 reply = create_error("Missing operation type form frontend.");
1915 goto send_reply;
1916 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001917
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001918 DEBUG("operation %d session_key %s.", operation, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001919 /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
1920 if (operation != MSG_CONNECT && session_key == NULL) {
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001921 reply = create_error("Missing session specification.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001922 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001923 msgtext = json_object_to_json_string(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001924 pthread_mutex_unlock(&json_lock);
1925
David Kupka8e60a372012-09-04 09:15:20 +02001926 send(client, msgtext, strlen(msgtext) + 1, 0);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001927
1928 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001929 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001930 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001931 /* there is some stupid client, so close the connection to give a chance to some other client */
1932 close(client);
1933 break;
1934 }
1935
David Kupka8e60a372012-09-04 09:15:20 +02001936 /* null global JSON error-reply */
Tomas Cejka442258e2014-04-01 18:17:18 +02001937 clean_err_reply();
David Kupka8e60a372012-09-04 09:15:20 +02001938
1939 /* prepare reply envelope */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001940 if (reply != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001941 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001942 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001943 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001944 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001945 reply = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001946
1947 /* process required operation */
1948 switch (operation) {
1949 case MSG_CONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001950 reply = handle_op_connect(pool, request);
David Kupka8e60a372012-09-04 09:15:20 +02001951 break;
1952 case MSG_GET:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001953 reply = handle_op_get(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001954 break;
1955 case MSG_GETCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001956 reply = handle_op_getconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001957 break;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001958 case MSG_GETSCHEMA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001959 reply = handle_op_getschema(pool, request, session_key);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001960 break;
David Kupka8e60a372012-09-04 09:15:20 +02001961 case MSG_EDITCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001962 reply = handle_op_editconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001963 break;
1964 case MSG_COPYCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001965 reply = handle_op_copyconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001966 break;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001967
David Kupka8e60a372012-09-04 09:15:20 +02001968 case MSG_DELETECONFIG:
David Kupka8e60a372012-09-04 09:15:20 +02001969 case MSG_LOCK:
David Kupka8e60a372012-09-04 09:15:20 +02001970 case MSG_UNLOCK:
Tomas Cejkad5b53772013-06-08 23:01:07 +02001971 /* get parameters */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001972 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001973 target = get_param_string(request, "target");
1974 pthread_mutex_unlock(&json_lock);
1975 if (target != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001976 ds_type_t = parse_datastore(target);
1977 }
David Kupka8e60a372012-09-04 09:15:20 +02001978
1979 if (ds_type_t == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001980 reply = create_error("Invalid target repository type requested.");
David Kupka8e60a372012-09-04 09:15:20 +02001981 break;
1982 }
David Kupka8e60a372012-09-04 09:15:20 +02001983 switch(operation) {
1984 case MSG_DELETECONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001985 DEBUG("Request: delete-config (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001986 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001987 url = get_param_string(request, "url");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001988 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001989 reply = netconf_deleteconfig(session_key, ds_type_t, url);
David Kupka8e60a372012-09-04 09:15:20 +02001990 break;
1991 case MSG_LOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001992 DEBUG("Request: lock (session %s)", session_key);
1993 reply = netconf_lock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001994 break;
1995 case MSG_UNLOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001996 DEBUG("Request: unlock (session %s)", session_key);
1997 reply = netconf_unlock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001998 break;
1999 default:
Tomas Cejkac7929632013-10-24 19:25:15 +02002000 reply = create_error("Internal: Unknown request type.");
David Kupka8e60a372012-09-04 09:15:20 +02002001 break;
2002 }
2003
Tomas Cejka442258e2014-04-01 18:17:18 +02002004 CHECK_ERR_SET_REPLY
Tomas Cejkac7929632013-10-24 19:25:15 +02002005 if (reply == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02002006 reply = create_ok();
David Kupka8e60a372012-09-04 09:15:20 +02002007 }
2008 break;
2009 case MSG_KILL:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002010 reply = handle_op_kill(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002011 break;
2012 case MSG_DISCONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002013 reply = handle_op_disconnect(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002014 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02002015 case MSG_RELOADHELLO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002016 reply = handle_op_reloadhello(pool, request, session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002017 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02002018 case MSG_INFO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002019 reply = handle_op_info(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002020 break;
2021 case MSG_GENERIC:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002022 reply = handle_op_generic(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002023 break;
Tomas Cejka6b886e02013-07-05 09:53:17 +02002024 case MSG_NTF_GETHISTORY:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002025 reply = handle_op_ntfgethistory(pool, request, session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02002026 break;
Tomas Cejka4003a702013-10-01 00:02:45 +02002027 case MSG_VALIDATE:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002028 reply = handle_op_validate(pool, request, session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02002029 break;
David Kupka8e60a372012-09-04 09:15:20 +02002030 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002031 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002032 reply = create_error("Operation not supported.");
David Kupka8e60a372012-09-04 09:15:20 +02002033 break;
2034 }
Tomas Cejka09629492014-07-10 15:58:06 +02002035 /* free parameters */
2036 CHECK_AND_FREE(session_key);
2037 CHECK_AND_FREE(url);
2038 CHECK_AND_FREE(target);
2039 request = NULL;
2040 operation = (-1);
2041 ds_type_t = (-1);
2042
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002043 DEBUG("Clean request json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02002044 if (request != NULL) {
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002045 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002046 json_object_put(request);
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002047 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002048 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002049 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02002050
2051
2052send_reply:
David Kupka1e3e4c82012-09-04 09:32:15 +02002053 /* send reply to caller */
2054 if (reply != NULL) {
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002055 pthread_mutex_lock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02002056 msgtext = json_object_to_json_string(reply);
Tomas Cejka866f2282014-09-18 15:20:26 +02002057 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int) strlen(msgtext), msgtext) == -1) {
Tomas Cejka00635972013-06-03 15:10:52 +02002058 if (buffer != NULL) {
2059 free(buffer);
2060 buffer = NULL;
2061 }
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002062 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02002063 break;
2064 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002065 pthread_mutex_unlock(&json_lock);
2066
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002067 DEBUG("Send framed reply json object.");
Tomas Cejka64b87482013-06-03 16:30:53 +02002068 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002069 DEBUG("Clean reply json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002070 pthread_mutex_lock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02002071 json_object_put(reply);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002072 reply = NULL;
2073 DEBUG("Clean message buffer.");
Tomas Cejka09629492014-07-10 15:58:06 +02002074 CHECK_AND_FREE(chunked_out_msg);
Tomas Cejka64b87482013-06-03 16:30:53 +02002075 chunked_out_msg = NULL;
Tomas Cejka00635972013-06-03 15:10:52 +02002076 if (buffer != NULL) {
2077 free(buffer);
2078 buffer = NULL;
2079 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002080 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02002081 clean_err_reply();
David Kupka1e3e4c82012-09-04 09:32:15 +02002082 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002083 ERROR("Reply is NULL, shouldn't be...");
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002084 continue;
David Kupka8e60a372012-09-04 09:15:20 +02002085 }
2086 }
2087 }
David Kupka8e60a372012-09-04 09:15:20 +02002088 free (arg);
2089
Tomas Cejka442258e2014-04-01 18:17:18 +02002090 free_err_reply();
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002091
David Kupka8e60a372012-09-04 09:15:20 +02002092 return retval;
2093}
2094
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002095/**
2096 * \brief Close all open NETCONF sessions.
2097 *
2098 * During termination of mod_netconf, it is useful to close all remaining
2099 * sessions. This function iterates over the list of sessions and close them
2100 * all.
2101 *
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002102 * \param[in] p apr pool needed for hash table iterating
2103 * \param[in] ht hash table of session_with_mutex structs
2104 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002105static void close_all_nc_sessions(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002106{
2107 apr_hash_index_t *hi;
2108 void *val = NULL;
2109 struct session_with_mutex *swm = NULL;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002110 const char *hashed_key = NULL;
2111 apr_ssize_t hashed_key_length;
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002112 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002113
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002114 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002115 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002116 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002117 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002118 return;
2119 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002120 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002121 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002122 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002123 swm = (struct session_with_mutex *) val;
2124 if (swm != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002125 DEBUG("LOCK mutex %s", __func__);
2126 pthread_mutex_lock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002127 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002128 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002129 pthread_mutex_unlock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002130
2131 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002132 close_and_free_session(swm);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002133 }
2134 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002135 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002136 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002137 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002138 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002139 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002140}
2141
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002142static void check_timeout_and_close(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002143{
2144 apr_hash_index_t *hi;
2145 void *val = NULL;
2146 struct nc_session *ns = NULL;
2147 struct session_with_mutex *swm = NULL;
2148 const char *hashed_key = NULL;
2149 apr_ssize_t hashed_key_length;
2150 apr_time_t current_time = apr_time_now();
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002151 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002152
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002153 /* get exclusive access to sessions_list (conns) */
Tomas Cejka866f2282014-09-18 15:20:26 +02002154 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002155 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002156 return;
2157 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002158 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002159 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
2160 swm = (struct session_with_mutex *) val;
2161 if (swm == NULL) {
2162 continue;
2163 }
2164 ns = swm->session;
2165 if (ns == NULL) {
2166 continue;
2167 }
2168 pthread_mutex_lock(&swm->lock);
2169 if ((current_time - swm->last_activity) > apr_time_from_sec(ACTIVITY_TIMEOUT)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002170 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002171 /* remove session from the active sessions list */
Tomas Cejka47387fd2013-06-10 20:37:46 +02002172 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
2173 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 {
2178 pthread_mutex_unlock(&swm->lock);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002179 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002180 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002181 /* get exclusive access to sessions_list (conns) */
Tomas Cejka866f2282014-09-18 15:20:26 +02002182 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002183 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002184 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002185}
2186
2187
2188/**
Radek Krejcif23850c2012-07-23 16:14:17 +02002189 * This is actually implementation of NETCONF client
2190 * - requests are received from UNIX socket in the predefined format
2191 * - results are replied through the same way
2192 * - the daemon run as a separate process, but it is started and stopped
2193 * automatically by Apache.
2194 *
2195 */
Radek Krejci469aab82012-07-22 18:42:20 +02002196static void forked_proc(apr_pool_t * pool, server_rec * server)
2197{
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002198 struct timeval tv;
Radek Krejci469aab82012-07-22 18:42:20 +02002199 struct sockaddr_un local, remote;
David Kupka8e60a372012-09-04 09:15:20 +02002200 int lsock, client, ret, i, pthread_count = 0;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002201 unsigned int olds = 0, timediff = 0;
David Kupka8e60a372012-09-04 09:15:20 +02002202 socklen_t len;
Radek Krejciae021c12012-07-25 18:03:52 +02002203 mod_netconf_cfg *cfg;
David Kupka8e60a372012-09-04 09:15:20 +02002204 struct pass_to_thread * arg;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002205 pthread_t * ptids = calloc(1, sizeof(pthread_t));
David Kupka8e60a372012-09-04 09:15:20 +02002206 struct timespec maxtime;
2207 pthread_rwlockattr_t lock_attrs;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002208 char *sockname = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +02002209 #ifdef WITH_NOTIFICATIONS
2210 char use_notifications = 0;
2211 #endif
David Kupka8e60a372012-09-04 09:15:20 +02002212
Tomas Cejka6b886e02013-07-05 09:53:17 +02002213 http_server = server;
2214
Tomas Cejka404d37e2013-04-13 02:31:35 +02002215 /* wait at most 5 seconds for every thread to terminate */
David Kupka8e60a372012-09-04 09:15:20 +02002216 maxtime.tv_sec = 5;
2217 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02002218
Tomas Cejka04e08f42014-03-27 19:52:34 +01002219#ifdef HAVE_UNIXD_SETUP_CHILD
Radek Krejcif23850c2012-07-23 16:14:17 +02002220 /* change uid and gid of process for security reasons */
Radek Krejci469aab82012-07-22 18:42:20 +02002221 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01002222#else
2223# ifdef SU_GROUP
2224 if (strlen(SU_GROUP) > 0) {
2225 struct group *g = getgrnam(SU_GROUP);
2226 if (g == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002227 ERROR("GID (%s) was not found.", SU_GROUP);
Tomas Cejka04e08f42014-03-27 19:52:34 +01002228 return;
2229 }
2230 if (setgid(g->gr_gid) != 0) {
2231
Tomas Cejkacf44e522015-04-24 17:29:21 +02002232 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
Tomas Cejka04e08f42014-03-27 19:52:34 +01002233 return;
2234 }
2235 }
2236# else
2237 DEBUG("no SU_GROUP");
2238# endif
2239# ifdef SU_USER
2240 if (strlen(SU_USER) > 0) {
2241 struct passwd *p = getpwnam(SU_USER);
2242 if (p == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002243 ERROR("UID (%s) was not found.", SU_USER);
Tomas Cejka04e08f42014-03-27 19:52:34 +01002244 return;
2245 }
2246 if (setuid(p->pw_uid) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002247 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
Tomas Cejka04e08f42014-03-27 19:52:34 +01002248 return;
2249 }
2250 }
2251# else
2252 DEBUG("no SU_USER");
2253# endif
2254#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002255
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002256 if (server != NULL) {
2257 cfg = ap_get_module_config(server->module_config, &netconf_module);
2258 if (cfg == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002259 ERROR("Getting mod_netconf configuration failed");
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002260 return;
2261 }
2262 sockname = cfg->sockname;
2263 } else {
2264 sockname = SOCKET_FILENAME;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002265 }
Radek Krejci469aab82012-07-22 18:42:20 +02002266
Tomas Cejka04e08f42014-03-27 19:52:34 +01002267 /* try to remove if exists */
2268 unlink(sockname);
2269
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002270 /* create listening UNIX socket to accept incoming connections */
Radek Krejci469aab82012-07-22 18:42:20 +02002271 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002272 ERROR("Creating socket failed (%s)", strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002273 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002274 }
2275
2276 local.sun_family = AF_UNIX;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002277 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
Radek Krejci469aab82012-07-22 18:42:20 +02002278 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
2279
2280 if (bind(lsock, (struct sockaddr *) &local, len) == -1) {
2281 if (errno == EADDRINUSE) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002282 ERROR("mod_netconf socket address already in use");
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002283 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002284 }
Tomas Cejkacf44e522015-04-24 17:29:21 +02002285 ERROR("Binding socket failed (%s)", strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002286 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002287 }
2288
2289 if (listen(lsock, MAX_SOCKET_CL) == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002290 ERROR("Setting up listen socket failed (%s)", strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002291 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002292 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002293 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02002294
Tomas Cejka04e08f42014-03-27 19:52:34 +01002295 uid_t user = -1;
2296 if (strlen(CHOWN_USER) > 0) {
2297 struct passwd *p = getpwnam(CHOWN_USER);
2298 if (p != NULL) {
2299 user = p->pw_uid;
2300 }
2301 }
2302 gid_t group = -1;
2303 if (strlen(CHOWN_GROUP) > 0) {
2304 struct group *g = getgrnam(CHOWN_GROUP);
2305 if (g != NULL) {
2306 group = g->gr_gid;
2307 }
2308 }
2309 if (chown(sockname, user, group) == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002310 ERROR("Chown on socket file failed (%s).", strerror(errno));
Tomas Cejka04e08f42014-03-27 19:52:34 +01002311 }
2312
Tomas Cejkaba21b382013-04-13 02:37:32 +02002313 /* prepare internal lists */
2314 netconf_sessions_list = apr_hash_make(pool);
2315
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002316 #ifdef WITH_NOTIFICATIONS
Tomas Cejka47387fd2013-06-10 20:37:46 +02002317 if (notification_init(pool, server) == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002318 ERROR("libwebsockets initialization failed");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002319 use_notifications = 0;
2320 } else {
2321 use_notifications = 1;
2322 }
2323 #endif
2324
Radek Krejci469aab82012-07-22 18:42:20 +02002325 /* setup libnetconf's callbacks */
2326 nc_verbosity(NC_VERB_DEBUG);
Radek Krejci469aab82012-07-22 18:42:20 +02002327 nc_callback_print(clb_print);
2328 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
2329 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
2330 nc_callback_sshauth_password(netconf_callback_sshauth_password);
Radek Krejcic11fd862012-07-26 12:41:21 +02002331 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02002332
2333 /* disable publickey authentication */
2334 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
2335
David Kupka8e60a372012-09-04 09:15:20 +02002336 /* create mutex protecting session list */
2337 pthread_rwlockattr_init(&lock_attrs);
2338 /* rwlock is shared only with threads in this process */
2339 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
2340 /* create rw lock */
2341 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002342 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002343 goto error_exit;
David Kupka8e60a372012-09-04 09:15:20 +02002344 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002345 pthread_mutex_init(&ntf_history_lock, NULL);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002346 pthread_mutex_init(&json_lock, NULL);
Tomas Cejkacf44e522015-04-24 17:29:21 +02002347 DEBUG("Initialization of notification history.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002348 if (pthread_key_create(&notif_history_key, NULL) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002349 ERROR("Initialization of notification history failed.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002350 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002351 if (pthread_key_create(&err_reply_key, NULL) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002352 ERROR("Initialization of reply key failed.");
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002353 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02002354
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002355 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002356 while (isterminated == 0) {
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002357 gettimeofday(&tv, NULL);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002358 timediff = (unsigned int)tv.tv_sec - olds;
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002359 #ifdef WITH_NOTIFICATIONS
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002360 if (use_notifications == 1) {
2361 notification_handle();
2362 }
2363 #endif
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002364 if (timediff > ACTIVITY_CHECK_INTERVAL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002365 check_timeout_and_close(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002366 }
Radek Krejci469aab82012-07-22 18:42:20 +02002367
2368 /* open incoming connection if any */
David Kupka8e60a372012-09-04 09:15:20 +02002369 len = sizeof(remote);
2370 client = accept(lsock, (struct sockaddr *) &remote, &len);
Radek Krejci469aab82012-07-22 18:42:20 +02002371 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
2372 apr_sleep(SLEEP_TIME);
2373 continue;
2374 } else if (client == -1 && (errno == EINTR)) {
2375 continue;
2376 } else if (client == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002377 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
Radek Krejci469aab82012-07-22 18:42:20 +02002378 continue;
2379 }
Radek Krejci469aab82012-07-22 18:42:20 +02002380
2381 /* set client's socket as non-blocking */
Radek Krejcif23850c2012-07-23 16:14:17 +02002382 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002383
David Kupka8e60a372012-09-04 09:15:20 +02002384 arg = malloc (sizeof(struct pass_to_thread));
2385 arg->client = client;
2386 arg->pool = pool;
2387 arg->server = server;
2388 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02002389
David Kupka8e60a372012-09-04 09:15:20 +02002390 /* start new thread. It will serve this particular request and then terminate */
2391 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void*)arg)) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002392 ERROR("Creating POSIX thread failed: %d\n", ret);
David Kupka8e60a372012-09-04 09:15:20 +02002393 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002394 DEBUG("Thread %lu created", ptids[pthread_count]);
David Kupka8e60a372012-09-04 09:15:20 +02002395 pthread_count++;
2396 ptids = realloc (ptids, sizeof(pthread_t)*(pthread_count+1));
2397 ptids[pthread_count] = 0;
2398 }
Radek Krejci469aab82012-07-22 18:42:20 +02002399
David Kupka8e60a372012-09-04 09:15:20 +02002400 /* check if some thread already terminated, free some resources by joining it */
2401 for (i=0; i<pthread_count; i++) {
2402 if (pthread_tryjoin_np (ptids[i], (void**)&arg) == 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002403 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
David Kupka8e60a372012-09-04 09:15:20 +02002404 pthread_count--;
2405 if (pthread_count > 0) {
2406 /* place last Thread ID on the place of joined one */
2407 ptids[i] = ptids[pthread_count];
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002408 }
Radek Krejci469aab82012-07-22 18:42:20 +02002409 }
2410 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002411 DEBUG("Running %d threads", pthread_count);
Radek Krejci469aab82012-07-22 18:42:20 +02002412 }
2413
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002414 DEBUG("mod_netconf terminating...");
David Kupka8e60a372012-09-04 09:15:20 +02002415 /* join all threads */
2416 for (i=0; i<pthread_count; i++) {
2417 pthread_timedjoin_np (ptids[i], (void**)&arg, &maxtime);
2418 }
Radek Krejci469aab82012-07-22 18:42:20 +02002419
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002420 #ifdef WITH_NOTIFICATIONS
2421 notification_close();
2422 #endif
2423
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002424 /* close all NETCONF sessions */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002425 close_all_nc_sessions(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002426
David Kupka8e60a372012-09-04 09:15:20 +02002427 /* destroy rwlock */
2428 pthread_rwlock_destroy(&session_lock);
2429 pthread_rwlockattr_destroy(&lock_attrs);
2430
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002431 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02002432
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002433 free(ptids);
2434 close(lsock);
Radek Krejci469aab82012-07-22 18:42:20 +02002435 exit(APR_SUCCESS);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002436 return;
2437error_exit:
2438 close(lsock);
2439 free(ptids);
2440 return;
Radek Krejci469aab82012-07-22 18:42:20 +02002441}
2442
Radek Krejcif23850c2012-07-23 16:14:17 +02002443static void *mod_netconf_create_conf(apr_pool_t * pool, server_rec * s)
Radek Krejci469aab82012-07-22 18:42:20 +02002444{
Radek Krejcif23850c2012-07-23 16:14:17 +02002445 mod_netconf_cfg *config = apr_pcalloc(pool, sizeof(mod_netconf_cfg));
2446 apr_pool_create(&config->pool, pool);
2447 config->forkproc = NULL;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002448 config->sockname = SOCKET_FILENAME;
Radek Krejcif23850c2012-07-23 16:14:17 +02002449
2450 return (void *)config;
Radek Krejci469aab82012-07-22 18:42:20 +02002451}
2452
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002453#ifndef HTTPD_INDEPENDENT
Radek Krejci469aab82012-07-22 18:42:20 +02002454static int mod_netconf_master_init(apr_pool_t * pconf, apr_pool_t * ptemp,
2455 apr_pool_t * plog, server_rec * s)
2456{
Radek Krejcif23850c2012-07-23 16:14:17 +02002457 mod_netconf_cfg *config;
2458 apr_status_t res;
2459
Radek Krejci469aab82012-07-22 18:42:20 +02002460 /* These two help ensure that we only init once. */
Radek Krejcia332b692012-11-12 16:15:54 +01002461 void *data = NULL;
Radek Krejcif23850c2012-07-23 16:14:17 +02002462 const char *userdata_key = "netconf_ipc_init";
Radek Krejci469aab82012-07-22 18:42:20 +02002463
2464 /*
2465 * The following checks if this routine has been called before.
2466 * This is necessary because the parent process gets initialized
2467 * a couple of times as the server starts up.
2468 */
2469 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2470 if (!data) {
2471 apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
2472 return (OK);
2473 }
2474
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002475 DEBUG("creating mod_netconf daemon");
Radek Krejcif23850c2012-07-23 16:14:17 +02002476 config = ap_get_module_config(s->module_config, &netconf_module);
Radek Krejcidfaa6ea2012-07-23 09:04:43 +02002477
Radek Krejcif23850c2012-07-23 16:14:17 +02002478 if (config && config->forkproc == NULL) {
2479 config->forkproc = apr_pcalloc(config->pool, sizeof(apr_proc_t));
2480 res = apr_proc_fork(config->forkproc, config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002481 switch (res) {
2482 case APR_INCHILD:
2483 /* set signal handler */
Radek Krejcif23850c2012-07-23 16:14:17 +02002484 apr_signal_init(config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002485 apr_signal(SIGTERM, signal_handler);
2486
2487 /* log start of the separated NETCONF communication process */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002488 DEBUG("mod_netconf daemon started (PID %d)", getpid());
Radek Krejci469aab82012-07-22 18:42:20 +02002489
2490 /* start main loop providing NETCONF communication */
Radek Krejcif23850c2012-07-23 16:14:17 +02002491 forked_proc(config->pool, s);
Radek Krejci469aab82012-07-22 18:42:20 +02002492
Radek Krejcif23850c2012-07-23 16:14:17 +02002493 /* I never should be here, wtf?!? */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002494 DEBUG("mod_netconf daemon unexpectedly stopped");
Radek Krejci469aab82012-07-22 18:42:20 +02002495 exit(APR_EGENERAL);
2496 break;
2497 case APR_INPARENT:
2498 /* register child to be killed (SIGTERM) when the module config's pool dies */
Radek Krejcif23850c2012-07-23 16:14:17 +02002499 apr_pool_note_subprocess(config->pool, config->forkproc, APR_KILL_AFTER_TIMEOUT);
Radek Krejci469aab82012-07-22 18:42:20 +02002500 break;
2501 default:
Tomas Cejkacf44e522015-04-24 17:29:21 +02002502 ERROR("apr_proc_fork() failed");
Radek Krejci469aab82012-07-22 18:42:20 +02002503 break;
2504 }
Radek Krejcif23850c2012-07-23 16:14:17 +02002505 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002506 ERROR("mod_netconf misses configuration structure");
Radek Krejci469aab82012-07-22 18:42:20 +02002507 }
2508
2509 return OK;
2510}
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002511#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002512
Radek Krejci469aab82012-07-22 18:42:20 +02002513/**
2514 * Register module hooks
2515 */
2516static void mod_netconf_register_hooks(apr_pool_t * p)
2517{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002518#ifndef HTTPD_INDEPENDENT
Radek Krejcif23850c2012-07-23 16:14:17 +02002519 ap_hook_post_config(mod_netconf_master_init, NULL, NULL, APR_HOOK_LAST);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002520#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002521}
2522
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002523static const char* cfg_set_socket_path(cmd_parms* cmd, void* cfg, const char* arg)
2524{
2525 ((mod_netconf_cfg*)cfg)->sockname = apr_pstrdup(cmd->pool, arg);
2526 return NULL;
2527}
2528
2529static const command_rec netconf_cmds[] = {
2530 AP_INIT_TAKE1("NetconfSocket", cfg_set_socket_path, NULL, OR_ALL, "UNIX socket path for mod_netconf communication."),
2531 {NULL}
2532};
2533
Radek Krejci469aab82012-07-22 18:42:20 +02002534/* Dispatch list for API hooks */
2535module AP_MODULE_DECLARE_DATA netconf_module = {
2536 STANDARD20_MODULE_STUFF,
2537 NULL, /* create per-dir config structures */
2538 NULL, /* merge per-dir config structures */
Radek Krejcif23850c2012-07-23 16:14:17 +02002539 mod_netconf_create_conf, /* create per-server config structures */
Radek Krejci469aab82012-07-22 18:42:20 +02002540 NULL, /* merge per-server config structures */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002541 netconf_cmds, /* table of config file commands */
Radek Krejci469aab82012-07-22 18:42:20 +02002542 mod_netconf_register_hooks /* register hooks */
2543};
Radek Krejcia332b692012-11-12 16:15:54 +01002544
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002545int main(int argc, char **argv)
2546{
2547 apr_pool_t *pool;
2548 apr_app_initialize(&argc, (char const *const **) &argv, NULL);
2549 apr_signal(SIGTERM, signal_handler);
2550 apr_signal(SIGINT, signal_handler);
2551 apr_pool_create(&pool, NULL);
2552 forked_proc(pool, NULL);
2553 apr_pool_destroy(pool);
2554 apr_terminate();
2555 DEBUG("Terminated");
2556 return 0;
2557}