blob: 7c639c22cb38565fb1902f6fb2169a9b254ee9c3 [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 Cejkab34b7b12015-06-21 22:54:11 +0200162int netconf_callback_ssh_hostkey_check(const char* hostname, ssh_session session)
Radek Krejci469aab82012-07-22 18:42:20 +0200163{
164 /* always approve */
165 return (EXIT_SUCCESS);
166}
167
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200168char *netconf_callback_sshauth_passphrase(const char *username, const char *hostname, const char *priv_key_file)
Radek Krejci469aab82012-07-22 18:42:20 +0200169{
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200170 char *buf;
171 buf = strdup(password);
Radek Krejci469aab82012-07-22 18:42:20 +0200172 return (buf);
173}
174
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200175char *netconf_callback_sshauth_password(const char* username, const char* hostname)
Radek Krejci469aab82012-07-22 18:42:20 +0200176{
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200177 char *buf;
178 buf = strdup(password);
179 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200180}
181
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200182char *netconf_callback_sshauth_interactive(const char *name, const char *instruction,
183 const char *prompt, int echo)
184{
185 char *buf;
186 buf = strdup(password);
187 return (buf);
188}
189
190void netconf_callback_error_process(const char *tag,
191 const char *type,
192 const char *severity,
193 const char *apptag,
194 const char *path,
195 const char *message,
196 const char *attribute,
197 const char *element,
198 const char *ns,
199 const char *sid)
Radek Krejcic11fd862012-07-26 12:41:21 +0200200{
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100201 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
Tomas Cejka442258e2014-04-01 18:17:18 +0200202 if (err_reply_p == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200203 ERROR("Error message was not allocated. %s", __func__);
Tomas Cejka442258e2014-04-01 18:17:18 +0200204 return;
205 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100206 json_object *err_reply = *err_reply_p;
207
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100208 json_object *array = NULL;
209 if (err_reply == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200210 ERROR("error calback: empty error list");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100211 pthread_mutex_lock(&json_lock);
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100212 err_reply = json_object_new_object();
213 array = json_object_new_array();
214 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
215 json_object_object_add(err_reply, "errors", array);
216 if (message != NULL) {
217 json_object_array_add(array, json_object_new_string(message));
218 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100219 pthread_mutex_unlock(&json_lock);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100220 (*err_reply_p) = err_reply;
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100221 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200222 ERROR("error calback: nonempty error list");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100223 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +0200224 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100225 if (message != NULL) {
226 json_object_array_add(array, json_object_new_string(message));
227 }
228 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100229 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100230 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100231 pthread_setspecific(err_reply_key, err_reply_p);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100232 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200233}
234
Tomas Cejka47387fd2013-06-10 20:37:46 +0200235/**
236 * should be used in locked area
237 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100238void prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200239{
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200240 json_object *json_obj = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +0200241 json_object *js_tmp = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100242 char *old_sid = NULL;
243 const char *j_old_sid = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200244 const char *cpbltstr;
245 struct nc_cpblts* cpblts = NULL;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200246
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200247 if (s == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200248 ERROR("No session given.");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200249 return;
250 }
251
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100252 pthread_mutex_lock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200253 if (s->hello_message != NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200254 ERROR("clean previous hello message");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200255 //json_object_put(s->hello_message);
Tomas Cejka09629492014-07-10 15:58:06 +0200256 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
257 j_old_sid = json_object_get_string(js_tmp);
258 if (j_old_sid != NULL) {
259 old_sid = strdup(j_old_sid);
260 }
261 json_object_put(s->hello_message);
262 json_object_put(js_tmp);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100263 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200264 s->hello_message = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200265 }
Tomas Cejkadd5cb452014-03-26 15:22:00 +0100266 s->hello_message = json_object_get(json_object_new_object());
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200267 if (session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100268 if (old_sid != NULL) {
269 /* use previous sid */
270 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
271 free(old_sid);
Tomas Cejkaa3ffdba2014-03-27 15:12:21 +0100272 old_sid = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100273 } else {
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200274 /* we don't have old sid */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100275 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
276 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200277 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
278 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
279 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
280 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
281 cpblts = nc_session_get_cpblts (session);
282 if (cpblts != NULL) {
283 json_obj = json_object_new_array();
284 nc_cpblts_iter_start (cpblts);
285 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
286 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
287 }
288 json_object_object_add(s->hello_message, "capabilities", json_obj);
289 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100290 DEBUG("%s", json_object_to_json_string(s->hello_message));
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200291 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200292 ERROR("Session was not given.");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200293 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
294 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
295 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100296 DEBUG("Status info from hello message prepared");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100297 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200298
299}
300
Tomas Cejka442258e2014-04-01 18:17:18 +0200301void create_err_reply_p()
302{
303 json_object **err_reply = calloc(1, sizeof(json_object **));
304 if (err_reply == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200305 ERROR("Allocation of err_reply storage failed!");
Tomas Cejka442258e2014-04-01 18:17:18 +0200306 return;
307 }
308 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200309 ERROR("cannot set thread-specific value.");
Tomas Cejka442258e2014-04-01 18:17:18 +0200310 }
311}
312
313void clean_err_reply()
314{
315 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
316 if (err_reply != NULL) {
317 if (*err_reply != NULL) {
318 pthread_mutex_lock(&json_lock);
319 json_object_put(*err_reply);
320 pthread_mutex_unlock(&json_lock);
321 }
322 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200323 ERROR("Cannot set thread-specific hash value.");
Tomas Cejka442258e2014-04-01 18:17:18 +0200324 }
325 }
326}
327
328void free_err_reply()
329{
330 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
331 if (err_reply != NULL) {
332 if (*err_reply != NULL) {
333 pthread_mutex_lock(&json_lock);
334 json_object_put(*err_reply);
335 pthread_mutex_unlock(&json_lock);
336 }
337 free(err_reply);
338 err_reply = NULL;
339 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200340 ERROR("Cannot set thread-specific hash value.");
Tomas Cejka442258e2014-04-01 18:17:18 +0200341 }
342 }
343}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200344
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200345/**
346 * \defgroup netconf_operations NETCONF operations
347 * The list of NETCONF operations that mod_netconf supports.
348 * @{
349 */
350
351/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200352 * \brief Send RPC and wait for reply with timeout.
353 *
354 * \param[in] session libnetconf session
355 * \param[in] rpc prepared RPC message
356 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
357 * \param[out] reply reply from the server
358 * \return Value from nc_session_recv_reply() or NC_MSG_UNKNOWN when send_rpc() fails.
359 * On success, it returns NC_MSG_REPLY.
360 */
361NC_MSG_TYPE netconf_send_recv_timed(struct nc_session *session, nc_rpc *rpc,
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200362 int timeout, nc_reply **reply)
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200363{
364 const nc_msgid msgid = NULL;
365 NC_MSG_TYPE ret = NC_MSG_UNKNOWN;
366 msgid = nc_session_send_rpc(session, rpc);
367 if (msgid == NULL) {
368 return ret;
369 }
Tomas Cejka756d1292015-05-06 09:31:46 +0200370 do {
371 ret = nc_session_recv_reply(session, timeout, reply);
372 if (ret == NC_MSG_HELLO) {
373 ERROR("<hello> received instead reply, it will be lost.");
374 nc_reply_free(*reply);
375 }
376 if (ret == NC_MSG_WOULDBLOCK) {
377 ERROR("Timeout for receiving RPC reply expired.");
378 break;
379 }
380 } while (ret == NC_MSG_HELLO || ret == NC_MSG_NOTIFICATION);
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200381 return ret;
382}
383
384/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200385 * \brief Connect to NETCONF server
386 *
387 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
388 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100389static 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 +0200390{
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200391 struct nc_session* session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200392 struct session_with_mutex * locked_session;
Radek Krejcif23850c2012-07-23 16:14:17 +0200393 char *session_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200394
395 /* connect to the requested NETCONF server */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200396 password = (char*)pass;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100397 DEBUG("prepare to connect %s@%s:%s", user, host, port);
David Kupka8e60a372012-09-04 09:15:20 +0200398 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100399 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +0200400
Radek Krejci469aab82012-07-22 18:42:20 +0200401 /* if connected successful, add session to the list */
402 if (session != NULL) {
403 /* generate hash for the session */
Radek Krejcic11fd862012-07-26 12:41:21 +0200404 session_key = gen_ncsession_hash(
405 (host==NULL) ? "localhost" : host,
406 (port==NULL) ? "830" : port,
Radek Krejcia282bed2012-07-27 14:43:45 +0200407 nc_session_get_id(session));
David Kupka8e60a372012-09-04 09:15:20 +0200408
Tomas Cejkaba21b382013-04-13 02:37:32 +0200409 /** \todo allocate from apr_pool */
Tomas Cejka73496bf2014-03-26 15:31:09 +0100410 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 +0200411 nc_session_free(session);
Tomas Cejka73496bf2014-03-26 15:31:09 +0100412 session = NULL;
413 free(locked_session);
414 locked_session = NULL;
Tomas Cejkacf44e522015-04-24 17:29:21 +0200415 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200416 return NULL;
417 }
418 locked_session->session = session;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200419 locked_session->last_activity = apr_time_now();
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200420 locked_session->hello_message = NULL;
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200421 locked_session->closed = 0;
David Kupka8e60a372012-09-04 09:15:20 +0200422 pthread_mutex_init (&locked_session->lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100423 DEBUG("Before session_lock");
David Kupka8e60a372012-09-04 09:15:20 +0200424 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100425 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka866f2282014-09-18 15:20:26 +0200426 if (pthread_rwlock_wrlock(&session_lock) != 0) {
David Kupka8e60a372012-09-04 09:15:20 +0200427 nc_session_free(session);
428 free (locked_session);
Tomas Cejkacf44e522015-04-24 17:29:21 +0200429 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200430 return NULL;
431 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200432 locked_session->notifications = apr_array_make(pool, NOTIFICATION_QUEUE_SIZE, sizeof(notification_t));
Tomas Cejka654f84e2013-04-19 11:55:01 +0200433 locked_session->ntfc_subscribed = 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100434 DEBUG("Add connection to the list");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200435 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 +0100436 DEBUG("Before session_unlock");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200437
Tomas Cejka47387fd2013-06-10 20:37:46 +0200438 /* lock session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100439 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200440 pthread_mutex_lock(&locked_session->lock);
441
442 /* unlock session list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100443 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200444 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200445 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +0200446 }
447
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200448 /* store information about session from hello message for future usage */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100449 prepare_status_message(locked_session, session);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200450
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100451 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200452 pthread_mutex_unlock(&locked_session->lock);
453
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100454 DEBUG("NETCONF session established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200455 return (session_key);
Radek Krejci469aab82012-07-22 18:42:20 +0200456 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200457 ERROR("Connection could not be established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200458 return (NULL);
Radek Krejci469aab82012-07-22 18:42:20 +0200459 }
460
Radek Krejci469aab82012-07-22 18:42:20 +0200461}
462
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100463static int close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +0200464{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100465 DEBUG("lock private lock.");
466 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200467 if (pthread_mutex_lock(&locked_session->lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200468 ERROR("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200469 }
470 locked_session->ntfc_subscribed = 0;
471 locked_session->closed = 1;
Tomas Cejka73496bf2014-03-26 15:31:09 +0100472 if (locked_session->session != NULL) {
473 nc_session_free(locked_session->session);
474 locked_session->session = NULL;
475 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100476 DEBUG("session closed.");
477 DEBUG("unlock private lock.");
478 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200479 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200480 ERROR("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200481 }
482
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100483 DEBUG("unlock session lock.");
484 DEBUG("closed session, disabled notif(?), wait 2s");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200485 usleep(500000); /* let notification thread stop */
486
487 /* session shouldn't be used by now */
488 /** \todo free all notifications from queue */
489 apr_array_clear(locked_session->notifications);
490 pthread_mutex_destroy(&locked_session->lock);
491 if (locked_session->hello_message != NULL) {
492 /** \todo free hello_message */
493 //json_object_put(locked_session->hello_message);
494 locked_session->hello_message = NULL;
495 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200496 locked_session->session = NULL;
Tomas Cejkaaecc5f72013-10-01 00:03:50 +0200497 free(locked_session);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200498 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100499 DEBUG("NETCONF session closed, everything cleared.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200500 return (EXIT_SUCCESS);
501}
502
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100503static int netconf_close(const char* session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +0200504{
David Kupka8e60a372012-09-04 09:15:20 +0200505 struct session_with_mutex * locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +0200506
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100507 DEBUG("Key in hash to close: %s", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200508
David Kupka8e60a372012-09-04 09:15:20 +0200509 /* get exclusive (write) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100510 DEBUG("lock session lock.");
511 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200512 if (pthread_rwlock_wrlock (&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200513 ERROR("Error while locking rwlock");
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100514 (*reply) = create_error("Internal: Error while locking.");
David Kupka8e60a372012-09-04 09:15:20 +0200515 return EXIT_FAILURE;
516 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200517 /* get session to close */
518 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
519 /* remove session from the active sessions list -> nobody new can now work with session */
520 apr_hash_set(netconf_sessions_list, session_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200521
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100522 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200523 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200524 ERROR("Error while unlocking rwlock");
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100525 (*reply) = create_error("Internal: Error while unlocking.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200526 }
527
528 if ((locked_session != NULL) && (locked_session->session != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100529 return close_and_free_session(locked_session);
Radek Krejci469aab82012-07-22 18:42:20 +0200530 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200531 ERROR("Unknown session to close");
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100532 (*reply) = create_error("Internal: Unkown session to close.");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200533 return (EXIT_FAILURE);
Radek Krejci469aab82012-07-22 18:42:20 +0200534 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100535 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +0200536}
537
Tomas Cejkac7929632013-10-24 19:25:15 +0200538/**
539 * Test reply message type and return error message.
540 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200541 * \param[in] session nc_session internal struct
542 * \param[in] session_key session key, NULL to disable disconnect on error
543 * \param[in] msgt RPC-REPLY message type
544 * \param[out] data
545 * \return NULL on success
546 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100547json_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 +0200548{
549 NC_REPLY_TYPE replyt;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100550 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200551
552 /* process the result of the operation */
553 switch (msgt) {
554 case NC_MSG_UNKNOWN:
555 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200556 ERROR("mod_netconf: receiving rpc-reply failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200557 if (session_key != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100558 netconf_close(session_key, &err);
559 }
560 if (err != NULL) {
561 return err;
Tomas Cejkac7929632013-10-24 19:25:15 +0200562 }
563 return create_error("Internal: Receiving RPC-REPLY failed.");
564 }
565 case NC_MSG_NONE:
566 /* there is error handled by callback */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100567 if (data != NULL) {
568 free(*data);
Tomas Cejka7b1e3bd2014-04-08 14:34:28 +0200569 (*data) = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100570 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200571 return NULL;
572 case NC_MSG_REPLY:
573 switch (replyt = nc_reply_get_type(reply)) {
574 case NC_REPLY_OK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100575 if ((data != NULL) && (*data != NULL)) {
576 free(*data);
577 (*data) = NULL;
578 }
579 return create_ok();
Tomas Cejkac7929632013-10-24 19:25:15 +0200580 case NC_REPLY_DATA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100581 if (((*data) = nc_reply_get_data(reply)) == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200582 ERROR("mod_netconf: no data from reply");
Tomas Cejkac7929632013-10-24 19:25:15 +0200583 return create_error("Internal: No data from reply received.");
584 } else {
585 return NULL;
586 }
587 break;
588 case NC_REPLY_ERROR:
Tomas Cejkacf44e522015-04-24 17:29:21 +0200589 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100590 if (data != NULL) {
591 free(*data);
592 (*data) = NULL;
593 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200594 return create_error(nc_reply_get_errormsg(reply));
595 default:
Tomas Cejkacf44e522015-04-24 17:29:21 +0200596 ERROR("mod_netconf: unexpected rpc-reply (%d)", replyt);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100597 if (data != NULL) {
598 free(*data);
599 (*data) = NULL;
600 }
Tomas Cejka60885252014-03-26 15:45:47 +0100601 return create_error("Unknown type of NETCONF reply.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200602 }
603 break;
604 default:
Tomas Cejkacf44e522015-04-24 17:29:21 +0200605 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100606 if (data != NULL) {
607 free(*data);
608 (*data) = NULL;
609 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200610 return create_error("Internal: Unexpected RPC-REPLY message type.");
611 }
612}
613
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100614json_object *netconf_unlocked_op(struct nc_session *session, nc_rpc* rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +0200615{
616 nc_reply* reply = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200617 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200618
619 /* check requests */
620 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200621 ERROR("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200622 return create_error("Internal error: RPC is not created");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200623 }
624
625 if (session != NULL) {
626 /* send the request and get the reply */
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200627 msgt = netconf_send_recv_timed(session, rpc, 5000, &reply);
Tomas Cejka6b886e02013-07-05 09:53:17 +0200628 /* process the result of the operation */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100629 return netconf_test_reply(session, NULL, msgt, reply, NULL);
Tomas Cejka6b886e02013-07-05 09:53:17 +0200630 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200631 ERROR("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200632 return create_error("Internal error: Unknown session to process.");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200633 }
634}
635
Tomas Cejkac7929632013-10-24 19:25:15 +0200636/**
637 * Perform RPC method that returns data.
638 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200639 * \param[in] session_key session identifier
640 * \param[in] rpc RPC message to perform
641 * \param[out] received_data received data string, can be NULL when no data expected, value can be set to NULL if no data received
642 * \return NULL on success, json object with error otherwise
643 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100644static json_object *netconf_op(const char* session_key, nc_rpc* rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +0200645{
646 struct nc_session *session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200647 struct session_with_mutex * locked_session;
Tomas Cejka00635972013-06-03 15:10:52 +0200648 nc_reply* reply = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200649 json_object *res = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100650 char *data = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100651 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200652
Radek Krejci8e4632a2012-07-26 13:40:34 +0200653 /* check requests */
654 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200655 ERROR("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200656 res = create_error("Internal: RPC could not be created.");
657 data = NULL;
658 goto finished;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200659 }
660
David Kupka8e60a372012-09-04 09:15:20 +0200661 /* get non-exclusive (read) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100662 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka866f2282014-09-18 15:20:26 +0200663 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200664 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200665 res = create_error("Internal: Lock failed.");
666 data = NULL;
667 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200668 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200669 /* get session where send the RPC */
Tomas Cejka47387fd2013-06-10 20:37:46 +0200670 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 +0200671 if (locked_session != NULL) {
672 session = locked_session->session;
673 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200674 if (session != NULL) {
David Kupka8e60a372012-09-04 09:15:20 +0200675 /* get exclusive access to session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100676 DEBUG("LOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200677 if (pthread_mutex_lock(&locked_session->lock) != 0) {
678 /* unlock before returning error */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100679 DEBUG("UNLOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200680 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200681
Tomas Cejkacf44e522015-04-24 17:29:21 +0200682 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200683 res = create_error("Internal: Could not unlock.");
684 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200685 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200686 res = create_error("Internal: Could not unlock.");
David Kupka8e60a372012-09-04 09:15:20 +0200687 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100688 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200689 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200690
Tomas Cejkacf44e522015-04-24 17:29:21 +0200691 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200692 res = create_error("Internal: Could not unlock.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200693 }
694
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200695 locked_session->last_activity = apr_time_now();
Tomas Cejkac7929632013-10-24 19:25:15 +0200696
Radek Krejci035bf4e2012-07-25 10:59:09 +0200697 /* send the request and get the reply */
Tomas Cejka8ce138c2015-04-27 23:25:25 +0200698 msgt = netconf_send_recv_timed(session, rpc, 5000, &reply);
Radek Krejcia332b692012-11-12 16:15:54 +0100699
David Kupka8e60a372012-09-04 09:15:20 +0200700 /* first release exclusive lock for this session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100701 DEBUG("UNLOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200702 pthread_mutex_unlock(&locked_session->lock);
703 /* end of critical section */
Radek Krejci035bf4e2012-07-25 10:59:09 +0200704
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100705 res = netconf_test_reply(session, session_key, msgt, reply, &data);
Radek Krejci035bf4e2012-07-25 10:59:09 +0200706 } else {
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100707 /* release lock on failure */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100708 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka866f2282014-09-18 15:20:26 +0200709 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200710 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100711 }
Tomas Cejkacf44e522015-04-24 17:29:21 +0200712 ERROR("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200713 res = create_error("Unknown session to process.");
714 data = NULL;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200715 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200716finished:
717 nc_reply_free(reply);
718 if (received_data != NULL) {
719 (*received_data) = data;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100720 } else {
721 if (data != NULL) {
722 free(data);
723 data = NULL;
724 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200725 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200726 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200727}
728
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100729static char* netconf_getconfig(const char* session_key, NC_DATASTORE source, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200730{
731 nc_rpc* rpc;
732 struct nc_filter *f = NULL;
733 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200734 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200735
736 /* create filter if set */
737 if (filter != NULL) {
738 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
739 }
740
741 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100742 rpc = nc_rpc_getconfig (source, f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200743 nc_filter_free(f);
744 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200745 ERROR("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200746 return (NULL);
747 }
748
Tomas Cejka94674662013-09-13 15:55:24 +0200749 /* tell server to show all elements even if they have default values */
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100750#ifdef HAVE_WITHDEFAULTS_TAGGED
Tomas Cejka1c3840d2014-01-29 21:08:23 +0100751 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100752#else
Tomas Cejkac0e166b2014-07-08 16:04:58 +0200753 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
754 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100755#endif
Tomas Cejkacf44e522015-04-24 17:29:21 +0200756 ERROR("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200757 }
758
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100759 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200760 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100761 if (res != NULL) {
762 (*err) = res;
763 } else {
764 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200765 }
766
Radek Krejci8e4632a2012-07-26 13:40:34 +0200767 return (data);
768}
769
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100770static 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 +0100771{
772 nc_rpc* rpc;
773 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200774 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100775
776 /* create requests */
Tomas Cejka94da2c52013-01-08 18:20:30 +0100777 rpc = nc_rpc_getschema(identifier, version, format);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100778 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200779 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100780 return (NULL);
781 }
782
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100783 res = netconf_op(session_key, rpc, &data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100784 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100785 if (res != NULL) {
786 (*err) = res;
787 } else {
788 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200789 }
790
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100791 return (data);
792}
793
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100794static char* netconf_get(const char* session_key, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200795{
796 nc_rpc* rpc;
797 struct nc_filter *f = NULL;
798 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200799 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200800
801 /* create filter if set */
802 if (filter != NULL) {
803 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
804 }
805
806 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100807 rpc = nc_rpc_get (f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200808 nc_filter_free(f);
809 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200810 ERROR("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200811 return (NULL);
812 }
813
Tomas Cejka94674662013-09-13 15:55:24 +0200814 /* tell server to show all elements even if they have default values */
Tomas Cejkac0e166b2014-07-08 16:04:58 +0200815 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
816 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
817 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200818 ERROR("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200819 }
820
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100821 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200822 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200823 if (res == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100824 (*err) = res;
825 } else {
826 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200827 }
828
Radek Krejci8e4632a2012-07-26 13:40:34 +0200829 return (data);
830}
831
Tomas Cejkab4d05872014-02-14 22:44:38 +0100832static 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 +0200833{
834 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200835 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200836
837 /* create requests */
Tomas Cejkab4d05872014-02-14 22:44:38 +0100838 if (source == NC_DATASTORE_CONFIG) {
Tomas Cejka5064c232013-01-17 09:30:58 +0100839 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100840 /* config, url */
841 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100842 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100843 /* config, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100844 rpc = nc_rpc_copyconfig(source, target, config);
845 }
Tomas Cejkab4d05872014-02-14 22:44:38 +0100846 } else if (source == NC_DATASTORE_URL) {
847 if (target == NC_DATASTORE_URL) {
848 /* url, url */
849 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
850 } else {
851 /* url, datastore */
852 rpc = nc_rpc_copyconfig(source, target, uri_src);
853 }
Tomas Cejka5064c232013-01-17 09:30:58 +0100854 } else {
855 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100856 /* datastore, url */
857 rpc = nc_rpc_copyconfig(source, target, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100858 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100859 /* datastore, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100860 rpc = nc_rpc_copyconfig(source, target);
861 }
862 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200863 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200864 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200865 return create_error("Internal: Creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200866 }
867
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100868 res = netconf_op(session_key, rpc, NULL);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200869 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200870
871 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200872}
Radek Krejci035bf4e2012-07-25 10:59:09 +0200873
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100874static 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 +0200875{
876 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200877 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200878
879 /* create requests */
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100880 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200881 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200882 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200883 return create_error("Internal: Creating rpc request failed");
Radek Krejci62ab34b2012-07-26 13:42:05 +0200884 }
885
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100886 res = netconf_op(session_key, rpc, NULL);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200887 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200888
889 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200890}
891
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100892static json_object *netconf_killsession(const char* session_key, const char* sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200893{
894 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200895 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200896
897 /* create requests */
898 rpc = nc_rpc_killsession(sid);
899 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200900 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200901 return create_error("Internal: Creating rpc request failed");
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200902 }
903
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100904 res = netconf_op(session_key, rpc, NULL);
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200905 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200906 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200907}
908
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100909static json_object *netconf_onlytargetop(const char* session_key, NC_DATASTORE target, nc_rpc* (*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +0200910{
911 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200912 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +0200913
914 /* create requests */
Radek Krejci5cd7d422012-07-26 14:50:29 +0200915 rpc = op_func(target);
Radek Krejci2f318372012-07-26 14:22:35 +0200916 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200917 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200918 return create_error("Internal: Creating rpc request failed");
Radek Krejci2f318372012-07-26 14:22:35 +0200919 }
920
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100921 res = netconf_op(session_key, rpc, NULL);
Radek Krejci2f318372012-07-26 14:22:35 +0200922 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200923 return res;
Radek Krejci2f318372012-07-26 14:22:35 +0200924}
925
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100926static json_object *netconf_deleteconfig(const char* session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200927{
Tomas Cejka404d37e2013-04-13 02:31:35 +0200928 nc_rpc *rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200929 json_object *res = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +0200930 if (target != NC_DATASTORE_URL) {
931 rpc = nc_rpc_deleteconfig(target);
932 } else {
Tomas Cejkac7929632013-10-24 19:25:15 +0200933 rpc = nc_rpc_deleteconfig(target, url);
934 }
935 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200936 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200937 return create_error("Internal: Creating rpc request failed");
Tomas Cejka404d37e2013-04-13 02:31:35 +0200938 }
939
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100940 res = netconf_op(session_key, rpc, NULL);
Tomas Cejkac7929632013-10-24 19:25:15 +0200941 nc_rpc_free (rpc);
942 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +0200943}
944
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100945static json_object *netconf_lock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200946{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100947 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200948}
949
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100950static json_object *netconf_unlock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200951{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100952 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200953}
954
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100955static json_object *netconf_generic(const char* session_key, const char* content, char** data)
Radek Krejci80c10d92012-07-30 08:38:50 +0200956{
Tomas Cejka00635972013-06-03 15:10:52 +0200957 nc_rpc* rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200958 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +0200959
960 /* create requests */
961 rpc = nc_rpc_generic(content);
962 if (rpc == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200963 ERROR("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200964 return create_error("Internal: Creating rpc request failed");
Radek Krejci80c10d92012-07-30 08:38:50 +0200965 }
966
Radek Krejcia332b692012-11-12 16:15:54 +0100967 if (data != NULL) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200968 // TODO ?free(*data);
969 (*data) = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100970 }
Radek Krejci80c10d92012-07-30 08:38:50 +0200971
972 /* get session where send the RPC */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100973 res = netconf_op(session_key, rpc, data);
Tomas Cejkac7929632013-10-24 19:25:15 +0200974 nc_rpc_free (rpc);
975 return res;
Radek Krejci80c10d92012-07-30 08:38:50 +0200976}
977
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200978/**
979 * @}
980 *//* netconf_operations */
981
Radek Krejci7338bde2012-08-10 12:57:30 +0200982void clb_print(NC_VERB_LEVEL level, const char* msg)
Radek Krejci469aab82012-07-22 18:42:20 +0200983{
Tomas Cejka8a86cc22014-09-18 15:35:07 +0200984#define FOREACH(I) \
Tomas Cejkacf44e522015-04-24 17:29:21 +0200985 I(NC_VERB_ERROR) I(NC_VERB_WARNING)
986
987#define CASE(VAL) case VAL: ERROR("%s: %s", #VAL, msg); \
Tomas Cejka8a86cc22014-09-18 15:35:07 +0200988 break;
989
Radek Krejci7338bde2012-08-10 12:57:30 +0200990 switch (level) {
Tomas Cejkacf44e522015-04-24 17:29:21 +0200991 FOREACH(CASE);
992 case NC_VERB_VERBOSE:
993 case NC_VERB_DEBUG:
994 DEBUG("DEBUG: %s", msg);
995 break;
Tomas Cejka8a86cc22014-09-18 15:35:07 +0200996 }
997 if (level == NC_VERB_ERROR) {
998 /* return global error */
999 netconf_callback_error_process(NULL /* tag */, NULL /* type */,
1000 NULL /* severity */, NULL /* apptag */,
1001 NULL /* path */, msg, NULL /* attribute */,
1002 NULL /* element */, NULL /* ns */, NULL /* sid */);
Radek Krejci7338bde2012-08-10 12:57:30 +02001003 }
Radek Krejci469aab82012-07-22 18:42:20 +02001004}
1005
Tomas Cejka64b87482013-06-03 16:30:53 +02001006/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02001007 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02001008 * Caller should free message memory.
1009 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02001010 * \return pointer to message
1011 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001012char *get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02001013{
1014 /* read json in chunked framing */
1015 unsigned int buffer_size = 0;
1016 ssize_t buffer_len = 0;
1017 char *buffer = NULL;
1018 char c;
1019 ssize_t ret;
1020 int i, chunk_len;
1021 char chunk_len_str[12];
1022
1023 while (1) {
1024 /* read chunk length */
1025 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
1026 if (buffer != NULL) {
1027 free (buffer);
1028 buffer = NULL;
1029 }
1030 break;
1031 }
1032 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
1033 if (buffer != NULL) {
1034 free (buffer);
1035 buffer = NULL;
1036 }
1037 break;
1038 }
1039 i=0;
1040 memset (chunk_len_str, 0, 12);
1041 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
1042 if (i==0 && c == '#') {
1043 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
1044 /* end but invalid */
1045 if (buffer != NULL) {
1046 free (buffer);
1047 buffer = NULL;
1048 }
1049 }
1050 /* end of message, double-loop break */
1051 goto msg_complete;
1052 }
1053 chunk_len_str[i++] = c;
1054 if (i==11) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001055 ERROR("Message is too long, buffer for length is not big enought!!!!");
Tomas Cejka64b87482013-06-03 16:30:53 +02001056 break;
1057 }
1058 }
1059 if (c != '\n') {
1060 if (buffer != NULL) {
1061 free (buffer);
1062 buffer = NULL;
1063 }
1064 break;
1065 }
1066 chunk_len_str[i] = 0;
1067 if ((chunk_len = atoi (chunk_len_str)) == 0) {
1068 if (buffer != NULL) {
1069 free (buffer);
1070 buffer = NULL;
1071 }
1072 break;
1073 }
1074 buffer_size += chunk_len+1;
1075 buffer = realloc (buffer, sizeof(char)*buffer_size);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001076 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
Tomas Cejka64b87482013-06-03 16:30:53 +02001077 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
1078 if (buffer != NULL) {
1079 free (buffer);
1080 buffer = NULL;
1081 }
1082 break;
1083 }
1084 buffer_len += ret;
1085 }
1086msg_complete:
1087 return buffer;
1088}
1089
Tomas Cejkad5b53772013-06-08 23:01:07 +02001090NC_DATASTORE parse_datastore(const char *ds)
1091{
1092 if (strcmp(ds, "running") == 0) {
1093 return NC_DATASTORE_RUNNING;
1094 } else if (strcmp(ds, "startup") == 0) {
1095 return NC_DATASTORE_STARTUP;
1096 } else if (strcmp(ds, "candidate") == 0) {
1097 return NC_DATASTORE_CANDIDATE;
Tomas Cejka4003a702013-10-01 00:02:45 +02001098 } else if (strcmp(ds, "url") == 0) {
1099 return NC_DATASTORE_URL;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001100 } else if (strcmp(ds, "config") == 0) {
1101 return NC_DATASTORE_CONFIG;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001102 }
1103 return -1;
1104}
1105
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001106NC_EDIT_TESTOPT_TYPE parse_testopt(const char *t)
1107{
1108 if (strcmp(t, "notset") == 0) {
1109 return NC_EDIT_TESTOPT_NOTSET;
1110 } else if (strcmp(t, "testset") == 0) {
1111 return NC_EDIT_TESTOPT_TESTSET;
1112 } else if (strcmp(t, "set") == 0) {
1113 return NC_EDIT_TESTOPT_SET;
1114 } else if (strcmp(t, "test") == 0) {
1115 return NC_EDIT_TESTOPT_TEST;
1116 }
1117 return NC_EDIT_TESTOPT_ERROR;
1118}
1119
Tomas Cejkad5b53772013-06-08 23:01:07 +02001120json_object *create_error(const char *errmess)
1121{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001122 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001123 json_object *reply = json_object_new_object();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001124 json_object *array = json_object_new_array();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001125 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001126 json_object_array_add(array, json_object_new_string(errmess));
1127 json_object_object_add(reply, "errors", array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001128 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001129 return reply;
1130
1131}
1132
1133json_object *create_data(const char *data)
1134{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001135 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001136 json_object *reply = json_object_new_object();
1137 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1138 json_object_object_add(reply, "data", json_object_new_string(data));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001139 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001140 return reply;
1141}
1142
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001143json_object *create_ok()
1144{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001145 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001146 json_object *reply = json_object_new_object();
1147 reply = json_object_new_object();
1148 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001149 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001150 return reply;
1151}
1152
Tomas Cejka09629492014-07-10 15:58:06 +02001153char *get_param_string(json_object *data, const char *name)
1154{
1155 json_object *js_tmp = NULL;
1156 char *res = NULL;
1157 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
1158 res = strdup(json_object_get_string(js_tmp));
1159 json_object_put(js_tmp);
1160 }
1161 return res;
1162}
1163
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001164json_object *handle_op_connect(apr_pool_t *pool, json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001165{
Tomas Cejka09629492014-07-10 15:58:06 +02001166 char *host = NULL;
1167 char *port = NULL;
1168 char *user = NULL;
1169 char *pass = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001170 json_object *capabilities = NULL;
1171 json_object *reply = NULL;
1172 char *session_key_hash = NULL;
1173 struct nc_cpblts* cpblts = NULL;
1174 unsigned int len, i;
1175
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001176 DEBUG("Request: Connect");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001177 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001178
Tomas Cejka09629492014-07-10 15:58:06 +02001179 host = get_param_string(request, "host");
1180 port = get_param_string(request, "port");
1181 user = get_param_string(request, "user");
1182 pass = get_param_string(request, "pass");
1183
1184 if (json_object_object_get_ex(request, "capabilities", &capabilities) == TRUE) {
1185 if ((capabilities != NULL) && ((len = json_object_array_length(capabilities)) > 0)) {
1186 cpblts = nc_cpblts_new(NULL);
1187 for (i=0; i<len; i++) {
1188 nc_cpblts_add(cpblts, json_object_get_string(json_object_array_get_idx(capabilities, i)));
1189 }
1190 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001191 ERROR("no capabilities specified");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001192 }
Tomas Cejka09629492014-07-10 15:58:06 +02001193 json_object_put(capabilities);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001194 }
Tomas Cejka09629492014-07-10 15:58:06 +02001195
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001196 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001197
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001198 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001199 if ((host == NULL) || (user == NULL)) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001200 ERROR("Cannot connect - insufficient input.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001201 session_key_hash = NULL;
1202 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001203 session_key_hash = netconf_connect(pool, host, port, user, pass, cpblts);
1204 DEBUG("hash: %s", session_key_hash);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001205 }
1206 if (cpblts != NULL) {
1207 nc_cpblts_free(cpblts);
1208 }
1209
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001210 GETSPEC_ERR_REPLY
1211
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001212 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001213 if (session_key_hash == NULL) {
1214 /* negative reply */
1215 if (err_reply == NULL) {
1216 reply = json_object_new_object();
1217 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1218 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
Tomas Cejkacf44e522015-04-24 17:29:21 +02001219 ERROR("Connection failed.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001220 } else {
1221 /* use filled err_reply from libnetconf's callback */
1222 reply = err_reply;
Tomas Cejkacf44e522015-04-24 17:29:21 +02001223 ERROR("Connect - error from libnetconf's callback.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001224 }
1225 } else {
1226 /* positive reply */
1227 reply = json_object_new_object();
1228 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1229 json_object_object_add(reply, "session", json_object_new_string(session_key_hash));
1230
1231 free(session_key_hash);
1232 }
Tomas Cejka09629492014-07-10 15:58:06 +02001233 memset(pass, 0, strlen(pass));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001234 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001235 CHECK_AND_FREE(host);
1236 CHECK_AND_FREE(user);
1237 CHECK_AND_FREE(port);
1238 CHECK_AND_FREE(pass);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001239 return reply;
1240}
1241
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001242json_object *handle_op_get(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001243{
Tomas Cejka09629492014-07-10 15:58:06 +02001244 char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001245 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001246 json_object *reply = NULL;
1247
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001248 DEBUG("Request: get (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001249
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001250 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001251 filter = get_param_string(request, "filter");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001252 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001253
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001254 if ((data = netconf_get(session_key, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001255 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001256 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001257 reply = create_data(data);
1258 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001259 }
1260 return reply;
1261}
1262
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001263json_object *handle_op_getconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001264{
1265 NC_DATASTORE ds_type_s = -1;
Tomas Cejka09629492014-07-10 15:58:06 +02001266 char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001267 char *data = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001268 char *source = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001269 json_object *reply = NULL;
1270
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001271 DEBUG("Request: get-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001272
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001273 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001274 filter = get_param_string(request, "filter");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001275
Tomas Cejka09629492014-07-10 15:58:06 +02001276 source = get_param_string(request, "source");
1277 if (source != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001278 ds_type_s = parse_datastore(source);
1279 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001280 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001281
Tomas Cejkad5b53772013-06-08 23:01:07 +02001282 if (ds_type_s == -1) {
Tomas Cejka09629492014-07-10 15:58:06 +02001283 reply = create_error("Invalid source repository type requested.");
1284 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001285 }
1286
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001287 if ((data = netconf_getconfig(session_key, ds_type_s, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001288 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001289 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001290 reply = create_data(data);
1291 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001292 }
Tomas Cejka09629492014-07-10 15:58:06 +02001293finalize:
1294 CHECK_AND_FREE(filter);
1295 CHECK_AND_FREE(source);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001296 return reply;
1297}
1298
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001299json_object *handle_op_getschema(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001300{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001301 char *data = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001302 char *identifier = NULL;
1303 char *version = NULL;
1304 char *format = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001305 json_object *reply = NULL;
1306
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001307 DEBUG("Request: get-schema (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001308 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001309 identifier = get_param_string(request, "identifier");
1310 version = get_param_string(request, "version");
1311 format = get_param_string(request, "format");
Tomas Cejkab9e7efe2014-03-28 21:09:04 +01001312 pthread_mutex_unlock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001313
Tomas Cejkad5b53772013-06-08 23:01:07 +02001314 if (identifier == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001315 reply = create_error("No identifier for get-schema supplied.");
1316 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001317 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001318
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001319 DEBUG("get-schema(version: %s, format: %s)", version, format);
1320 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001321 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001322 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001323 reply = create_data(data);
1324 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001325 }
Tomas Cejka09629492014-07-10 15:58:06 +02001326finalize:
1327 CHECK_AND_FREE(identifier);
1328 CHECK_AND_FREE(version);
1329 CHECK_AND_FREE(format);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001330 return reply;
1331}
1332
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001333json_object *handle_op_editconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001334{
1335 NC_DATASTORE ds_type_s = -1;
1336 NC_DATASTORE ds_type_t = -1;
1337 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
1338 NC_EDIT_ERROPT_TYPE erropt_type = 0;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001339 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
Tomas Cejka09629492014-07-10 15:58:06 +02001340 char *defop = NULL;
1341 char *erropt = NULL;
1342 char *config = NULL;
1343 char *source = NULL;
1344 char *target = NULL;
1345 char *testopt = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001346 json_object *reply = NULL;
1347
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001348 DEBUG("Request: edit-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001349
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001350 pthread_mutex_lock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001351 /* get parameters */
Tomas Cejka09629492014-07-10 15:58:06 +02001352 defop = get_param_string(request, "default-operation");
1353 erropt = get_param_string(request, "error-option");
1354 target = get_param_string(request, "target");
1355 source = get_param_string(request, "source");
1356 config = get_param_string(request, "config");
1357 testopt = get_param_string(request, "test-option");
1358 pthread_mutex_unlock(&json_lock);
1359
1360 if (target != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001361 ds_type_t = parse_datastore(target);
1362 }
Tomas Cejka09629492014-07-10 15:58:06 +02001363 if (source != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001364 ds_type_s = parse_datastore(source);
1365 } else {
1366 /* source is optional, default value is config */
1367 ds_type_s = NC_DATASTORE_CONFIG;
1368 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001369
Tomas Cejkad5b53772013-06-08 23:01:07 +02001370 if (defop != NULL) {
1371 if (strcmp(defop, "merge") == 0) {
1372 defop_type = NC_EDIT_DEFOP_MERGE;
1373 } else if (strcmp(defop, "replace") == 0) {
1374 defop_type = NC_EDIT_DEFOP_REPLACE;
1375 } else if (strcmp(defop, "none") == 0) {
1376 defop_type = NC_EDIT_DEFOP_NONE;
1377 } else {
Tomas Cejka09629492014-07-10 15:58:06 +02001378 reply = create_error("Invalid default-operation parameter.");
1379 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001380 }
1381 } else {
1382 defop_type = NC_EDIT_DEFOP_NOTSET;
1383 }
1384
Tomas Cejkad5b53772013-06-08 23:01:07 +02001385 if (erropt != NULL) {
1386 if (strcmp(erropt, "continue-on-error") == 0) {
1387 erropt_type = NC_EDIT_ERROPT_CONT;
1388 } else if (strcmp(erropt, "stop-on-error") == 0) {
1389 erropt_type = NC_EDIT_ERROPT_STOP;
1390 } else if (strcmp(erropt, "rollback-on-error") == 0) {
1391 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
1392 } else {
Tomas Cejka09629492014-07-10 15:58:06 +02001393 reply = create_error("Invalid error-option parameter.");
1394 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001395 }
1396 } else {
1397 erropt_type = 0;
1398 }
1399
Tomas Cejkad5b53772013-06-08 23:01:07 +02001400 if (ds_type_t == -1) {
Tomas Cejka09629492014-07-10 15:58:06 +02001401 reply = create_error("Invalid target repository type requested.");
1402 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001403 }
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001404 if (ds_type_s == NC_DATASTORE_CONFIG) {
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001405 if (config == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001406 reply = create_error("Invalid config data parameter.");
1407 goto finalize;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001408 }
1409 } else if (ds_type_s == NC_DATASTORE_URL){
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001410 if (config == NULL) {
1411 config = "";
1412 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001413 }
1414
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001415 if (testopt != NULL) {
1416 testopt_type = parse_testopt(testopt);
1417 } else {
1418 testopt_type = NC_EDIT_TESTOPT_TESTSET;
1419 }
1420
1421 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 +01001422
1423 CHECK_ERR_SET_REPLY
Tomas Cejka09629492014-07-10 15:58:06 +02001424finalize:
1425 CHECK_AND_FREE(defop);
1426 CHECK_AND_FREE(erropt);
1427 CHECK_AND_FREE(config);
1428 CHECK_AND_FREE(source);
1429 CHECK_AND_FREE(target);
1430 CHECK_AND_FREE(testopt);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001431 return reply;
1432}
1433
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001434json_object *handle_op_copyconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001435{
1436 NC_DATASTORE ds_type_s = -1;
1437 NC_DATASTORE ds_type_t = -1;
Tomas Cejka09629492014-07-10 15:58:06 +02001438 char *config = NULL;
1439 char *target = NULL;
1440 char *source = NULL;
1441 char *uri_src = NULL;
1442 char *uri_trg = NULL;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001443
Tomas Cejkad5b53772013-06-08 23:01:07 +02001444 json_object *reply = NULL;
1445
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001446 DEBUG("Request: copy-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001447
1448 /* get parameters */
Tomas Cejka09629492014-07-10 15:58:06 +02001449 pthread_mutex_lock(&json_lock);
1450 target = get_param_string(request, "target");
1451 source = get_param_string(request, "source");
1452 config = get_param_string(request, "config");
1453 uri_src = get_param_string(request, "uri-source");
1454 uri_trg = get_param_string(request, "uri-target");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001455 pthread_mutex_unlock(&json_lock);
1456
Tomas Cejka09629492014-07-10 15:58:06 +02001457 if (target != NULL) {
1458 ds_type_t = parse_datastore(target);
1459 }
1460 if (source != NULL) {
1461 ds_type_s = parse_datastore(source);
1462 } else {
1463 /* source == NULL *//* no explicit source specified -> use config data */
Tomas Cejkad5b53772013-06-08 23:01:07 +02001464 ds_type_s = NC_DATASTORE_CONFIG;
Tomas Cejka09629492014-07-10 15:58:06 +02001465 }
1466 if (ds_type_s == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001467 /* source datastore specified, but it is invalid */
Tomas Cejka09629492014-07-10 15:58:06 +02001468 reply = create_error("Invalid source repository type requested.");
1469 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001470 }
1471
1472 if (ds_type_t == -1) {
1473 /* invalid target datastore specified */
Tomas Cejka09629492014-07-10 15:58:06 +02001474 reply = create_error("Invalid target repository type requested.");
1475 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001476 }
1477
Tomas Cejka55c6df52014-02-20 12:59:33 +01001478 /* source can be missing when config is given */
1479 if (source == NULL && config == NULL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001480 reply = create_error("invalid input parameters - source and config is required.");
Tomas Cejka09629492014-07-10 15:58:06 +02001481 goto finalize;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001482 }
1483
1484 if (ds_type_s == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001485 if (uri_src == NULL) {
1486 uri_src = "";
1487 }
1488 }
1489 if (ds_type_t == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001490 if (uri_trg == NULL) {
1491 uri_trg = "";
1492 }
1493 }
1494 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001495
1496 CHECK_ERR_SET_REPLY
1497
Tomas Cejka09629492014-07-10 15:58:06 +02001498finalize:
1499 CHECK_AND_FREE(config);
1500 CHECK_AND_FREE(target);
1501 CHECK_AND_FREE(source);
1502 CHECK_AND_FREE(uri_src);
1503 CHECK_AND_FREE(uri_trg);
1504
Tomas Cejkad5b53772013-06-08 23:01:07 +02001505 return reply;
1506}
1507
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001508json_object *handle_op_generic(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001509{
1510 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001511 char *config = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001512 char *data = NULL;
1513
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001514 DEBUG("Request: generic request for session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001515
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001516 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001517 config = get_param_string(request, "content");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001518 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001519
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001520 reply = netconf_generic(session_key, config, &data);
1521 if (reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001522 GETSPEC_ERR_REPLY
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001523 if (err_reply != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001524 /* use filled err_reply from libnetconf's callback */
1525 reply = err_reply;
1526 }
1527 } else {
1528 if (data == NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001529 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001530 reply = json_object_new_object();
1531 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001532 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001533 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001534 reply = create_data(data);
1535 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001536 }
1537 }
Tomas Cejka09629492014-07-10 15:58:06 +02001538 CHECK_AND_FREE(config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001539 return reply;
1540}
1541
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001542json_object *handle_op_disconnect(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001543{
1544 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001545 DEBUG("Request: Disconnect session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001546
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001547 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001548 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001549 } else {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001550 reply = create_ok();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001551 }
1552 return reply;
1553}
1554
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001555json_object *handle_op_kill(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001556{
1557 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001558 char *sid = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001559
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001560 DEBUG("Request: kill-session, session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001561
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001562 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001563 sid = get_param_string(request, "session-id");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001564 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001565
1566 if (sid == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001567 reply = create_error("Missing session-id parameter.");
1568 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001569 }
1570
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001571 reply = netconf_killsession(session_key, sid);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001572
1573 CHECK_ERR_SET_REPLY
1574
Tomas Cejka09629492014-07-10 15:58:06 +02001575finalize:
1576 CHECK_AND_FREE(sid);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001577 return reply;
1578}
1579
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001580json_object *handle_op_reloadhello(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001581{
Tomas Cejka47387fd2013-06-10 20:37:46 +02001582 struct nc_session *temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001583 struct session_with_mutex * locked_session = NULL;
1584 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001585 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001586
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001587 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001588 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001589 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001590 return NULL;
1591 }
1592
Tomas Cejka09629492014-07-10 15:58:06 +02001593 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 +02001594 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001595 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001596 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001597 DEBUG("creating temporal NC session.");
Tomas Cejka47387fd2013-06-10 20:37:46 +02001598 temp_session = nc_session_connect_channel(locked_session->session, NULL);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001599 if (temp_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001600 prepare_status_message(locked_session, temp_session);
1601 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001602 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001603 temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001604 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001605 DEBUG("Reload hello failed due to channel establishment");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001606 reply = create_error("Reload was unsuccessful, connection failed.");
1607 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001608 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001609 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001610 DEBUG("UNLOCK wrlock %s", __func__);
1611 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001612 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka93b0e492014-03-26 15:47:45 +01001613 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001614 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001615 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001616 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001617 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001618 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001619 reply = create_error("Invalid session identifier.");
1620 }
1621
1622 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
1623 reply = locked_session->hello_message;
1624 }
1625 return reply;
1626}
1627
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001628json_object *handle_op_info(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001629{
1630 json_object *reply = NULL;
Tomas Cejka47387fd2013-06-10 20:37:46 +02001631 struct session_with_mutex * locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001632 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001633
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001634 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001635 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001636 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001637 }
1638
Tomas Cejka09629492014-07-10 15:58:06 +02001639 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 +02001640 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001641 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001642 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001643 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001644 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001645 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001646 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001647 if (locked_session->hello_message != NULL) {
1648 reply = locked_session->hello_message;
1649 } else {
1650 reply = create_error("Invalid session identifier.");
1651 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001652 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001653 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001654 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001655 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001656 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001657 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001658 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001659 reply = create_error("Invalid session identifier.");
1660 }
1661
Tomas Cejka47387fd2013-06-10 20:37:46 +02001662
Tomas Cejkad5b53772013-06-08 23:01:07 +02001663 return reply;
1664}
1665
Tomas Cejka6b886e02013-07-05 09:53:17 +02001666void notification_history(time_t eventtime, const char *content)
1667{
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001668 json_object *notif_history_array = (json_object *) pthread_getspecific(notif_history_key);
1669 if (notif_history_array == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001670 ERROR("No list of notification history found.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001671 return;
1672 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001673 DEBUG("Got notification from history %lu.", (long unsigned) eventtime);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001674 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001675 json_object *notif = json_object_new_object();
1676 if (notif == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001677 ERROR("Could not allocate memory for notification (json).");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001678 goto failed;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001679 }
1680 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
1681 json_object_object_add(notif, "content", json_object_new_string(content));
1682 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001683failed:
1684 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001685}
1686
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001687json_object *handle_op_ntfgethistory(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001688{
1689 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001690 json_object *js_tmp = NULL;
1691 char *sid = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001692 struct session_with_mutex *locked_session = NULL;
1693 struct nc_session *temp_session = NULL;
1694 nc_rpc *rpc = NULL;
1695 time_t start = 0;
1696 time_t stop = 0;
Tomas Cejka09629492014-07-10 15:58:06 +02001697 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001698
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001699 DEBUG("Request: get notification history, session %s", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001700
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001701 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001702 sid = get_param_string(request, "session");
1703
1704 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
1705 from = json_object_get_int64(js_tmp);
1706 json_object_put(js_tmp);
1707 }
1708 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
1709 to = json_object_get_int64(js_tmp);
1710 json_object_put(js_tmp);
1711 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001712 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001713
1714 start = time(NULL) + from;
1715 stop = time(NULL) + to;
1716
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001717 DEBUG("notification history interval %li %li", (long int) from, (long int) to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001718
1719 if (sid == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001720 reply = create_error("Missing session parameter.");
1721 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001722 }
1723
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001724 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001725 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001726 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka09629492014-07-10 15:58:06 +02001727 reply = create_error("Internal lock failed.");
1728 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001729 }
1730
1731 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1732 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001733 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001734 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001735 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001736 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001737 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001738 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001739 DEBUG("creating temporal NC session.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001740 temp_session = nc_session_connect_channel(locked_session->session, NULL);
1741 if (temp_session != NULL) {
1742 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
1743 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001744 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001745 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001746 DEBUG("notifications: creating an rpc request failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001747 reply = create_error("notifications: creating an rpc request failed.");
1748 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001749 }
1750
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001751 DEBUG("Send NC subscribe.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001752 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001753 json_object *res = netconf_unlocked_op(temp_session, rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001754 if (res != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001755 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001756 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001757 DEBUG("Subscription RPC failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001758 reply = res;
1759 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001760 }
1761 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
1762
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001763 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001764 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001765 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001766 pthread_mutex_lock(&ntf_history_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001767 pthread_mutex_lock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001768 json_object *notif_history_array = json_object_new_array();
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001769 pthread_mutex_unlock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001770 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001771 ERROR("notif_history: cannot set thread-specific hash value.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001772 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001773
1774 ncntf_dispatch_receive(temp_session, notification_history);
1775
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001776 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001777 reply = json_object_new_object();
1778 json_object_object_add(reply, "notifications", notif_history_array);
1779 //json_object_put(notif_history_array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001780 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001781
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001782 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001783 pthread_mutex_unlock(&ntf_history_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001784 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001785 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001786 temp_session = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001787 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001788 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001789 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001790 DEBUG("Get history of notification failed due to channel establishment");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001791 reply = create_error("Get history of notification was unsuccessful, connection failed.");
1792 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001793 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001794 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001795 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001796 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001797 }
1798 reply = create_error("Invalid session identifier.");
1799 }
1800
Tomas Cejka09629492014-07-10 15:58:06 +02001801finalize:
1802 CHECK_AND_FREE(sid);
Tomas Cejka4003a702013-10-01 00:02:45 +02001803 return reply;
1804}
1805
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001806json_object *handle_op_validate(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02001807{
1808 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001809 char *sid = NULL;
1810 char *target = NULL;
1811 char *url = NULL;
Tomas Cejka4003a702013-10-01 00:02:45 +02001812 nc_rpc *rpc = NULL;
1813 NC_DATASTORE target_ds;
1814
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001815 DEBUG("Request: validate datastore, session %s", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02001816
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001817 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001818 sid = get_param_string(request, "session");
1819 target = get_param_string(request, "target");
1820 url = get_param_string(request, "url");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001821 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02001822
1823
1824 if ((sid == NULL) || (target == NULL)) {
Tomas Cejka09629492014-07-10 15:58:06 +02001825 reply = create_error("Missing session parameter.");
1826 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001827 }
Tomas Cejka4003a702013-10-01 00:02:45 +02001828
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001829 /* validation */
1830 target_ds = parse_datastore(target);
1831 if (target_ds == NC_DATASTORE_URL) {
1832 if (url != NULL) {
1833 rpc = nc_rpc_validate(target_ds, url);
Tomas Cejka4003a702013-10-01 00:02:45 +02001834 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001835 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
Tomas Cejka4003a702013-10-01 00:02:45 +02001836 || (target_ds == NC_DATASTORE_CANDIDATE)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001837 rpc = nc_rpc_validate(target_ds);
1838 }
1839 if (rpc == NULL) {
1840 DEBUG("mod_netconf: creating rpc request failed");
1841 reply = create_error("Creation of RPC request failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001842 goto finalize;
Tomas Cejka4003a702013-10-01 00:02:45 +02001843 }
1844
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001845 DEBUG("Request: validate datastore");
1846 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001847
1848 CHECK_ERR_SET_REPLY
1849
1850 if (reply == NULL) {
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001851 DEBUG("Request: validation ok.");
1852 reply = create_ok();
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001853 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001854 }
1855 nc_rpc_free (rpc);
Tomas Cejka09629492014-07-10 15:58:06 +02001856finalize:
1857 CHECK_AND_FREE(sid);
1858 CHECK_AND_FREE(target);
1859 CHECK_AND_FREE(url);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001860 return reply;
1861}
1862
David Kupka8e60a372012-09-04 09:15:20 +02001863void * thread_routine (void * arg)
1864{
1865 void * retval = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001866 struct pollfd fds;
Tomas Cejka00635972013-06-03 15:10:52 +02001867 json_object *request = NULL;
1868 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001869 json_object *js_tmp = NULL;
1870 int operation = (-1);
1871 NC_DATASTORE ds_type_t = -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001872 int status = 0;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001873 const char *msgtext;
Tomas Cejka09629492014-07-10 15:58:06 +02001874 char *session_key = NULL;
1875 char *target = NULL;
1876 char *url = NULL;
Tomas Cejka64b87482013-06-03 16:30:53 +02001877 char *chunked_out_msg = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001878 apr_pool_t * pool = ((struct pass_to_thread*)arg)->pool;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001879 //server_rec * server = ((struct pass_to_thread*)arg)->server;
David Kupka8e60a372012-09-04 09:15:20 +02001880 int client = ((struct pass_to_thread*)arg)->client;
1881
Tomas Cejka00635972013-06-03 15:10:52 +02001882 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001883
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001884 /* init thread specific err_reply memory */
Tomas Cejka442258e2014-04-01 18:17:18 +02001885 create_err_reply_p();
1886
David Kupka8e60a372012-09-04 09:15:20 +02001887 while (!isterminated) {
1888 fds.fd = client;
1889 fds.events = POLLIN;
1890 fds.revents = 0;
1891
1892 status = poll(&fds, 1, 1000);
1893
1894 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
1895 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
David Kupka8e60a372012-09-04 09:15:20 +02001896 continue;
1897 } else if (status < 0) {
1898 /* 0: poll time outed
1899 * close socket and ignore this request from the client, it can try it again
1900 * -1: poll failed
1901 * something wrong happend, close this socket and wait for another request
1902 */
David Kupka8e60a372012-09-04 09:15:20 +02001903 close(client);
1904 break;
1905 }
1906 /* status > 0 */
1907
1908 /* check the status of the socket */
1909
1910 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
1911 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
1912 /* close client's socket (it's probably already closed by client */
David Kupka8e60a372012-09-04 09:15:20 +02001913 close(client);
1914 break;
1915 }
1916
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001917 DEBUG("Get framed message...");
1918 buffer = get_framed_message(client);
David Kupka8e60a372012-09-04 09:15:20 +02001919
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001920 DEBUG("Check read buffer.");
David Kupka8e60a372012-09-04 09:15:20 +02001921 if (buffer != NULL) {
Tomas Cejka00635972013-06-03 15:10:52 +02001922 enum json_tokener_error jerr;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001923 pthread_mutex_lock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001924 request = json_tokener_parse_verbose(buffer, &jerr);
1925 if (jerr != json_tokener_success) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02001926 ERROR("JSON parsing error");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001927 pthread_mutex_unlock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001928 continue;
1929 }
David Kupka8e60a372012-09-04 09:15:20 +02001930
Tomas Cejka09629492014-07-10 15:58:06 +02001931 session_key = get_param_string(request, "session");
1932 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
1933 operation = json_object_get_int(js_tmp);
1934 json_object_put(js_tmp);
1935 js_tmp = NULL;
1936 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001937 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001938 if (operation == -1) {
1939 reply = create_error("Missing operation type form frontend.");
1940 goto send_reply;
1941 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001942
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001943 DEBUG("operation %d session_key %s.", operation, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001944 /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
1945 if (operation != MSG_CONNECT && session_key == NULL) {
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001946 reply = create_error("Missing session specification.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001947 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001948 msgtext = json_object_to_json_string(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001949 pthread_mutex_unlock(&json_lock);
1950
David Kupka8e60a372012-09-04 09:15:20 +02001951 send(client, msgtext, strlen(msgtext) + 1, 0);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001952
1953 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001954 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001955 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001956 /* there is some stupid client, so close the connection to give a chance to some other client */
1957 close(client);
1958 break;
1959 }
1960
David Kupka8e60a372012-09-04 09:15:20 +02001961 /* null global JSON error-reply */
Tomas Cejka442258e2014-04-01 18:17:18 +02001962 clean_err_reply();
David Kupka8e60a372012-09-04 09:15:20 +02001963
1964 /* prepare reply envelope */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001965 if (reply != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001966 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001967 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001968 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001969 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001970 reply = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001971
1972 /* process required operation */
1973 switch (operation) {
1974 case MSG_CONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001975 reply = handle_op_connect(pool, request);
David Kupka8e60a372012-09-04 09:15:20 +02001976 break;
1977 case MSG_GET:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001978 reply = handle_op_get(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001979 break;
1980 case MSG_GETCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001981 reply = handle_op_getconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001982 break;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001983 case MSG_GETSCHEMA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001984 reply = handle_op_getschema(pool, request, session_key);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001985 break;
David Kupka8e60a372012-09-04 09:15:20 +02001986 case MSG_EDITCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001987 reply = handle_op_editconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001988 break;
1989 case MSG_COPYCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001990 reply = handle_op_copyconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001991 break;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001992
David Kupka8e60a372012-09-04 09:15:20 +02001993 case MSG_DELETECONFIG:
David Kupka8e60a372012-09-04 09:15:20 +02001994 case MSG_LOCK:
David Kupka8e60a372012-09-04 09:15:20 +02001995 case MSG_UNLOCK:
Tomas Cejkad5b53772013-06-08 23:01:07 +02001996 /* get parameters */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001997 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001998 target = get_param_string(request, "target");
1999 pthread_mutex_unlock(&json_lock);
2000 if (target != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02002001 ds_type_t = parse_datastore(target);
2002 }
David Kupka8e60a372012-09-04 09:15:20 +02002003
2004 if (ds_type_t == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02002005 reply = create_error("Invalid target repository type requested.");
David Kupka8e60a372012-09-04 09:15:20 +02002006 break;
2007 }
David Kupka8e60a372012-09-04 09:15:20 +02002008 switch(operation) {
2009 case MSG_DELETECONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002010 DEBUG("Request: delete-config (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002011 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002012 url = get_param_string(request, "url");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002013 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002014 reply = netconf_deleteconfig(session_key, ds_type_t, url);
David Kupka8e60a372012-09-04 09:15:20 +02002015 break;
2016 case MSG_LOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002017 DEBUG("Request: lock (session %s)", session_key);
2018 reply = netconf_lock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02002019 break;
2020 case MSG_UNLOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002021 DEBUG("Request: unlock (session %s)", session_key);
2022 reply = netconf_unlock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02002023 break;
2024 default:
Tomas Cejkac7929632013-10-24 19:25:15 +02002025 reply = create_error("Internal: Unknown request type.");
David Kupka8e60a372012-09-04 09:15:20 +02002026 break;
2027 }
2028
Tomas Cejka442258e2014-04-01 18:17:18 +02002029 CHECK_ERR_SET_REPLY
Tomas Cejkac7929632013-10-24 19:25:15 +02002030 if (reply == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02002031 reply = create_ok();
David Kupka8e60a372012-09-04 09:15:20 +02002032 }
2033 break;
2034 case MSG_KILL:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002035 reply = handle_op_kill(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002036 break;
2037 case MSG_DISCONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002038 reply = handle_op_disconnect(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002039 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02002040 case MSG_RELOADHELLO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002041 reply = handle_op_reloadhello(pool, request, session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002042 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02002043 case MSG_INFO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002044 reply = handle_op_info(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002045 break;
2046 case MSG_GENERIC:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002047 reply = handle_op_generic(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002048 break;
Tomas Cejka6b886e02013-07-05 09:53:17 +02002049 case MSG_NTF_GETHISTORY:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002050 reply = handle_op_ntfgethistory(pool, request, session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02002051 break;
Tomas Cejka4003a702013-10-01 00:02:45 +02002052 case MSG_VALIDATE:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002053 reply = handle_op_validate(pool, request, session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02002054 break;
David Kupka8e60a372012-09-04 09:15:20 +02002055 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002056 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002057 reply = create_error("Operation not supported.");
David Kupka8e60a372012-09-04 09:15:20 +02002058 break;
2059 }
Tomas Cejka09629492014-07-10 15:58:06 +02002060 /* free parameters */
2061 CHECK_AND_FREE(session_key);
2062 CHECK_AND_FREE(url);
2063 CHECK_AND_FREE(target);
2064 request = NULL;
2065 operation = (-1);
2066 ds_type_t = (-1);
2067
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002068 DEBUG("Clean request json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02002069 if (request != NULL) {
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002070 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002071 json_object_put(request);
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002072 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002073 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002074 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02002075
2076
2077send_reply:
David Kupka1e3e4c82012-09-04 09:32:15 +02002078 /* send reply to caller */
2079 if (reply != NULL) {
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002080 pthread_mutex_lock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02002081 msgtext = json_object_to_json_string(reply);
Tomas Cejka866f2282014-09-18 15:20:26 +02002082 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int) strlen(msgtext), msgtext) == -1) {
Tomas Cejka00635972013-06-03 15:10:52 +02002083 if (buffer != NULL) {
2084 free(buffer);
2085 buffer = NULL;
2086 }
Tomas Cejka74dd7a92014-09-18 15:58:45 +02002087 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02002088 break;
2089 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002090 pthread_mutex_unlock(&json_lock);
2091
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002092 DEBUG("Send framed reply json object.");
Tomas Cejka64b87482013-06-03 16:30:53 +02002093 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002094 DEBUG("Clean reply json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002095 pthread_mutex_lock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02002096 json_object_put(reply);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002097 reply = NULL;
2098 DEBUG("Clean message buffer.");
Tomas Cejka09629492014-07-10 15:58:06 +02002099 CHECK_AND_FREE(chunked_out_msg);
Tomas Cejka64b87482013-06-03 16:30:53 +02002100 chunked_out_msg = NULL;
Tomas Cejka00635972013-06-03 15:10:52 +02002101 if (buffer != NULL) {
2102 free(buffer);
2103 buffer = NULL;
2104 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002105 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02002106 clean_err_reply();
David Kupka1e3e4c82012-09-04 09:32:15 +02002107 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002108 ERROR("Reply is NULL, shouldn't be...");
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002109 continue;
David Kupka8e60a372012-09-04 09:15:20 +02002110 }
2111 }
2112 }
David Kupka8e60a372012-09-04 09:15:20 +02002113 free (arg);
2114
Tomas Cejka442258e2014-04-01 18:17:18 +02002115 free_err_reply();
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002116
David Kupka8e60a372012-09-04 09:15:20 +02002117 return retval;
2118}
2119
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002120/**
2121 * \brief Close all open NETCONF sessions.
2122 *
2123 * During termination of mod_netconf, it is useful to close all remaining
2124 * sessions. This function iterates over the list of sessions and close them
2125 * all.
2126 *
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002127 * \param[in] p apr pool needed for hash table iterating
2128 * \param[in] ht hash table of session_with_mutex structs
2129 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002130static void close_all_nc_sessions(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002131{
2132 apr_hash_index_t *hi;
2133 void *val = NULL;
2134 struct session_with_mutex *swm = NULL;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002135 const char *hashed_key = NULL;
2136 apr_ssize_t hashed_key_length;
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002137 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002138
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002139 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002140 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002141 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002142 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002143 return;
2144 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002145 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002146 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002147 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002148 swm = (struct session_with_mutex *) val;
2149 if (swm != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002150 DEBUG("LOCK mutex %s", __func__);
2151 pthread_mutex_lock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002152 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002153 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002154 pthread_mutex_unlock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002155
2156 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002157 close_and_free_session(swm);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002158 }
2159 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002160 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002161 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002162 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002163 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002164 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002165}
2166
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002167static void check_timeout_and_close(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002168{
2169 apr_hash_index_t *hi;
2170 void *val = NULL;
2171 struct nc_session *ns = NULL;
2172 struct session_with_mutex *swm = NULL;
2173 const char *hashed_key = NULL;
2174 apr_ssize_t hashed_key_length;
2175 apr_time_t current_time = apr_time_now();
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002176 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002177
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002178 /* get exclusive access to sessions_list (conns) */
Tomas Cejka866f2282014-09-18 15:20:26 +02002179 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002180 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002181 return;
2182 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002183 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002184 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
2185 swm = (struct session_with_mutex *) val;
2186 if (swm == NULL) {
2187 continue;
2188 }
2189 ns = swm->session;
2190 if (ns == NULL) {
2191 continue;
2192 }
2193 pthread_mutex_lock(&swm->lock);
2194 if ((current_time - swm->last_activity) > apr_time_from_sec(ACTIVITY_TIMEOUT)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002195 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002196 /* remove session from the active sessions list */
Tomas Cejka47387fd2013-06-10 20:37:46 +02002197 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
2198 pthread_mutex_unlock(&swm->lock);
2199
2200 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002201 close_and_free_session(swm);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002202 } else {
2203 pthread_mutex_unlock(&swm->lock);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002204 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002205 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002206 /* get exclusive access to sessions_list (conns) */
Tomas Cejka866f2282014-09-18 15:20:26 +02002207 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002208 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002209 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002210}
2211
2212
2213/**
Radek Krejcif23850c2012-07-23 16:14:17 +02002214 * This is actually implementation of NETCONF client
2215 * - requests are received from UNIX socket in the predefined format
2216 * - results are replied through the same way
2217 * - the daemon run as a separate process, but it is started and stopped
2218 * automatically by Apache.
2219 *
2220 */
Radek Krejci469aab82012-07-22 18:42:20 +02002221static void forked_proc(apr_pool_t * pool, server_rec * server)
2222{
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002223 struct timeval tv;
Radek Krejci469aab82012-07-22 18:42:20 +02002224 struct sockaddr_un local, remote;
David Kupka8e60a372012-09-04 09:15:20 +02002225 int lsock, client, ret, i, pthread_count = 0;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002226 unsigned int olds = 0, timediff = 0;
David Kupka8e60a372012-09-04 09:15:20 +02002227 socklen_t len;
Radek Krejciae021c12012-07-25 18:03:52 +02002228 mod_netconf_cfg *cfg;
David Kupka8e60a372012-09-04 09:15:20 +02002229 struct pass_to_thread * arg;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002230 pthread_t * ptids = calloc(1, sizeof(pthread_t));
David Kupka8e60a372012-09-04 09:15:20 +02002231 struct timespec maxtime;
2232 pthread_rwlockattr_t lock_attrs;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002233 char *sockname = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +02002234 #ifdef WITH_NOTIFICATIONS
2235 char use_notifications = 0;
2236 #endif
David Kupka8e60a372012-09-04 09:15:20 +02002237
Tomas Cejka6b886e02013-07-05 09:53:17 +02002238 http_server = server;
2239
Tomas Cejka404d37e2013-04-13 02:31:35 +02002240 /* wait at most 5 seconds for every thread to terminate */
David Kupka8e60a372012-09-04 09:15:20 +02002241 maxtime.tv_sec = 5;
2242 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02002243
Tomas Cejka04e08f42014-03-27 19:52:34 +01002244#ifdef HAVE_UNIXD_SETUP_CHILD
Radek Krejcif23850c2012-07-23 16:14:17 +02002245 /* change uid and gid of process for security reasons */
Radek Krejci469aab82012-07-22 18:42:20 +02002246 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01002247#else
2248# ifdef SU_GROUP
2249 if (strlen(SU_GROUP) > 0) {
2250 struct group *g = getgrnam(SU_GROUP);
2251 if (g == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002252 ERROR("GID (%s) was not found.", SU_GROUP);
Tomas Cejka04e08f42014-03-27 19:52:34 +01002253 return;
2254 }
2255 if (setgid(g->gr_gid) != 0) {
2256
Tomas Cejkacf44e522015-04-24 17:29:21 +02002257 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
Tomas Cejka04e08f42014-03-27 19:52:34 +01002258 return;
2259 }
2260 }
2261# else
2262 DEBUG("no SU_GROUP");
2263# endif
2264# ifdef SU_USER
2265 if (strlen(SU_USER) > 0) {
2266 struct passwd *p = getpwnam(SU_USER);
2267 if (p == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002268 ERROR("UID (%s) was not found.", SU_USER);
Tomas Cejka04e08f42014-03-27 19:52:34 +01002269 return;
2270 }
2271 if (setuid(p->pw_uid) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002272 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
Tomas Cejka04e08f42014-03-27 19:52:34 +01002273 return;
2274 }
2275 }
2276# else
2277 DEBUG("no SU_USER");
2278# endif
2279#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002280
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002281 if (server != NULL) {
2282 cfg = ap_get_module_config(server->module_config, &netconf_module);
2283 if (cfg == NULL) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002284 ERROR("Getting mod_netconf configuration failed");
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002285 return;
2286 }
2287 sockname = cfg->sockname;
2288 } else {
2289 sockname = SOCKET_FILENAME;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002290 }
Radek Krejci469aab82012-07-22 18:42:20 +02002291
Tomas Cejka04e08f42014-03-27 19:52:34 +01002292 /* try to remove if exists */
2293 unlink(sockname);
2294
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002295 /* create listening UNIX socket to accept incoming connections */
Radek Krejci469aab82012-07-22 18:42:20 +02002296 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002297 ERROR("Creating socket failed (%s)", strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002298 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002299 }
2300
2301 local.sun_family = AF_UNIX;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002302 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
Radek Krejci469aab82012-07-22 18:42:20 +02002303 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
2304
2305 if (bind(lsock, (struct sockaddr *) &local, len) == -1) {
2306 if (errno == EADDRINUSE) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002307 ERROR("mod_netconf socket address already in use");
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002308 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002309 }
Tomas Cejkacf44e522015-04-24 17:29:21 +02002310 ERROR("Binding socket failed (%s)", strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002311 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002312 }
2313
2314 if (listen(lsock, MAX_SOCKET_CL) == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002315 ERROR("Setting up listen socket failed (%s)", strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002316 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002317 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002318 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02002319
Tomas Cejka04e08f42014-03-27 19:52:34 +01002320 uid_t user = -1;
2321 if (strlen(CHOWN_USER) > 0) {
2322 struct passwd *p = getpwnam(CHOWN_USER);
2323 if (p != NULL) {
2324 user = p->pw_uid;
2325 }
2326 }
2327 gid_t group = -1;
2328 if (strlen(CHOWN_GROUP) > 0) {
2329 struct group *g = getgrnam(CHOWN_GROUP);
2330 if (g != NULL) {
2331 group = g->gr_gid;
2332 }
2333 }
2334 if (chown(sockname, user, group) == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002335 ERROR("Chown on socket file failed (%s).", strerror(errno));
Tomas Cejka04e08f42014-03-27 19:52:34 +01002336 }
2337
Tomas Cejkaba21b382013-04-13 02:37:32 +02002338 /* prepare internal lists */
2339 netconf_sessions_list = apr_hash_make(pool);
2340
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002341 #ifdef WITH_NOTIFICATIONS
Tomas Cejka47387fd2013-06-10 20:37:46 +02002342 if (notification_init(pool, server) == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002343 ERROR("libwebsockets initialization failed");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002344 use_notifications = 0;
2345 } else {
2346 use_notifications = 1;
2347 }
2348 #endif
2349
Radek Krejci469aab82012-07-22 18:42:20 +02002350 /* setup libnetconf's callbacks */
2351 nc_verbosity(NC_VERB_DEBUG);
Radek Krejci469aab82012-07-22 18:42:20 +02002352 nc_callback_print(clb_print);
2353 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
2354 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
2355 nc_callback_sshauth_password(netconf_callback_sshauth_password);
Tomas Cejkab34b7b12015-06-21 22:54:11 +02002356 nc_callback_sshauth_passphrase(netconf_callback_sshauth_passphrase);
Radek Krejcic11fd862012-07-26 12:41:21 +02002357 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02002358
2359 /* disable publickey authentication */
2360 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
2361
David Kupka8e60a372012-09-04 09:15:20 +02002362 /* create mutex protecting session list */
2363 pthread_rwlockattr_init(&lock_attrs);
2364 /* rwlock is shared only with threads in this process */
2365 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
2366 /* create rw lock */
2367 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002368 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002369 goto error_exit;
David Kupka8e60a372012-09-04 09:15:20 +02002370 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002371 pthread_mutex_init(&ntf_history_lock, NULL);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002372 pthread_mutex_init(&json_lock, NULL);
Tomas Cejkacf44e522015-04-24 17:29:21 +02002373 DEBUG("Initialization of notification history.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002374 if (pthread_key_create(&notif_history_key, NULL) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002375 ERROR("Initialization of notification history failed.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002376 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002377 if (pthread_key_create(&err_reply_key, NULL) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002378 ERROR("Initialization of reply key failed.");
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002379 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02002380
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002381 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002382 while (isterminated == 0) {
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002383 gettimeofday(&tv, NULL);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002384 timediff = (unsigned int)tv.tv_sec - olds;
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002385 #ifdef WITH_NOTIFICATIONS
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002386 if (use_notifications == 1) {
2387 notification_handle();
2388 }
2389 #endif
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002390 if (timediff > ACTIVITY_CHECK_INTERVAL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002391 check_timeout_and_close(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002392 }
Radek Krejci469aab82012-07-22 18:42:20 +02002393
2394 /* open incoming connection if any */
David Kupka8e60a372012-09-04 09:15:20 +02002395 len = sizeof(remote);
2396 client = accept(lsock, (struct sockaddr *) &remote, &len);
Radek Krejci469aab82012-07-22 18:42:20 +02002397 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
2398 apr_sleep(SLEEP_TIME);
2399 continue;
2400 } else if (client == -1 && (errno == EINTR)) {
2401 continue;
2402 } else if (client == -1) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002403 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
Radek Krejci469aab82012-07-22 18:42:20 +02002404 continue;
2405 }
Radek Krejci469aab82012-07-22 18:42:20 +02002406
2407 /* set client's socket as non-blocking */
Radek Krejcif23850c2012-07-23 16:14:17 +02002408 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002409
David Kupka8e60a372012-09-04 09:15:20 +02002410 arg = malloc (sizeof(struct pass_to_thread));
2411 arg->client = client;
2412 arg->pool = pool;
2413 arg->server = server;
2414 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02002415
David Kupka8e60a372012-09-04 09:15:20 +02002416 /* start new thread. It will serve this particular request and then terminate */
2417 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void*)arg)) != 0) {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002418 ERROR("Creating POSIX thread failed: %d\n", ret);
David Kupka8e60a372012-09-04 09:15:20 +02002419 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002420 DEBUG("Thread %lu created", ptids[pthread_count]);
David Kupka8e60a372012-09-04 09:15:20 +02002421 pthread_count++;
2422 ptids = realloc (ptids, sizeof(pthread_t)*(pthread_count+1));
2423 ptids[pthread_count] = 0;
2424 }
Radek Krejci469aab82012-07-22 18:42:20 +02002425
David Kupka8e60a372012-09-04 09:15:20 +02002426 /* check if some thread already terminated, free some resources by joining it */
2427 for (i=0; i<pthread_count; i++) {
2428 if (pthread_tryjoin_np (ptids[i], (void**)&arg) == 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002429 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
David Kupka8e60a372012-09-04 09:15:20 +02002430 pthread_count--;
2431 if (pthread_count > 0) {
2432 /* place last Thread ID on the place of joined one */
2433 ptids[i] = ptids[pthread_count];
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002434 }
Radek Krejci469aab82012-07-22 18:42:20 +02002435 }
2436 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002437 DEBUG("Running %d threads", pthread_count);
Radek Krejci469aab82012-07-22 18:42:20 +02002438 }
2439
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002440 DEBUG("mod_netconf terminating...");
David Kupka8e60a372012-09-04 09:15:20 +02002441 /* join all threads */
2442 for (i=0; i<pthread_count; i++) {
2443 pthread_timedjoin_np (ptids[i], (void**)&arg, &maxtime);
2444 }
Radek Krejci469aab82012-07-22 18:42:20 +02002445
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002446 #ifdef WITH_NOTIFICATIONS
2447 notification_close();
2448 #endif
2449
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002450 /* close all NETCONF sessions */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002451 close_all_nc_sessions(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002452
David Kupka8e60a372012-09-04 09:15:20 +02002453 /* destroy rwlock */
2454 pthread_rwlock_destroy(&session_lock);
2455 pthread_rwlockattr_destroy(&lock_attrs);
2456
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002457 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02002458
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002459 free(ptids);
2460 close(lsock);
Radek Krejci469aab82012-07-22 18:42:20 +02002461 exit(APR_SUCCESS);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002462 return;
2463error_exit:
2464 close(lsock);
2465 free(ptids);
2466 return;
Radek Krejci469aab82012-07-22 18:42:20 +02002467}
2468
Radek Krejcif23850c2012-07-23 16:14:17 +02002469static void *mod_netconf_create_conf(apr_pool_t * pool, server_rec * s)
Radek Krejci469aab82012-07-22 18:42:20 +02002470{
Radek Krejcif23850c2012-07-23 16:14:17 +02002471 mod_netconf_cfg *config = apr_pcalloc(pool, sizeof(mod_netconf_cfg));
2472 apr_pool_create(&config->pool, pool);
2473 config->forkproc = NULL;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002474 config->sockname = SOCKET_FILENAME;
Radek Krejcif23850c2012-07-23 16:14:17 +02002475
2476 return (void *)config;
Radek Krejci469aab82012-07-22 18:42:20 +02002477}
2478
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002479#ifndef HTTPD_INDEPENDENT
Radek Krejci469aab82012-07-22 18:42:20 +02002480static int mod_netconf_master_init(apr_pool_t * pconf, apr_pool_t * ptemp,
2481 apr_pool_t * plog, server_rec * s)
2482{
Radek Krejcif23850c2012-07-23 16:14:17 +02002483 mod_netconf_cfg *config;
2484 apr_status_t res;
2485
Radek Krejci469aab82012-07-22 18:42:20 +02002486 /* These two help ensure that we only init once. */
Radek Krejcia332b692012-11-12 16:15:54 +01002487 void *data = NULL;
Radek Krejcif23850c2012-07-23 16:14:17 +02002488 const char *userdata_key = "netconf_ipc_init";
Radek Krejci469aab82012-07-22 18:42:20 +02002489
2490 /*
2491 * The following checks if this routine has been called before.
2492 * This is necessary because the parent process gets initialized
2493 * a couple of times as the server starts up.
2494 */
2495 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2496 if (!data) {
2497 apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
2498 return (OK);
2499 }
2500
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002501 DEBUG("creating mod_netconf daemon");
Radek Krejcif23850c2012-07-23 16:14:17 +02002502 config = ap_get_module_config(s->module_config, &netconf_module);
Radek Krejcidfaa6ea2012-07-23 09:04:43 +02002503
Radek Krejcif23850c2012-07-23 16:14:17 +02002504 if (config && config->forkproc == NULL) {
2505 config->forkproc = apr_pcalloc(config->pool, sizeof(apr_proc_t));
2506 res = apr_proc_fork(config->forkproc, config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002507 switch (res) {
2508 case APR_INCHILD:
2509 /* set signal handler */
Radek Krejcif23850c2012-07-23 16:14:17 +02002510 apr_signal_init(config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002511 apr_signal(SIGTERM, signal_handler);
2512
2513 /* log start of the separated NETCONF communication process */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002514 DEBUG("mod_netconf daemon started (PID %d)", getpid());
Radek Krejci469aab82012-07-22 18:42:20 +02002515
2516 /* start main loop providing NETCONF communication */
Radek Krejcif23850c2012-07-23 16:14:17 +02002517 forked_proc(config->pool, s);
Radek Krejci469aab82012-07-22 18:42:20 +02002518
Radek Krejcif23850c2012-07-23 16:14:17 +02002519 /* I never should be here, wtf?!? */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002520 DEBUG("mod_netconf daemon unexpectedly stopped");
Radek Krejci469aab82012-07-22 18:42:20 +02002521 exit(APR_EGENERAL);
2522 break;
2523 case APR_INPARENT:
2524 /* register child to be killed (SIGTERM) when the module config's pool dies */
Radek Krejcif23850c2012-07-23 16:14:17 +02002525 apr_pool_note_subprocess(config->pool, config->forkproc, APR_KILL_AFTER_TIMEOUT);
Radek Krejci469aab82012-07-22 18:42:20 +02002526 break;
2527 default:
Tomas Cejkacf44e522015-04-24 17:29:21 +02002528 ERROR("apr_proc_fork() failed");
Radek Krejci469aab82012-07-22 18:42:20 +02002529 break;
2530 }
Radek Krejcif23850c2012-07-23 16:14:17 +02002531 } else {
Tomas Cejkacf44e522015-04-24 17:29:21 +02002532 ERROR("mod_netconf misses configuration structure");
Radek Krejci469aab82012-07-22 18:42:20 +02002533 }
2534
2535 return OK;
2536}
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002537#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002538
Radek Krejci469aab82012-07-22 18:42:20 +02002539/**
2540 * Register module hooks
2541 */
2542static void mod_netconf_register_hooks(apr_pool_t * p)
2543{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002544#ifndef HTTPD_INDEPENDENT
Radek Krejcif23850c2012-07-23 16:14:17 +02002545 ap_hook_post_config(mod_netconf_master_init, NULL, NULL, APR_HOOK_LAST);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002546#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002547}
2548
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002549static const char* cfg_set_socket_path(cmd_parms* cmd, void* cfg, const char* arg)
2550{
2551 ((mod_netconf_cfg*)cfg)->sockname = apr_pstrdup(cmd->pool, arg);
2552 return NULL;
2553}
2554
2555static const command_rec netconf_cmds[] = {
2556 AP_INIT_TAKE1("NetconfSocket", cfg_set_socket_path, NULL, OR_ALL, "UNIX socket path for mod_netconf communication."),
2557 {NULL}
2558};
2559
Radek Krejci469aab82012-07-22 18:42:20 +02002560/* Dispatch list for API hooks */
2561module AP_MODULE_DECLARE_DATA netconf_module = {
2562 STANDARD20_MODULE_STUFF,
2563 NULL, /* create per-dir config structures */
2564 NULL, /* merge per-dir config structures */
Radek Krejcif23850c2012-07-23 16:14:17 +02002565 mod_netconf_create_conf, /* create per-server config structures */
Radek Krejci469aab82012-07-22 18:42:20 +02002566 NULL, /* merge per-server config structures */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002567 netconf_cmds, /* table of config file commands */
Radek Krejci469aab82012-07-22 18:42:20 +02002568 mod_netconf_register_hooks /* register hooks */
2569};
Radek Krejcia332b692012-11-12 16:15:54 +01002570
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002571int main(int argc, char **argv)
2572{
2573 apr_pool_t *pool;
2574 apr_app_initialize(&argc, (char const *const **) &argv, NULL);
2575 apr_signal(SIGTERM, signal_handler);
2576 apr_signal(SIGINT, signal_handler);
2577 apr_pool_create(&pool, NULL);
2578 forked_proc(pool, NULL);
2579 apr_pool_destroy(pool);
2580 apr_terminate();
2581 DEBUG("Terminated");
2582 return 0;
2583}