blob: 1412f123c6569f4629f57941aa8d03cb4f0a3f67 [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
Radek Krejci6cb08982012-07-25 18:01:06 +020083#define SOCKET_FILENAME "/tmp/mod_netconf.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020084#define MAX_SOCKET_CL 10
85#define BUFFER_SIZE 4096
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020086#define NOTIFICATION_QUEUE_SIZE 10
87#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
Tomas Cejka04e39952013-04-19 11:49:38 +020088#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020089
90/* sleep in master process for non-blocking socket reading */
91#define SLEEP_TIME 200
92
93#ifndef offsetof
94#define offsetof(type, member) ((size_t) ((type *) 0)->member)
95#endif
96
Tomas Cejkaef531ee2013-11-12 16:07:00 +010097server_rec *http_server = NULL;
98
Tomas Cejka027f3bc2012-11-10 20:28:36 +010099/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +0200100struct timeval timeout = { 1, 0 };
101
Tomas Cejka5064c232013-01-17 09:30:58 +0100102#define NCWITHDEFAULTS NCWD_MODE_NOTSET
103
104
Radek Krejci469aab82012-07-22 18:42:20 +0200105#define MSG_OK 0
106#define MSG_OPEN 1
107#define MSG_DATA 2
108#define MSG_CLOSE 3
109#define MSG_ERROR 4
110#define MSG_UNKNOWN 5
111
Radek Krejci469aab82012-07-22 18:42:20 +0200112module AP_MODULE_DECLARE_DATA netconf_module;
113
Tomas Cejka47387fd2013-06-10 20:37:46 +0200114pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200115pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100116pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100117pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
118
Tomas Cejka47387fd2013-06-10 20:37:46 +0200119apr_hash_t *netconf_sessions_list = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200120
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200121static pthread_key_t notif_history_key;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200122
Tomas Cejka442258e2014-04-01 18:17:18 +0200123pthread_key_t err_reply_key;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100124
Radek Krejci469aab82012-07-22 18:42:20 +0200125volatile int isterminated = 0;
126
127static char* password;
128
Radek Krejci469aab82012-07-22 18:42:20 +0200129static void signal_handler(int sign)
130{
131 switch (sign) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100132 case SIGINT:
Radek Krejci469aab82012-07-22 18:42:20 +0200133 case SIGTERM:
134 isterminated = 1;
Radek Krejci469aab82012-07-22 18:42:20 +0200135 break;
136 }
137}
138
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200139static char* gen_ncsession_hash( const char* hostname, const char* port, const char* sid)
Radek Krejci469aab82012-07-22 18:42:20 +0200140{
Radek Krejcif23850c2012-07-23 16:14:17 +0200141 unsigned char hash_raw[APR_SHA1_DIGESTSIZE];
142 int i;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200143 char* hash;
Radek Krejcif23850c2012-07-23 16:14:17 +0200144
Radek Krejci469aab82012-07-22 18:42:20 +0200145 apr_sha1_ctx_t sha1_ctx;
146 apr_sha1_init(&sha1_ctx);
147 apr_sha1_update(&sha1_ctx, hostname, strlen(hostname));
148 apr_sha1_update(&sha1_ctx, port, strlen(port));
149 apr_sha1_update(&sha1_ctx, sid, strlen(sid));
Radek Krejcif23850c2012-07-23 16:14:17 +0200150 apr_sha1_final(hash_raw, &sha1_ctx);
Radek Krejci469aab82012-07-22 18:42:20 +0200151
Radek Krejcif23850c2012-07-23 16:14:17 +0200152 /* convert binary hash into hex string, which is printable */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200153 hash = malloc(sizeof(char) * ((2*APR_SHA1_DIGESTSIZE)+1));
Radek Krejcif23850c2012-07-23 16:14:17 +0200154 for (i = 0; i < APR_SHA1_DIGESTSIZE; i++) {
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200155 snprintf(hash + (2*i), 3, "%02x", hash_raw[i]);
Radek Krejcif23850c2012-07-23 16:14:17 +0200156 }
157 //hash[2*APR_SHA1_DIGESTSIZE] = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200158
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200159 return (hash);
Radek Krejci469aab82012-07-22 18:42:20 +0200160}
161
Tomas Cejka8254dba2014-06-22 17:58:59 +0200162int netconf_callback_ssh_hostkey_check (const char* hostname, LIBSSH2_SESSION *session)
Radek Krejci469aab82012-07-22 18:42:20 +0200163{
164 /* always approve */
165 return (EXIT_SUCCESS);
166}
167
168char* netconf_callback_sshauth_password (const char* username, const char* hostname)
169{
170 char* buf;
171
172 buf = malloc ((strlen(password) + 1) * sizeof(char));
173 apr_cpystrn(buf, password, strlen(password) + 1);
174
175 return (buf);
176}
177
178void netconf_callback_sshauth_interactive (const char* name,
179 int name_len,
180 const char* instruction,
181 int instruction_len,
182 int num_prompts,
183 const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts,
184 LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses,
185 void** abstract)
186{
187 int i;
188
189 for (i=0; i<num_prompts; i++) {
190 responses[i].text = malloc ((strlen(password) + 1) * sizeof(char));
191 apr_cpystrn(responses[i].text, password, strlen(password) + 1);
192 responses[i].length = strlen(responses[i].text) + 1;
193 }
194
195 return;
196}
197
Radek Krejcic11fd862012-07-26 12:41:21 +0200198void netconf_callback_error_process(const char* tag,
199 const char* type,
200 const char* severity,
201 const char* apptag,
202 const char* path,
203 const char* message,
204 const char* attribute,
205 const char* element,
206 const char* ns,
207 const char* sid)
208{
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100209 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
Tomas Cejka442258e2014-04-01 18:17:18 +0200210 if (err_reply_p == NULL) {
211 DEBUG("Error message was not allocated. %s", __func__);
212 return;
213 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100214 json_object *err_reply = *err_reply_p;
215
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100216 json_object *array = NULL;
217 if (err_reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100218 DEBUG("error calback: empty error list");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100219 pthread_mutex_lock(&json_lock);
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100220 err_reply = json_object_new_object();
221 array = json_object_new_array();
222 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
223 json_object_object_add(err_reply, "errors", array);
224 if (message != NULL) {
225 json_object_array_add(array, json_object_new_string(message));
226 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100227 pthread_mutex_unlock(&json_lock);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100228 (*err_reply_p) = err_reply;
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100229 } else {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100230 DEBUG("error calback: nonempty error list");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100231 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +0200232 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
Tomas Cejka0858dbb2013-11-19 15:11:00 +0100233 if (message != NULL) {
234 json_object_array_add(array, json_object_new_string(message));
235 }
236 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100237 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100238 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100239 pthread_setspecific(err_reply_key, err_reply_p);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100240 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200241}
242
Tomas Cejka47387fd2013-06-10 20:37:46 +0200243/**
244 * should be used in locked area
245 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100246void prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200247{
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200248 json_object *json_obj = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +0200249 json_object *js_tmp = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100250 char *old_sid = NULL;
251 const char *j_old_sid = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200252 const char *cpbltstr;
253 struct nc_cpblts* cpblts = NULL;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200254
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200255 if (s == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100256 DEBUG("No session given.");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200257 return;
258 }
259
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100260 pthread_mutex_lock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200261 if (s->hello_message != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100262 DEBUG("clean previous hello message");
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200263 //json_object_put(s->hello_message);
Tomas Cejka09629492014-07-10 15:58:06 +0200264 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
265 j_old_sid = json_object_get_string(js_tmp);
266 if (j_old_sid != NULL) {
267 old_sid = strdup(j_old_sid);
268 }
269 json_object_put(s->hello_message);
270 json_object_put(js_tmp);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100271 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200272 s->hello_message = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200273 }
Tomas Cejkadd5cb452014-03-26 15:22:00 +0100274 s->hello_message = json_object_get(json_object_new_object());
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200275 if (session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100276 if (old_sid != NULL) {
277 /* use previous sid */
278 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
279 free(old_sid);
Tomas Cejkaa3ffdba2014-03-27 15:12:21 +0100280 old_sid = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100281 } else {
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200282 /* we don't have old sid */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100283 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
284 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200285 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
286 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
287 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
288 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
289 cpblts = nc_session_get_cpblts (session);
290 if (cpblts != NULL) {
291 json_obj = json_object_new_array();
292 nc_cpblts_iter_start (cpblts);
293 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
294 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
295 }
296 json_object_object_add(s->hello_message, "capabilities", json_obj);
297 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100298 DEBUG("%s", json_object_to_json_string(s->hello_message));
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200299 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100300 DEBUG("Session was not given.");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200301 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
302 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
303 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100304 DEBUG("Status info from hello message prepared");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100305 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200306
307}
308
Tomas Cejka442258e2014-04-01 18:17:18 +0200309void create_err_reply_p()
310{
311 json_object **err_reply = calloc(1, sizeof(json_object **));
312 if (err_reply == NULL) {
313 DEBUG("Allocation of err_reply storage failed!");
314 return;
315 }
316 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
317 DEBUG("cannot set thread-specific value.");
318 }
319}
320
321void clean_err_reply()
322{
323 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
324 if (err_reply != NULL) {
325 if (*err_reply != NULL) {
326 pthread_mutex_lock(&json_lock);
327 json_object_put(*err_reply);
328 pthread_mutex_unlock(&json_lock);
329 }
330 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
331 DEBUG("Cannot set thread-specific hash value.");
332 }
333 }
334}
335
336void free_err_reply()
337{
338 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
339 if (err_reply != NULL) {
340 if (*err_reply != NULL) {
341 pthread_mutex_lock(&json_lock);
342 json_object_put(*err_reply);
343 pthread_mutex_unlock(&json_lock);
344 }
345 free(err_reply);
346 err_reply = NULL;
347 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
348 DEBUG("Cannot set thread-specific hash value.");
349 }
350 }
351}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200352
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200353/**
354 * \defgroup netconf_operations NETCONF operations
355 * The list of NETCONF operations that mod_netconf supports.
356 * @{
357 */
358
359/**
360 * \brief Connect to NETCONF server
361 *
362 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
363 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100364static char* netconf_connect(apr_pool_t* pool, const char* host, const char* port, const char* user, const char* pass, struct nc_cpblts * cpblts)
Radek Krejci469aab82012-07-22 18:42:20 +0200365{
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200366 struct nc_session* session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200367 struct session_with_mutex * locked_session;
Radek Krejcif23850c2012-07-23 16:14:17 +0200368 char *session_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200369
370 /* connect to the requested NETCONF server */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200371 password = (char*)pass;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100372 DEBUG("prepare to connect %s@%s:%s", user, host, port);
David Kupka8e60a372012-09-04 09:15:20 +0200373 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100374 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +0200375
Radek Krejci469aab82012-07-22 18:42:20 +0200376 /* if connected successful, add session to the list */
377 if (session != NULL) {
378 /* generate hash for the session */
Radek Krejcic11fd862012-07-26 12:41:21 +0200379 session_key = gen_ncsession_hash(
380 (host==NULL) ? "localhost" : host,
381 (port==NULL) ? "830" : port,
Radek Krejcia282bed2012-07-27 14:43:45 +0200382 nc_session_get_id(session));
David Kupka8e60a372012-09-04 09:15:20 +0200383
Tomas Cejkaba21b382013-04-13 02:37:32 +0200384 /** \todo allocate from apr_pool */
Tomas Cejka73496bf2014-03-26 15:31:09 +0100385 if ((locked_session = calloc(1, sizeof(struct session_with_mutex))) == NULL || pthread_mutex_init (&locked_session->lock, NULL) != 0) {
David Kupka8e60a372012-09-04 09:15:20 +0200386 nc_session_free(session);
Tomas Cejka73496bf2014-03-26 15:31:09 +0100387 session = NULL;
388 free(locked_session);
389 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100390 DEBUG("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200391 return NULL;
392 }
393 locked_session->session = session;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200394 locked_session->last_activity = apr_time_now();
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200395 locked_session->hello_message = NULL;
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200396 locked_session->closed = 0;
David Kupka8e60a372012-09-04 09:15:20 +0200397 pthread_mutex_init (&locked_session->lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100398 DEBUG("Before session_lock");
David Kupka8e60a372012-09-04 09:15:20 +0200399 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100400 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200401 if (pthread_rwlock_wrlock (&session_lock) != 0) {
402 nc_session_free(session);
403 free (locked_session);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100404 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +0200405 return NULL;
406 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200407 locked_session->notifications = apr_array_make(pool, NOTIFICATION_QUEUE_SIZE, sizeof(notification_t));
Tomas Cejka654f84e2013-04-19 11:55:01 +0200408 locked_session->ntfc_subscribed = 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100409 DEBUG("Add connection to the list");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200410 apr_hash_set(netconf_sessions_list, apr_pstrdup(pool, session_key), APR_HASH_KEY_STRING, (void *) locked_session);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100411 DEBUG("Before session_unlock");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200412
Tomas Cejka47387fd2013-06-10 20:37:46 +0200413 /* lock session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100414 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200415 pthread_mutex_lock(&locked_session->lock);
416
417 /* unlock session list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100418 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200419 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100420 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +0200421 }
422
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200423 /* store information about session from hello message for future usage */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100424 prepare_status_message(locked_session, session);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200425
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100426 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200427 pthread_mutex_unlock(&locked_session->lock);
428
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100429 DEBUG("NETCONF session established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200430 return (session_key);
Radek Krejci469aab82012-07-22 18:42:20 +0200431 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100432 DEBUG("Connection could not be established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200433 return (NULL);
Radek Krejci469aab82012-07-22 18:42:20 +0200434 }
435
Radek Krejci469aab82012-07-22 18:42:20 +0200436}
437
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100438static int close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +0200439{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100440 DEBUG("lock private lock.");
441 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200442 if (pthread_mutex_lock(&locked_session->lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100443 DEBUG("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200444 }
445 locked_session->ntfc_subscribed = 0;
446 locked_session->closed = 1;
Tomas Cejka73496bf2014-03-26 15:31:09 +0100447 if (locked_session->session != NULL) {
448 nc_session_free(locked_session->session);
449 locked_session->session = NULL;
450 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100451 DEBUG("session closed.");
452 DEBUG("unlock private lock.");
453 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200454 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100455 DEBUG("Error while locking rwlock");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200456 }
457
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100458 DEBUG("unlock session lock.");
459 DEBUG("closed session, disabled notif(?), wait 2s");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200460 usleep(500000); /* let notification thread stop */
461
462 /* session shouldn't be used by now */
463 /** \todo free all notifications from queue */
464 apr_array_clear(locked_session->notifications);
465 pthread_mutex_destroy(&locked_session->lock);
466 if (locked_session->hello_message != NULL) {
467 /** \todo free hello_message */
468 //json_object_put(locked_session->hello_message);
469 locked_session->hello_message = NULL;
470 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200471 locked_session->session = NULL;
Tomas Cejkaaecc5f72013-10-01 00:03:50 +0200472 free(locked_session);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200473 locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100474 DEBUG("NETCONF session closed, everything cleared.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200475 return (EXIT_SUCCESS);
476}
477
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100478static int netconf_close(const char* session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +0200479{
David Kupka8e60a372012-09-04 09:15:20 +0200480 struct session_with_mutex * locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +0200481
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100482 DEBUG("Key in hash to close: %s", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200483
David Kupka8e60a372012-09-04 09:15:20 +0200484 /* get exclusive (write) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100485 DEBUG("lock session lock.");
486 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200487 if (pthread_rwlock_wrlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100488 DEBUG("Error while locking rwlock");
489 (*reply) = create_error("Internal: Error while locking.");
David Kupka8e60a372012-09-04 09:15:20 +0200490 return EXIT_FAILURE;
491 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200492 /* get session to close */
493 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
494 /* remove session from the active sessions list -> nobody new can now work with session */
495 apr_hash_set(netconf_sessions_list, session_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkabdedcd32013-06-09 11:54:53 +0200496
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100497 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200498 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100499 DEBUG("Error while unlocking rwlock");
500 (*reply) = create_error("Internal: Error while unlocking.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200501 }
502
503 if ((locked_session != NULL) && (locked_session->session != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100504 return close_and_free_session(locked_session);
Radek Krejci469aab82012-07-22 18:42:20 +0200505 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100506 DEBUG("Unknown session to close");
507 (*reply) = create_error("Internal: Unkown session to close.");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200508 return (EXIT_FAILURE);
Radek Krejci469aab82012-07-22 18:42:20 +0200509 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100510 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +0200511}
512
Tomas Cejkac7929632013-10-24 19:25:15 +0200513/**
514 * Test reply message type and return error message.
515 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200516 * \param[in] session nc_session internal struct
517 * \param[in] session_key session key, NULL to disable disconnect on error
518 * \param[in] msgt RPC-REPLY message type
519 * \param[out] data
520 * \return NULL on success
521 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100522json_object *netconf_test_reply(struct nc_session *session, const char *session_key, NC_MSG_TYPE msgt, nc_reply *reply, char **data)
Tomas Cejkac7929632013-10-24 19:25:15 +0200523{
524 NC_REPLY_TYPE replyt;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100525 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200526
527 /* process the result of the operation */
528 switch (msgt) {
529 case NC_MSG_UNKNOWN:
530 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100531 DEBUG("mod_netconf: receiving rpc-reply failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200532 if (session_key != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100533 netconf_close(session_key, &err);
534 }
535 if (err != NULL) {
536 return err;
Tomas Cejkac7929632013-10-24 19:25:15 +0200537 }
538 return create_error("Internal: Receiving RPC-REPLY failed.");
539 }
540 case NC_MSG_NONE:
541 /* there is error handled by callback */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100542 if (data != NULL) {
543 free(*data);
Tomas Cejka7b1e3bd2014-04-08 14:34:28 +0200544 (*data) = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100545 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200546 return NULL;
547 case NC_MSG_REPLY:
548 switch (replyt = nc_reply_get_type(reply)) {
549 case NC_REPLY_OK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100550 if ((data != NULL) && (*data != NULL)) {
551 free(*data);
552 (*data) = NULL;
553 }
554 return create_ok();
Tomas Cejkac7929632013-10-24 19:25:15 +0200555 case NC_REPLY_DATA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100556 if (((*data) = nc_reply_get_data(reply)) == NULL) {
557 DEBUG("mod_netconf: no data from reply");
Tomas Cejkac7929632013-10-24 19:25:15 +0200558 return create_error("Internal: No data from reply received.");
559 } else {
560 return NULL;
561 }
562 break;
563 case NC_REPLY_ERROR:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100564 DEBUG("mod_netconf: unexpected rpc-reply (%d)", replyt);
565 if (data != NULL) {
566 free(*data);
567 (*data) = NULL;
568 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200569 return create_error(nc_reply_get_errormsg(reply));
570 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100571 DEBUG("mod_netconf: unexpected rpc-reply (%d)", replyt);
572 if (data != NULL) {
573 free(*data);
574 (*data) = NULL;
575 }
Tomas Cejka60885252014-03-26 15:45:47 +0100576 return create_error("Unknown type of NETCONF reply.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200577 }
578 break;
579 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100580 DEBUG("mod_netconf: unexpected reply message received (%d)", msgt);
581 if (data != NULL) {
582 free(*data);
583 (*data) = NULL;
584 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200585 return create_error("Internal: Unexpected RPC-REPLY message type.");
586 }
587}
588
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100589json_object *netconf_unlocked_op(struct nc_session *session, nc_rpc* rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +0200590{
591 nc_reply* reply = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200592 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +0200593
594 /* check requests */
595 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100596 DEBUG("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200597 return create_error("Internal error: RPC is not created");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200598 }
599
600 if (session != NULL) {
601 /* send the request and get the reply */
602 msgt = nc_session_send_recv(session, rpc, &reply);
603 /* process the result of the operation */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100604 return netconf_test_reply(session, NULL, msgt, reply, NULL);
Tomas Cejka6b886e02013-07-05 09:53:17 +0200605 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100606 DEBUG("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200607 return create_error("Internal error: Unknown session to process.");
Tomas Cejka6b886e02013-07-05 09:53:17 +0200608 }
609}
610
Tomas Cejkac7929632013-10-24 19:25:15 +0200611/**
612 * Perform RPC method that returns data.
613 *
Tomas Cejkac7929632013-10-24 19:25:15 +0200614 * \param[in] session_key session identifier
615 * \param[in] rpc RPC message to perform
616 * \param[out] received_data received data string, can be NULL when no data expected, value can be set to NULL if no data received
617 * \return NULL on success, json object with error otherwise
618 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100619static json_object *netconf_op(const char* session_key, nc_rpc* rpc, char **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +0200620{
621 struct nc_session *session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200622 struct session_with_mutex * locked_session;
Tomas Cejka00635972013-06-03 15:10:52 +0200623 nc_reply* reply = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200624 json_object *res = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100625 char *data = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100626 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200627
Radek Krejci8e4632a2012-07-26 13:40:34 +0200628 /* check requests */
629 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100630 DEBUG("mod_netconf: rpc is not created");
Tomas Cejkac7929632013-10-24 19:25:15 +0200631 res = create_error("Internal: RPC could not be created.");
632 data = NULL;
633 goto finished;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200634 }
635
David Kupka8e60a372012-09-04 09:15:20 +0200636 /* get non-exclusive (read) access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100637 DEBUG("LOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200638 if (pthread_rwlock_rdlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100639 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200640 res = create_error("Internal: Lock failed.");
641 data = NULL;
642 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200643 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200644 /* get session where send the RPC */
Tomas Cejka47387fd2013-06-10 20:37:46 +0200645 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
David Kupka8e60a372012-09-04 09:15:20 +0200646 if (locked_session != NULL) {
647 session = locked_session->session;
648 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200649 if (session != NULL) {
David Kupka8e60a372012-09-04 09:15:20 +0200650 /* get exclusive access to session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100651 DEBUG("LOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200652 if (pthread_mutex_lock(&locked_session->lock) != 0) {
653 /* unlock before returning error */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100654 DEBUG("UNLOCK wrlock %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200655 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200656
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100657 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200658 res = create_error("Internal: Could not unlock.");
659 goto finished;
David Kupka8e60a372012-09-04 09:15:20 +0200660 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200661 res = create_error("Internal: Could not unlock.");
David Kupka8e60a372012-09-04 09:15:20 +0200662 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100663 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200664 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200665
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100666 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkac7929632013-10-24 19:25:15 +0200667 res = create_error("Internal: Could not unlock.");
Tomas Cejka47387fd2013-06-10 20:37:46 +0200668 }
669
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200670 locked_session->last_activity = apr_time_now();
Tomas Cejkac7929632013-10-24 19:25:15 +0200671
Radek Krejci035bf4e2012-07-25 10:59:09 +0200672 /* send the request and get the reply */
Radek Krejcia332b692012-11-12 16:15:54 +0100673 msgt = nc_session_send_recv(session, rpc, &reply);
674
David Kupka8e60a372012-09-04 09:15:20 +0200675 /* first release exclusive lock for this session */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100676 DEBUG("UNLOCK mutex %s", __func__);
David Kupka8e60a372012-09-04 09:15:20 +0200677 pthread_mutex_unlock(&locked_session->lock);
678 /* end of critical section */
Radek Krejci035bf4e2012-07-25 10:59:09 +0200679
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100680 res = netconf_test_reply(session, session_key, msgt, reply, &data);
Radek Krejci035bf4e2012-07-25 10:59:09 +0200681 } else {
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100682 /* release lock on failure */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100683 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100684 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100685 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100686 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100687 DEBUG("Unknown session to process.");
Tomas Cejkac7929632013-10-24 19:25:15 +0200688 res = create_error("Unknown session to process.");
689 data = NULL;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200690 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200691finished:
692 nc_reply_free(reply);
693 if (received_data != NULL) {
694 (*received_data) = data;
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100695 } else {
696 if (data != NULL) {
697 free(data);
698 data = NULL;
699 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200700 }
Tomas Cejkac7929632013-10-24 19:25:15 +0200701 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200702}
703
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100704static char* netconf_getconfig(const char* session_key, NC_DATASTORE source, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200705{
706 nc_rpc* rpc;
707 struct nc_filter *f = NULL;
708 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200709 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200710
711 /* create filter if set */
712 if (filter != NULL) {
713 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
714 }
715
716 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100717 rpc = nc_rpc_getconfig (source, f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200718 nc_filter_free(f);
719 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100720 DEBUG("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200721 return (NULL);
722 }
723
Tomas Cejka94674662013-09-13 15:55:24 +0200724 /* tell server to show all elements even if they have default values */
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100725#ifdef HAVE_WITHDEFAULTS_TAGGED
Tomas Cejka1c3840d2014-01-29 21:08:23 +0100726 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100727#else
Tomas Cejkac0e166b2014-07-08 16:04:58 +0200728 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
729 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
Tomas Cejkae8bd27c2014-03-27 15:16:31 +0100730#endif
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100731 DEBUG("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200732 }
733
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100734 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200735 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100736 if (res != NULL) {
737 (*err) = res;
738 } else {
739 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200740 }
741
Radek Krejci8e4632a2012-07-26 13:40:34 +0200742 return (data);
743}
744
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100745static char* netconf_getschema(const char* session_key, const char* identifier, const char* version, const char* format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100746{
747 nc_rpc* rpc;
748 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200749 json_object *res = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100750
751 /* create requests */
Tomas Cejka94da2c52013-01-08 18:20:30 +0100752 rpc = nc_rpc_getschema(identifier, version, format);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100753 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100754 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100755 return (NULL);
756 }
757
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100758 res = netconf_op(session_key, rpc, &data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100759 nc_rpc_free (rpc);
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100760 if (res != NULL) {
761 (*err) = res;
762 } else {
763 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200764 }
765
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100766 return (data);
767}
768
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100769static char* netconf_get(const char* session_key, const char* filter, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200770{
771 nc_rpc* rpc;
772 struct nc_filter *f = NULL;
773 char* data = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200774 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200775
776 /* create filter if set */
777 if (filter != NULL) {
778 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
779 }
780
781 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100782 rpc = nc_rpc_get (f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200783 nc_filter_free(f);
784 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100785 DEBUG("mod_netconf: creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200786 return (NULL);
787 }
788
Tomas Cejka94674662013-09-13 15:55:24 +0200789 /* tell server to show all elements even if they have default values */
Tomas Cejkac0e166b2014-07-08 16:04:58 +0200790 if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_NOTSET)) {
791 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL)) {
792 //if (nc_rpc_capability_attr(rpc, NC_CAP_ATTR_WITHDEFAULTS_MODE, NCWD_MODE_ALL_TAGGED)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100793 DEBUG("mod_netconf: setting withdefaults failed");
Tomas Cejka94674662013-09-13 15:55:24 +0200794 }
795
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100796 res = netconf_op(session_key, rpc, &data);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200797 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200798 if (res == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100799 (*err) = res;
800 } else {
801 (*err) = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200802 }
803
Radek Krejci8e4632a2012-07-26 13:40:34 +0200804 return (data);
805}
806
Tomas Cejkab4d05872014-02-14 22:44:38 +0100807static json_object *netconf_copyconfig(const char* session_key, NC_DATASTORE source, NC_DATASTORE target, const char* config, const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200808{
809 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200810 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200811
812 /* create requests */
Tomas Cejkab4d05872014-02-14 22:44:38 +0100813 if (source == NC_DATASTORE_CONFIG) {
Tomas Cejka5064c232013-01-17 09:30:58 +0100814 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100815 /* config, url */
816 rpc = nc_rpc_copyconfig(source, target, config, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100817 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100818 /* config, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100819 rpc = nc_rpc_copyconfig(source, target, config);
820 }
Tomas Cejkab4d05872014-02-14 22:44:38 +0100821 } else if (source == NC_DATASTORE_URL) {
822 if (target == NC_DATASTORE_URL) {
823 /* url, url */
824 rpc = nc_rpc_copyconfig(source, target, uri_src, uri_trg);
825 } else {
826 /* url, datastore */
827 rpc = nc_rpc_copyconfig(source, target, uri_src);
828 }
Tomas Cejka5064c232013-01-17 09:30:58 +0100829 } else {
830 if (target == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100831 /* datastore, url */
832 rpc = nc_rpc_copyconfig(source, target, uri_trg);
Tomas Cejka5064c232013-01-17 09:30:58 +0100833 } else {
Tomas Cejkab4d05872014-02-14 22:44:38 +0100834 /* datastore, datastore */
Tomas Cejka5064c232013-01-17 09:30:58 +0100835 rpc = nc_rpc_copyconfig(source, target);
836 }
837 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200838 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100839 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200840 return create_error("Internal: Creating rpc request failed");
Radek Krejci8e4632a2012-07-26 13:40:34 +0200841 }
842
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100843 res = netconf_op(session_key, rpc, NULL);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200844 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200845
846 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200847}
Radek Krejci035bf4e2012-07-25 10:59:09 +0200848
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100849static json_object *netconf_editconfig(const char* session_key, NC_DATASTORE source, NC_DATASTORE target, NC_EDIT_DEFOP_TYPE defop, NC_EDIT_ERROPT_TYPE erropt, NC_EDIT_TESTOPT_TYPE testopt, const char* config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +0200850{
851 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200852 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200853
854 /* create requests */
Tomas Cejka8f3031e2014-02-14 23:15:08 +0100855 rpc = nc_rpc_editconfig(target, source, defop, erropt, testopt, config_or_url);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200856 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100857 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200858 return create_error("Internal: Creating rpc request failed");
Radek Krejci62ab34b2012-07-26 13:42:05 +0200859 }
860
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100861 res = netconf_op(session_key, rpc, NULL);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200862 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200863
864 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200865}
866
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100867static json_object *netconf_killsession(const char* session_key, const char* sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200868{
869 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200870 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200871
872 /* create requests */
873 rpc = nc_rpc_killsession(sid);
874 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100875 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200876 return create_error("Internal: Creating rpc request failed");
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200877 }
878
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100879 res = netconf_op(session_key, rpc, NULL);
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200880 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200881 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200882}
883
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100884static json_object *netconf_onlytargetop(const char* session_key, NC_DATASTORE target, nc_rpc* (*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +0200885{
886 nc_rpc* rpc;
Tomas Cejkac7929632013-10-24 19:25:15 +0200887 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +0200888
889 /* create requests */
Radek Krejci5cd7d422012-07-26 14:50:29 +0200890 rpc = op_func(target);
Radek Krejci2f318372012-07-26 14:22:35 +0200891 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100892 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200893 return create_error("Internal: Creating rpc request failed");
Radek Krejci2f318372012-07-26 14:22:35 +0200894 }
895
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100896 res = netconf_op(session_key, rpc, NULL);
Radek Krejci2f318372012-07-26 14:22:35 +0200897 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +0200898 return res;
Radek Krejci2f318372012-07-26 14:22:35 +0200899}
900
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100901static json_object *netconf_deleteconfig(const char* session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200902{
Tomas Cejka404d37e2013-04-13 02:31:35 +0200903 nc_rpc *rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200904 json_object *res = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +0200905 if (target != NC_DATASTORE_URL) {
906 rpc = nc_rpc_deleteconfig(target);
907 } else {
Tomas Cejkac7929632013-10-24 19:25:15 +0200908 rpc = nc_rpc_deleteconfig(target, url);
909 }
910 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100911 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200912 return create_error("Internal: Creating rpc request failed");
Tomas Cejka404d37e2013-04-13 02:31:35 +0200913 }
914
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100915 res = netconf_op(session_key, rpc, NULL);
Tomas Cejkac7929632013-10-24 19:25:15 +0200916 nc_rpc_free (rpc);
917 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +0200918}
919
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100920static json_object *netconf_lock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200921{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100922 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200923}
924
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100925static json_object *netconf_unlock(const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200926{
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100927 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200928}
929
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100930static json_object *netconf_generic(const char* session_key, const char* content, char** data)
Radek Krejci80c10d92012-07-30 08:38:50 +0200931{
Tomas Cejka00635972013-06-03 15:10:52 +0200932 nc_rpc* rpc = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +0200933 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +0200934
935 /* create requests */
936 rpc = nc_rpc_generic(content);
937 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100938 DEBUG("mod_netconf: creating rpc request failed");
Tomas Cejkac7929632013-10-24 19:25:15 +0200939 return create_error("Internal: Creating rpc request failed");
Radek Krejci80c10d92012-07-30 08:38:50 +0200940 }
941
Radek Krejcia332b692012-11-12 16:15:54 +0100942 if (data != NULL) {
Tomas Cejkac7929632013-10-24 19:25:15 +0200943 // TODO ?free(*data);
944 (*data) = NULL;
Radek Krejcia332b692012-11-12 16:15:54 +0100945 }
Radek Krejci80c10d92012-07-30 08:38:50 +0200946
947 /* get session where send the RPC */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100948 res = netconf_op(session_key, rpc, data);
Tomas Cejkac7929632013-10-24 19:25:15 +0200949 nc_rpc_free (rpc);
950 return res;
Radek Krejci80c10d92012-07-30 08:38:50 +0200951}
952
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200953/**
954 * @}
955 *//* netconf_operations */
956
Radek Krejci7338bde2012-08-10 12:57:30 +0200957void clb_print(NC_VERB_LEVEL level, const char* msg)
Radek Krejci469aab82012-07-22 18:42:20 +0200958{
Radek Krejci7338bde2012-08-10 12:57:30 +0200959 switch (level) {
960 case NC_VERB_ERROR:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100961 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200962 break;
963 case NC_VERB_WARNING:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100964 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200965 break;
966 case NC_VERB_VERBOSE:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100967 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200968 break;
969 case NC_VERB_DEBUG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100970 DEBUG("%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200971 break;
972 }
Radek Krejci469aab82012-07-22 18:42:20 +0200973}
974
Tomas Cejka64b87482013-06-03 16:30:53 +0200975/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +0200976 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +0200977 * Caller should free message memory.
978 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +0200979 * \return pointer to message
980 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100981char *get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +0200982{
983 /* read json in chunked framing */
984 unsigned int buffer_size = 0;
985 ssize_t buffer_len = 0;
986 char *buffer = NULL;
987 char c;
988 ssize_t ret;
989 int i, chunk_len;
990 char chunk_len_str[12];
991
992 while (1) {
993 /* read chunk length */
994 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
995 if (buffer != NULL) {
996 free (buffer);
997 buffer = NULL;
998 }
999 break;
1000 }
1001 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
1002 if (buffer != NULL) {
1003 free (buffer);
1004 buffer = NULL;
1005 }
1006 break;
1007 }
1008 i=0;
1009 memset (chunk_len_str, 0, 12);
1010 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
1011 if (i==0 && c == '#') {
1012 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
1013 /* end but invalid */
1014 if (buffer != NULL) {
1015 free (buffer);
1016 buffer = NULL;
1017 }
1018 }
1019 /* end of message, double-loop break */
1020 goto msg_complete;
1021 }
1022 chunk_len_str[i++] = c;
1023 if (i==11) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001024 DEBUG("Message is too long, buffer for length is not big enought!!!!");
Tomas Cejka64b87482013-06-03 16:30:53 +02001025 break;
1026 }
1027 }
1028 if (c != '\n') {
1029 if (buffer != NULL) {
1030 free (buffer);
1031 buffer = NULL;
1032 }
1033 break;
1034 }
1035 chunk_len_str[i] = 0;
1036 if ((chunk_len = atoi (chunk_len_str)) == 0) {
1037 if (buffer != NULL) {
1038 free (buffer);
1039 buffer = NULL;
1040 }
1041 break;
1042 }
1043 buffer_size += chunk_len+1;
1044 buffer = realloc (buffer, sizeof(char)*buffer_size);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001045 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
Tomas Cejka64b87482013-06-03 16:30:53 +02001046 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
1047 if (buffer != NULL) {
1048 free (buffer);
1049 buffer = NULL;
1050 }
1051 break;
1052 }
1053 buffer_len += ret;
1054 }
1055msg_complete:
1056 return buffer;
1057}
1058
Tomas Cejkad5b53772013-06-08 23:01:07 +02001059NC_DATASTORE parse_datastore(const char *ds)
1060{
1061 if (strcmp(ds, "running") == 0) {
1062 return NC_DATASTORE_RUNNING;
1063 } else if (strcmp(ds, "startup") == 0) {
1064 return NC_DATASTORE_STARTUP;
1065 } else if (strcmp(ds, "candidate") == 0) {
1066 return NC_DATASTORE_CANDIDATE;
Tomas Cejka4003a702013-10-01 00:02:45 +02001067 } else if (strcmp(ds, "url") == 0) {
1068 return NC_DATASTORE_URL;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001069 } else if (strcmp(ds, "config") == 0) {
1070 return NC_DATASTORE_CONFIG;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001071 }
1072 return -1;
1073}
1074
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001075NC_EDIT_TESTOPT_TYPE parse_testopt(const char *t)
1076{
1077 if (strcmp(t, "notset") == 0) {
1078 return NC_EDIT_TESTOPT_NOTSET;
1079 } else if (strcmp(t, "testset") == 0) {
1080 return NC_EDIT_TESTOPT_TESTSET;
1081 } else if (strcmp(t, "set") == 0) {
1082 return NC_EDIT_TESTOPT_SET;
1083 } else if (strcmp(t, "test") == 0) {
1084 return NC_EDIT_TESTOPT_TEST;
1085 }
1086 return NC_EDIT_TESTOPT_ERROR;
1087}
1088
Tomas Cejkad5b53772013-06-08 23:01:07 +02001089json_object *create_error(const char *errmess)
1090{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001091 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001092 json_object *reply = json_object_new_object();
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001093 json_object *array = json_object_new_array();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001094 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001095 json_object_array_add(array, json_object_new_string(errmess));
1096 json_object_object_add(reply, "errors", array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001097 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001098 return reply;
1099
1100}
1101
1102json_object *create_data(const char *data)
1103{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001104 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001105 json_object *reply = json_object_new_object();
1106 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1107 json_object_object_add(reply, "data", json_object_new_string(data));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001108 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001109 return reply;
1110}
1111
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001112json_object *create_ok()
1113{
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001114 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001115 json_object *reply = json_object_new_object();
1116 reply = json_object_new_object();
1117 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001118 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001119 return reply;
1120}
1121
Tomas Cejka09629492014-07-10 15:58:06 +02001122char *get_param_string(json_object *data, const char *name)
1123{
1124 json_object *js_tmp = NULL;
1125 char *res = NULL;
1126 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
1127 res = strdup(json_object_get_string(js_tmp));
1128 json_object_put(js_tmp);
1129 }
1130 return res;
1131}
1132
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001133json_object *handle_op_connect(apr_pool_t *pool, json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001134{
Tomas Cejka09629492014-07-10 15:58:06 +02001135 char *host = NULL;
1136 char *port = NULL;
1137 char *user = NULL;
1138 char *pass = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001139 json_object *capabilities = NULL;
1140 json_object *reply = NULL;
1141 char *session_key_hash = NULL;
1142 struct nc_cpblts* cpblts = NULL;
1143 unsigned int len, i;
1144
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001145 DEBUG("Request: Connect");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001146 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001147
Tomas Cejka09629492014-07-10 15:58:06 +02001148 host = get_param_string(request, "host");
1149 port = get_param_string(request, "port");
1150 user = get_param_string(request, "user");
1151 pass = get_param_string(request, "pass");
1152
1153 if (json_object_object_get_ex(request, "capabilities", &capabilities) == TRUE) {
1154 if ((capabilities != NULL) && ((len = json_object_array_length(capabilities)) > 0)) {
1155 cpblts = nc_cpblts_new(NULL);
1156 for (i=0; i<len; i++) {
1157 nc_cpblts_add(cpblts, json_object_get_string(json_object_array_get_idx(capabilities, i)));
1158 }
1159 } else {
1160 DEBUG("no capabilities specified");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001161 }
Tomas Cejka09629492014-07-10 15:58:06 +02001162 json_object_put(capabilities);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001163 }
Tomas Cejka09629492014-07-10 15:58:06 +02001164
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001165 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001166
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001167 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001168 if ((host == NULL) || (user == NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001169 DEBUG("Cannot connect - insufficient input.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001170 session_key_hash = NULL;
1171 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001172 session_key_hash = netconf_connect(pool, host, port, user, pass, cpblts);
1173 DEBUG("hash: %s", session_key_hash);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001174 }
1175 if (cpblts != NULL) {
1176 nc_cpblts_free(cpblts);
1177 }
1178
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001179 GETSPEC_ERR_REPLY
1180
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001181 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001182 if (session_key_hash == NULL) {
1183 /* negative reply */
1184 if (err_reply == NULL) {
1185 reply = json_object_new_object();
1186 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1187 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001188 DEBUG("Connection failed.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001189 } else {
1190 /* use filled err_reply from libnetconf's callback */
1191 reply = err_reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001192 DEBUG("Connect - error from libnetconf's callback.");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001193 }
1194 } else {
1195 /* positive reply */
1196 reply = json_object_new_object();
1197 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1198 json_object_object_add(reply, "session", json_object_new_string(session_key_hash));
1199
1200 free(session_key_hash);
1201 }
Tomas Cejka09629492014-07-10 15:58:06 +02001202 memset(pass, 0, strlen(pass));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001203 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001204 CHECK_AND_FREE(host);
1205 CHECK_AND_FREE(user);
1206 CHECK_AND_FREE(port);
1207 CHECK_AND_FREE(pass);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001208 return reply;
1209}
1210
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001211json_object *handle_op_get(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001212{
Tomas Cejka09629492014-07-10 15:58:06 +02001213 char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001214 char *data = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001215 json_object *reply = NULL;
1216
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001217 DEBUG("Request: get (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001218
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001219 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001220 filter = get_param_string(request, "filter");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001221 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001222
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001223 if ((data = netconf_get(session_key, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001224 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001225 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001226 reply = create_data(data);
1227 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001228 }
1229 return reply;
1230}
1231
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001232json_object *handle_op_getconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001233{
1234 NC_DATASTORE ds_type_s = -1;
Tomas Cejka09629492014-07-10 15:58:06 +02001235 char *filter = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001236 char *data = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001237 char *source = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001238 json_object *reply = NULL;
1239
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001240 DEBUG("Request: get-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001241
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001242 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001243 filter = get_param_string(request, "filter");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001244
Tomas Cejka09629492014-07-10 15:58:06 +02001245 source = get_param_string(request, "source");
1246 if (source != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001247 ds_type_s = parse_datastore(source);
1248 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001249 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001250
Tomas Cejkad5b53772013-06-08 23:01:07 +02001251 if (ds_type_s == -1) {
Tomas Cejka09629492014-07-10 15:58:06 +02001252 reply = create_error("Invalid source repository type requested.");
1253 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001254 }
1255
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001256 if ((data = netconf_getconfig(session_key, ds_type_s, filter, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001257 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001258 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001259 reply = create_data(data);
1260 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001261 }
Tomas Cejka09629492014-07-10 15:58:06 +02001262finalize:
1263 CHECK_AND_FREE(filter);
1264 CHECK_AND_FREE(source);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001265 return reply;
1266}
1267
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001268json_object *handle_op_getschema(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001269{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001270 char *data = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001271 char *identifier = NULL;
1272 char *version = NULL;
1273 char *format = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001274 json_object *reply = NULL;
1275
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001276 DEBUG("Request: get-schema (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001277 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001278 identifier = get_param_string(request, "identifier");
1279 version = get_param_string(request, "version");
1280 format = get_param_string(request, "format");
Tomas Cejkab9e7efe2014-03-28 21:09:04 +01001281 pthread_mutex_unlock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001282
Tomas Cejkad5b53772013-06-08 23:01:07 +02001283 if (identifier == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001284 reply = create_error("No identifier for get-schema supplied.");
1285 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001286 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001287
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001288 DEBUG("get-schema(version: %s, format: %s)", version, format);
1289 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001290 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001291 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001292 reply = create_data(data);
1293 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001294 }
Tomas Cejka09629492014-07-10 15:58:06 +02001295finalize:
1296 CHECK_AND_FREE(identifier);
1297 CHECK_AND_FREE(version);
1298 CHECK_AND_FREE(format);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001299 return reply;
1300}
1301
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001302json_object *handle_op_editconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001303{
1304 NC_DATASTORE ds_type_s = -1;
1305 NC_DATASTORE ds_type_t = -1;
1306 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
1307 NC_EDIT_ERROPT_TYPE erropt_type = 0;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001308 NC_EDIT_TESTOPT_TYPE testopt_type = NC_EDIT_TESTOPT_TESTSET;
Tomas Cejka09629492014-07-10 15:58:06 +02001309 char *defop = NULL;
1310 char *erropt = NULL;
1311 char *config = NULL;
1312 char *source = NULL;
1313 char *target = NULL;
1314 char *testopt = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001315 json_object *reply = NULL;
1316
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001317 DEBUG("Request: edit-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001318
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001319 pthread_mutex_lock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001320 /* get parameters */
Tomas Cejka09629492014-07-10 15:58:06 +02001321 defop = get_param_string(request, "default-operation");
1322 erropt = get_param_string(request, "error-option");
1323 target = get_param_string(request, "target");
1324 source = get_param_string(request, "source");
1325 config = get_param_string(request, "config");
1326 testopt = get_param_string(request, "test-option");
1327 pthread_mutex_unlock(&json_lock);
1328
1329 if (target != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001330 ds_type_t = parse_datastore(target);
1331 }
Tomas Cejka09629492014-07-10 15:58:06 +02001332 if (source != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001333 ds_type_s = parse_datastore(source);
1334 } else {
1335 /* source is optional, default value is config */
1336 ds_type_s = NC_DATASTORE_CONFIG;
1337 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001338
Tomas Cejkad5b53772013-06-08 23:01:07 +02001339 if (defop != NULL) {
1340 if (strcmp(defop, "merge") == 0) {
1341 defop_type = NC_EDIT_DEFOP_MERGE;
1342 } else if (strcmp(defop, "replace") == 0) {
1343 defop_type = NC_EDIT_DEFOP_REPLACE;
1344 } else if (strcmp(defop, "none") == 0) {
1345 defop_type = NC_EDIT_DEFOP_NONE;
1346 } else {
Tomas Cejka09629492014-07-10 15:58:06 +02001347 reply = create_error("Invalid default-operation parameter.");
1348 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001349 }
1350 } else {
1351 defop_type = NC_EDIT_DEFOP_NOTSET;
1352 }
1353
Tomas Cejkad5b53772013-06-08 23:01:07 +02001354 if (erropt != NULL) {
1355 if (strcmp(erropt, "continue-on-error") == 0) {
1356 erropt_type = NC_EDIT_ERROPT_CONT;
1357 } else if (strcmp(erropt, "stop-on-error") == 0) {
1358 erropt_type = NC_EDIT_ERROPT_STOP;
1359 } else if (strcmp(erropt, "rollback-on-error") == 0) {
1360 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
1361 } else {
Tomas Cejka09629492014-07-10 15:58:06 +02001362 reply = create_error("Invalid error-option parameter.");
1363 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001364 }
1365 } else {
1366 erropt_type = 0;
1367 }
1368
Tomas Cejkad5b53772013-06-08 23:01:07 +02001369 if (ds_type_t == -1) {
Tomas Cejka09629492014-07-10 15:58:06 +02001370 reply = create_error("Invalid target repository type requested.");
1371 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001372 }
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001373 if (ds_type_s == NC_DATASTORE_CONFIG) {
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001374 if (config == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001375 reply = create_error("Invalid config data parameter.");
1376 goto finalize;
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001377 }
1378 } else if (ds_type_s == NC_DATASTORE_URL){
Tomas Cejka8f3031e2014-02-14 23:15:08 +01001379 if (config == NULL) {
1380 config = "";
1381 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001382 }
1383
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01001384 if (testopt != NULL) {
1385 testopt_type = parse_testopt(testopt);
1386 } else {
1387 testopt_type = NC_EDIT_TESTOPT_TESTSET;
1388 }
1389
1390 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 +01001391
1392 CHECK_ERR_SET_REPLY
Tomas Cejka09629492014-07-10 15:58:06 +02001393finalize:
1394 CHECK_AND_FREE(defop);
1395 CHECK_AND_FREE(erropt);
1396 CHECK_AND_FREE(config);
1397 CHECK_AND_FREE(source);
1398 CHECK_AND_FREE(target);
1399 CHECK_AND_FREE(testopt);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001400 return reply;
1401}
1402
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001403json_object *handle_op_copyconfig(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001404{
1405 NC_DATASTORE ds_type_s = -1;
1406 NC_DATASTORE ds_type_t = -1;
Tomas Cejka09629492014-07-10 15:58:06 +02001407 char *config = NULL;
1408 char *target = NULL;
1409 char *source = NULL;
1410 char *uri_src = NULL;
1411 char *uri_trg = NULL;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001412
Tomas Cejkad5b53772013-06-08 23:01:07 +02001413 json_object *reply = NULL;
1414
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001415 DEBUG("Request: copy-config (session %s)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001416
1417 /* get parameters */
Tomas Cejka09629492014-07-10 15:58:06 +02001418 pthread_mutex_lock(&json_lock);
1419 target = get_param_string(request, "target");
1420 source = get_param_string(request, "source");
1421 config = get_param_string(request, "config");
1422 uri_src = get_param_string(request, "uri-source");
1423 uri_trg = get_param_string(request, "uri-target");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001424 pthread_mutex_unlock(&json_lock);
1425
Tomas Cejka09629492014-07-10 15:58:06 +02001426 if (target != NULL) {
1427 ds_type_t = parse_datastore(target);
1428 }
1429 if (source != NULL) {
1430 ds_type_s = parse_datastore(source);
1431 } else {
1432 /* source == NULL *//* no explicit source specified -> use config data */
Tomas Cejkad5b53772013-06-08 23:01:07 +02001433 ds_type_s = NC_DATASTORE_CONFIG;
Tomas Cejka09629492014-07-10 15:58:06 +02001434 }
1435 if (ds_type_s == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001436 /* source datastore specified, but it is invalid */
Tomas Cejka09629492014-07-10 15:58:06 +02001437 reply = create_error("Invalid source repository type requested.");
1438 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001439 }
1440
1441 if (ds_type_t == -1) {
1442 /* invalid target datastore specified */
Tomas Cejka09629492014-07-10 15:58:06 +02001443 reply = create_error("Invalid target repository type requested.");
1444 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001445 }
1446
Tomas Cejka55c6df52014-02-20 12:59:33 +01001447 /* source can be missing when config is given */
1448 if (source == NULL && config == NULL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001449 reply = create_error("invalid input parameters - source and config is required.");
Tomas Cejka09629492014-07-10 15:58:06 +02001450 goto finalize;
Tomas Cejkab4d05872014-02-14 22:44:38 +01001451 }
1452
1453 if (ds_type_s == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001454 if (uri_src == NULL) {
1455 uri_src = "";
1456 }
1457 }
1458 if (ds_type_t == NC_DATASTORE_URL) {
Tomas Cejkab4d05872014-02-14 22:44:38 +01001459 if (uri_trg == NULL) {
1460 uri_trg = "";
1461 }
1462 }
1463 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001464
1465 CHECK_ERR_SET_REPLY
1466
Tomas Cejka09629492014-07-10 15:58:06 +02001467finalize:
1468 CHECK_AND_FREE(config);
1469 CHECK_AND_FREE(target);
1470 CHECK_AND_FREE(source);
1471 CHECK_AND_FREE(uri_src);
1472 CHECK_AND_FREE(uri_trg);
1473
Tomas Cejkad5b53772013-06-08 23:01:07 +02001474 return reply;
1475}
1476
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001477json_object *handle_op_generic(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001478{
1479 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001480 char *config = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001481 char *data = NULL;
1482
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001483 DEBUG("Request: generic request for session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001484
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001485 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001486 config = get_param_string(request, "content");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001487 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001488
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001489 reply = netconf_generic(session_key, config, &data);
1490 if (reply == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001491 GETSPEC_ERR_REPLY
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001492 if (err_reply != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001493 /* use filled err_reply from libnetconf's callback */
1494 reply = err_reply;
1495 }
1496 } else {
1497 if (data == NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001498 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001499 reply = json_object_new_object();
1500 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001501 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001502 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001503 reply = create_data(data);
1504 free(data);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001505 }
1506 }
Tomas Cejka09629492014-07-10 15:58:06 +02001507 CHECK_AND_FREE(config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001508 return reply;
1509}
1510
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001511json_object *handle_op_disconnect(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001512{
1513 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001514 DEBUG("Request: Disconnect session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001515
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001516 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001517 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
Tomas Cejkad5b53772013-06-08 23:01:07 +02001518 } else {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001519 reply = create_ok();
Tomas Cejkad5b53772013-06-08 23:01:07 +02001520 }
1521 return reply;
1522}
1523
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001524json_object *handle_op_kill(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001525{
1526 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001527 char *sid = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001528
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001529 DEBUG("Request: kill-session, session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001530
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001531 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001532 sid = get_param_string(request, "session-id");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001533 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001534
1535 if (sid == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001536 reply = create_error("Missing session-id parameter.");
1537 goto finalize;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001538 }
1539
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001540 reply = netconf_killsession(session_key, sid);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001541
1542 CHECK_ERR_SET_REPLY
1543
Tomas Cejka09629492014-07-10 15:58:06 +02001544finalize:
1545 CHECK_AND_FREE(sid);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001546 return reply;
1547}
1548
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001549json_object *handle_op_reloadhello(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001550{
Tomas Cejka47387fd2013-06-10 20:37:46 +02001551 struct nc_session *temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001552 struct session_with_mutex * locked_session = NULL;
1553 json_object *reply = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001554 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001555
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001556 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001557 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001558 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001559 return NULL;
1560 }
1561
Tomas Cejka09629492014-07-10 15:58:06 +02001562 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 +02001563 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001564 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001565 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001566 DEBUG("creating temporal NC session.");
Tomas Cejka47387fd2013-06-10 20:37:46 +02001567 temp_session = nc_session_connect_channel(locked_session->session, NULL);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001568 if (temp_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001569 prepare_status_message(locked_session, temp_session);
1570 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001571 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001572 temp_session = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001573 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001574 DEBUG("Reload hello failed due to channel establishment");
Tomas Cejkad5b53772013-06-08 23:01:07 +02001575 reply = create_error("Reload was unsuccessful, connection failed.");
1576 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001577 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001578 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejka93b0e492014-03-26 15:47:45 +01001579 DEBUG("UNLOCK wrlock %s", __func__);
1580 if (pthread_rwlock_unlock(&session_lock) != 0) {
1581 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1582 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001583 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001584 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001585 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001586 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001587 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001588 reply = create_error("Invalid session identifier.");
1589 }
1590
1591 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
1592 reply = locked_session->hello_message;
1593 }
1594 return reply;
1595}
1596
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001597json_object *handle_op_info(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02001598{
1599 json_object *reply = NULL;
Tomas Cejka47387fd2013-06-10 20:37:46 +02001600 struct session_with_mutex * locked_session = NULL;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001601 DEBUG("Request: get info about session %s", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001602
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001603 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001604 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001605 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001606 }
1607
Tomas Cejka09629492014-07-10 15:58:06 +02001608 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 +02001609 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001610 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001611 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001612 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001613 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001614 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001615 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001616 if (locked_session->hello_message != NULL) {
1617 reply = locked_session->hello_message;
1618 } else {
1619 reply = create_error("Invalid session identifier.");
1620 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001621 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001622 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02001623 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001624 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001625 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001626 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka47387fd2013-06-10 20:37:46 +02001627 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001628 reply = create_error("Invalid session identifier.");
1629 }
1630
Tomas Cejka47387fd2013-06-10 20:37:46 +02001631
Tomas Cejkad5b53772013-06-08 23:01:07 +02001632 return reply;
1633}
1634
Tomas Cejka6b886e02013-07-05 09:53:17 +02001635void notification_history(time_t eventtime, const char *content)
1636{
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001637 json_object *notif_history_array = (json_object *) pthread_getspecific(notif_history_key);
1638 if (notif_history_array == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001639 DEBUG("No list of notification history found.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001640 return;
1641 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001642 DEBUG("Got notification from history %lu.", (long unsigned) eventtime);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001643 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001644 json_object *notif = json_object_new_object();
1645 if (notif == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001646 DEBUG("Could not allocate memory for notification (json).");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001647 goto failed;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001648 }
1649 json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
1650 json_object_object_add(notif, "content", json_object_new_string(content));
1651 json_object_array_add(notif_history_array, notif);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001652failed:
1653 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001654}
1655
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001656json_object *handle_op_ntfgethistory(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001657{
1658 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001659 json_object *js_tmp = NULL;
1660 char *sid = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001661 struct session_with_mutex *locked_session = NULL;
1662 struct nc_session *temp_session = NULL;
1663 nc_rpc *rpc = NULL;
1664 time_t start = 0;
1665 time_t stop = 0;
Tomas Cejka09629492014-07-10 15:58:06 +02001666 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001667
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001668 DEBUG("Request: get notification history, session %s", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001669
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001670 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001671 sid = get_param_string(request, "session");
1672
1673 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
1674 from = json_object_get_int64(js_tmp);
1675 json_object_put(js_tmp);
1676 }
1677 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
1678 to = json_object_get_int64(js_tmp);
1679 json_object_put(js_tmp);
1680 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001681 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001682
1683 start = time(NULL) + from;
1684 stop = time(NULL) + to;
1685
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001686 DEBUG("notification history interval %li %li", (long int) from, (long int) to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001687
1688 if (sid == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02001689 reply = create_error("Missing session parameter.");
1690 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001691 }
1692
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001693 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001694 if (pthread_rwlock_rdlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001695 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka09629492014-07-10 15:58:06 +02001696 reply = create_error("Internal lock failed.");
1697 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001698 }
1699
1700 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1701 if (locked_session != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001702 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001703 pthread_mutex_lock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001704 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001705 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001706 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001707 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001708 DEBUG("creating temporal NC session.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001709 temp_session = nc_session_connect_channel(locked_session->session, NULL);
1710 if (temp_session != NULL) {
1711 rpc = nc_rpc_subscribe(NULL /* stream */, NULL /* filter */, &start, &stop);
1712 if (rpc == NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001713 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001714 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001715 DEBUG("notifications: creating an rpc request failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001716 reply = create_error("notifications: creating an rpc request failed.");
1717 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001718 }
1719
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001720 DEBUG("Send NC subscribe.");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001721 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001722 json_object *res = netconf_unlocked_op(temp_session, rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001723 if (res != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001724 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001725 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001726 DEBUG("Subscription RPC failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001727 reply = res;
1728 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001729 }
1730 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
1731
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001732 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001733 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001734 DEBUG("LOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001735 pthread_mutex_lock(&ntf_history_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001736 pthread_mutex_lock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001737 json_object *notif_history_array = json_object_new_array();
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001738 pthread_mutex_unlock(&json_lock);
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001739 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001740 DEBUG("notif_history: cannot set thread-specific hash value.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02001741 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001742
1743 ncntf_dispatch_receive(temp_session, notification_history);
1744
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001745 pthread_mutex_lock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001746 reply = json_object_new_object();
1747 json_object_object_add(reply, "notifications", notif_history_array);
1748 //json_object_put(notif_history_array);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001749 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001750
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001751 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001752 pthread_mutex_unlock(&ntf_history_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001753 DEBUG("closing temporal NC session.");
Tomas Cejkaaecc5f72013-10-01 00:03:50 +02001754 nc_session_free(temp_session);
Tomas Cejka73496bf2014-03-26 15:31:09 +01001755 temp_session = NULL;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001756 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001757 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkac7929632013-10-24 19:25:15 +02001758 pthread_mutex_unlock(&locked_session->lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001759 DEBUG("Get history of notification failed due to channel establishment");
Tomas Cejka6b886e02013-07-05 09:53:17 +02001760 reply = create_error("Get history of notification was unsuccessful, connection failed.");
1761 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001762 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001763 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001764 if (pthread_rwlock_unlock(&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001765 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejka6b886e02013-07-05 09:53:17 +02001766 }
1767 reply = create_error("Invalid session identifier.");
1768 }
1769
Tomas Cejka09629492014-07-10 15:58:06 +02001770finalize:
1771 CHECK_AND_FREE(sid);
Tomas Cejka4003a702013-10-01 00:02:45 +02001772 return reply;
1773}
1774
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001775json_object *handle_op_validate(apr_pool_t *pool, json_object *request, const char *session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02001776{
1777 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001778 char *sid = NULL;
1779 char *target = NULL;
1780 char *url = NULL;
Tomas Cejka4003a702013-10-01 00:02:45 +02001781 nc_rpc *rpc = NULL;
1782 NC_DATASTORE target_ds;
1783
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001784 DEBUG("Request: validate datastore, session %s", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02001785
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001786 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001787 sid = get_param_string(request, "session");
1788 target = get_param_string(request, "target");
1789 url = get_param_string(request, "url");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001790 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02001791
1792
1793 if ((sid == NULL) || (target == NULL)) {
Tomas Cejka09629492014-07-10 15:58:06 +02001794 reply = create_error("Missing session parameter.");
1795 goto finalize;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001796 }
Tomas Cejka4003a702013-10-01 00:02:45 +02001797
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001798 /* validation */
1799 target_ds = parse_datastore(target);
1800 if (target_ds == NC_DATASTORE_URL) {
1801 if (url != NULL) {
1802 rpc = nc_rpc_validate(target_ds, url);
Tomas Cejka4003a702013-10-01 00:02:45 +02001803 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001804 } else if ((target_ds == NC_DATASTORE_RUNNING) || (target_ds == NC_DATASTORE_STARTUP)
Tomas Cejka4003a702013-10-01 00:02:45 +02001805 || (target_ds == NC_DATASTORE_CANDIDATE)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001806 rpc = nc_rpc_validate(target_ds);
1807 }
1808 if (rpc == NULL) {
1809 DEBUG("mod_netconf: creating rpc request failed");
1810 reply = create_error("Creation of RPC request failed.");
Tomas Cejka09629492014-07-10 15:58:06 +02001811 goto finalize;
Tomas Cejka4003a702013-10-01 00:02:45 +02001812 }
1813
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001814 DEBUG("Request: validate datastore");
1815 if ((reply = netconf_op(session_key, rpc, NULL)) == NULL) {
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001816
1817 CHECK_ERR_SET_REPLY
1818
1819 if (reply == NULL) {
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001820 DEBUG("Request: validation ok.");
1821 reply = create_ok();
Tomas Cejka4ad470b2014-03-20 15:30:50 +01001822 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001823 }
1824 nc_rpc_free (rpc);
Tomas Cejka09629492014-07-10 15:58:06 +02001825finalize:
1826 CHECK_AND_FREE(sid);
1827 CHECK_AND_FREE(target);
1828 CHECK_AND_FREE(url);
Tomas Cejka6b886e02013-07-05 09:53:17 +02001829 return reply;
1830}
1831
David Kupka8e60a372012-09-04 09:15:20 +02001832void * thread_routine (void * arg)
1833{
1834 void * retval = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001835 struct pollfd fds;
Tomas Cejka00635972013-06-03 15:10:52 +02001836 json_object *request = NULL;
1837 json_object *reply = NULL;
Tomas Cejka09629492014-07-10 15:58:06 +02001838 json_object *js_tmp = NULL;
1839 int operation = (-1);
1840 NC_DATASTORE ds_type_t = -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001841 int status = 0;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001842 const char *msgtext;
Tomas Cejka09629492014-07-10 15:58:06 +02001843 char *session_key = NULL;
1844 char *target = NULL;
1845 char *url = NULL;
Tomas Cejka64b87482013-06-03 16:30:53 +02001846 char *chunked_out_msg = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001847 apr_pool_t * pool = ((struct pass_to_thread*)arg)->pool;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001848 //server_rec * server = ((struct pass_to_thread*)arg)->server;
David Kupka8e60a372012-09-04 09:15:20 +02001849 int client = ((struct pass_to_thread*)arg)->client;
1850
Tomas Cejka00635972013-06-03 15:10:52 +02001851 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001852
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01001853 /* init thread specific err_reply memory */
Tomas Cejka442258e2014-04-01 18:17:18 +02001854 create_err_reply_p();
1855
David Kupka8e60a372012-09-04 09:15:20 +02001856 while (!isterminated) {
1857 fds.fd = client;
1858 fds.events = POLLIN;
1859 fds.revents = 0;
1860
1861 status = poll(&fds, 1, 1000);
1862
1863 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
1864 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001865 //DEBUG("poll interrupted");
David Kupka8e60a372012-09-04 09:15:20 +02001866 continue;
1867 } else if (status < 0) {
1868 /* 0: poll time outed
1869 * close socket and ignore this request from the client, it can try it again
1870 * -1: poll failed
1871 * something wrong happend, close this socket and wait for another request
1872 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001873 //DEBUG("poll failed, status %d(%d: %s)", status, errno, strerror(errno));
David Kupka8e60a372012-09-04 09:15:20 +02001874 close(client);
1875 break;
1876 }
1877 /* status > 0 */
1878
1879 /* check the status of the socket */
1880
1881 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
1882 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
1883 /* close client's socket (it's probably already closed by client */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001884 //DEBUG("socket error (%d)", fds.revents);
David Kupka8e60a372012-09-04 09:15:20 +02001885 close(client);
1886 break;
1887 }
1888
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001889 DEBUG("Get framed message...");
1890 buffer = get_framed_message(client);
David Kupka8e60a372012-09-04 09:15:20 +02001891
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001892 DEBUG("Check read buffer.");
David Kupka8e60a372012-09-04 09:15:20 +02001893 if (buffer != NULL) {
Tomas Cejka00635972013-06-03 15:10:52 +02001894 enum json_tokener_error jerr;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001895 pthread_mutex_lock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001896 request = json_tokener_parse_verbose(buffer, &jerr);
1897 if (jerr != json_tokener_success) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001898 DEBUG("JSON parsing error");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001899 pthread_mutex_unlock(&json_lock);
Tomas Cejka00635972013-06-03 15:10:52 +02001900 continue;
1901 }
David Kupka8e60a372012-09-04 09:15:20 +02001902
Tomas Cejka09629492014-07-10 15:58:06 +02001903 session_key = get_param_string(request, "session");
1904 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
1905 operation = json_object_get_int(js_tmp);
1906 json_object_put(js_tmp);
1907 js_tmp = NULL;
1908 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001909 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001910 if (operation == -1) {
1911 reply = create_error("Missing operation type form frontend.");
1912 goto send_reply;
1913 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001914
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001915 DEBUG("operation %d session_key %s.", operation, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001916 /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
1917 if (operation != MSG_CONNECT && session_key == NULL) {
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001918 reply = create_error("Missing session specification.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001919 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001920 msgtext = json_object_to_json_string(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001921 pthread_mutex_unlock(&json_lock);
1922
David Kupka8e60a372012-09-04 09:15:20 +02001923 send(client, msgtext, strlen(msgtext) + 1, 0);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001924
1925 pthread_mutex_lock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001926 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001927 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02001928 /* there is some stupid client, so close the connection to give a chance to some other client */
1929 close(client);
1930 break;
1931 }
1932
David Kupka8e60a372012-09-04 09:15:20 +02001933 /* null global JSON error-reply */
Tomas Cejka442258e2014-04-01 18:17:18 +02001934 clean_err_reply();
David Kupka8e60a372012-09-04 09:15:20 +02001935
1936 /* prepare reply envelope */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001937 if (reply != NULL) {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001938 pthread_mutex_lock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001939 json_object_put(reply);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001940 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001941 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02001942 reply = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02001943
1944 /* process required operation */
1945 switch (operation) {
1946 case MSG_CONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001947 reply = handle_op_connect(pool, request);
David Kupka8e60a372012-09-04 09:15:20 +02001948 break;
1949 case MSG_GET:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001950 reply = handle_op_get(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001951 break;
1952 case MSG_GETCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001953 reply = handle_op_getconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001954 break;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001955 case MSG_GETSCHEMA:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001956 reply = handle_op_getschema(pool, request, session_key);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001957 break;
David Kupka8e60a372012-09-04 09:15:20 +02001958 case MSG_EDITCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001959 reply = handle_op_editconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001960 break;
1961 case MSG_COPYCONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001962 reply = handle_op_copyconfig(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001963 break;
Tomas Cejkad5b53772013-06-08 23:01:07 +02001964
David Kupka8e60a372012-09-04 09:15:20 +02001965 case MSG_DELETECONFIG:
David Kupka8e60a372012-09-04 09:15:20 +02001966 case MSG_LOCK:
David Kupka8e60a372012-09-04 09:15:20 +02001967 case MSG_UNLOCK:
Tomas Cejkad5b53772013-06-08 23:01:07 +02001968 /* get parameters */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001969 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001970 target = get_param_string(request, "target");
1971 pthread_mutex_unlock(&json_lock);
1972 if (target != NULL) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001973 ds_type_t = parse_datastore(target);
1974 }
David Kupka8e60a372012-09-04 09:15:20 +02001975
1976 if (ds_type_t == -1) {
Tomas Cejkad5b53772013-06-08 23:01:07 +02001977 reply = create_error("Invalid target repository type requested.");
David Kupka8e60a372012-09-04 09:15:20 +02001978 break;
1979 }
David Kupka8e60a372012-09-04 09:15:20 +02001980 switch(operation) {
1981 case MSG_DELETECONFIG:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001982 DEBUG("Request: delete-config (session %s)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001983 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02001984 url = get_param_string(request, "url");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01001985 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001986 reply = netconf_deleteconfig(session_key, ds_type_t, url);
David Kupka8e60a372012-09-04 09:15:20 +02001987 break;
1988 case MSG_LOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001989 DEBUG("Request: lock (session %s)", session_key);
1990 reply = netconf_lock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001991 break;
1992 case MSG_UNLOCK:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01001993 DEBUG("Request: unlock (session %s)", session_key);
1994 reply = netconf_unlock(session_key, ds_type_t);
David Kupka8e60a372012-09-04 09:15:20 +02001995 break;
1996 default:
Tomas Cejkac7929632013-10-24 19:25:15 +02001997 reply = create_error("Internal: Unknown request type.");
David Kupka8e60a372012-09-04 09:15:20 +02001998 break;
1999 }
2000
Tomas Cejka442258e2014-04-01 18:17:18 +02002001 CHECK_ERR_SET_REPLY
Tomas Cejkac7929632013-10-24 19:25:15 +02002002 if (reply == NULL) {
Tomas Cejka09629492014-07-10 15:58:06 +02002003 reply = create_ok();
David Kupka8e60a372012-09-04 09:15:20 +02002004 }
2005 break;
2006 case MSG_KILL:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002007 reply = handle_op_kill(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002008 break;
2009 case MSG_DISCONNECT:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002010 reply = handle_op_disconnect(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002011 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02002012 case MSG_RELOADHELLO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002013 reply = handle_op_reloadhello(pool, request, session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002014 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02002015 case MSG_INFO:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002016 reply = handle_op_info(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002017 break;
2018 case MSG_GENERIC:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002019 reply = handle_op_generic(pool, request, session_key);
David Kupka8e60a372012-09-04 09:15:20 +02002020 break;
Tomas Cejka6b886e02013-07-05 09:53:17 +02002021 case MSG_NTF_GETHISTORY:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002022 reply = handle_op_ntfgethistory(pool, request, session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02002023 break;
Tomas Cejka4003a702013-10-01 00:02:45 +02002024 case MSG_VALIDATE:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002025 reply = handle_op_validate(pool, request, session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02002026 break;
David Kupka8e60a372012-09-04 09:15:20 +02002027 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002028 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002029 reply = create_error("Operation not supported.");
David Kupka8e60a372012-09-04 09:15:20 +02002030 break;
2031 }
Tomas Cejka09629492014-07-10 15:58:06 +02002032 /* free parameters */
2033 CHECK_AND_FREE(session_key);
2034 CHECK_AND_FREE(url);
2035 CHECK_AND_FREE(target);
2036 request = NULL;
2037 operation = (-1);
2038 ds_type_t = (-1);
2039
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002040 DEBUG("Clean request json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002041 pthread_mutex_lock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002042 if (request != NULL) {
2043 json_object_put(request);
2044 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002045 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02002046
2047
2048send_reply:
David Kupka1e3e4c82012-09-04 09:32:15 +02002049 /* send reply to caller */
2050 if (reply != NULL) {
2051 msgtext = json_object_to_json_string(reply);
Tomas Cejka64b87482013-06-03 16:30:53 +02002052 if (asprintf (&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
Tomas Cejka00635972013-06-03 15:10:52 +02002053 if (buffer != NULL) {
2054 free(buffer);
2055 buffer = NULL;
2056 }
David Kupka8e60a372012-09-04 09:15:20 +02002057 break;
2058 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002059 pthread_mutex_unlock(&json_lock);
2060
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002061 DEBUG("Send framed reply json object.");
Tomas Cejka64b87482013-06-03 16:30:53 +02002062 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002063 DEBUG("Clean reply json object.");
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002064 pthread_mutex_lock(&json_lock);
David Kupka1e3e4c82012-09-04 09:32:15 +02002065 json_object_put(reply);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002066 reply = NULL;
2067 DEBUG("Clean message buffer.");
Tomas Cejka09629492014-07-10 15:58:06 +02002068 CHECK_AND_FREE(chunked_out_msg);
Tomas Cejka64b87482013-06-03 16:30:53 +02002069 chunked_out_msg = NULL;
Tomas Cejka00635972013-06-03 15:10:52 +02002070 if (buffer != NULL) {
2071 free(buffer);
2072 buffer = NULL;
2073 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002074 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02002075 clean_err_reply();
David Kupka1e3e4c82012-09-04 09:32:15 +02002076 } else {
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002077 pthread_mutex_unlock(&json_lock);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002078 DEBUG("Reply is NULL, shouldn't be...");
2079 continue;
David Kupka8e60a372012-09-04 09:15:20 +02002080 }
2081 }
2082 }
David Kupka8e60a372012-09-04 09:15:20 +02002083 free (arg);
2084
Tomas Cejka442258e2014-04-01 18:17:18 +02002085 free_err_reply();
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002086
David Kupka8e60a372012-09-04 09:15:20 +02002087 return retval;
2088}
2089
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002090/**
2091 * \brief Close all open NETCONF sessions.
2092 *
2093 * During termination of mod_netconf, it is useful to close all remaining
2094 * sessions. This function iterates over the list of sessions and close them
2095 * all.
2096 *
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002097 * \param[in] p apr pool needed for hash table iterating
2098 * \param[in] ht hash table of session_with_mutex structs
2099 */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002100static void close_all_nc_sessions(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002101{
2102 apr_hash_index_t *hi;
2103 void *val = NULL;
2104 struct session_with_mutex *swm = NULL;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002105 const char *hashed_key = NULL;
2106 apr_ssize_t hashed_key_length;
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002107 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002108
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002109 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002110 DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002111 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002112 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002113 return;
2114 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002115 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002116 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002117 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002118 swm = (struct session_with_mutex *) val;
2119 if (swm != NULL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002120 DEBUG("LOCK mutex %s", __func__);
2121 pthread_mutex_lock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002122 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002123 DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002124 pthread_mutex_unlock(&swm->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002125
2126 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002127 close_and_free_session(swm);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002128 }
2129 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002130 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002131 DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002132 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002133 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002134 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002135}
2136
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002137static void check_timeout_and_close(apr_pool_t *p)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002138{
2139 apr_hash_index_t *hi;
2140 void *val = NULL;
2141 struct nc_session *ns = NULL;
2142 struct session_with_mutex *swm = NULL;
2143 const char *hashed_key = NULL;
2144 apr_ssize_t hashed_key_length;
2145 apr_time_t current_time = apr_time_now();
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002146 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002147
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002148 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002149//DEBUG("LOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002150 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002151 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002152 return;
2153 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002154 for (hi = apr_hash_first(p, netconf_sessions_list); hi; hi = apr_hash_next(hi)) {
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002155 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
2156 swm = (struct session_with_mutex *) val;
2157 if (swm == NULL) {
2158 continue;
2159 }
2160 ns = swm->session;
2161 if (ns == NULL) {
2162 continue;
2163 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002164//DEBUG("LOCK mutex %s", __func__);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002165 pthread_mutex_lock(&swm->lock);
2166 if ((current_time - swm->last_activity) > apr_time_from_sec(ACTIVITY_TIMEOUT)) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002167 DEBUG("Closing NETCONF session (%s).", hashed_key);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002168 /* remove session from the active sessions list */
Tomas Cejka47387fd2013-06-10 20:37:46 +02002169 apr_hash_set(netconf_sessions_list, hashed_key, APR_HASH_KEY_STRING, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002170//DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002171 pthread_mutex_unlock(&swm->lock);
2172
2173 /* close_and_free_session handles locking on its own */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002174 close_and_free_session(swm);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002175 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002176//DEBUG("UNLOCK mutex %s", __func__);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002177 pthread_mutex_unlock(&swm->lock);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002178 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002179 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002180 /* get exclusive access to sessions_list (conns) */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002181//DEBUG("UNLOCK wrlock %s", __func__);
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002182 if (pthread_rwlock_unlock (&session_lock) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002183 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Tomas Cejkabdedcd32013-06-09 11:54:53 +02002184 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002185}
2186
2187
2188/**
Radek Krejcif23850c2012-07-23 16:14:17 +02002189 * This is actually implementation of NETCONF client
2190 * - requests are received from UNIX socket in the predefined format
2191 * - results are replied through the same way
2192 * - the daemon run as a separate process, but it is started and stopped
2193 * automatically by Apache.
2194 *
2195 */
Radek Krejci469aab82012-07-22 18:42:20 +02002196static void forked_proc(apr_pool_t * pool, server_rec * server)
2197{
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002198 struct timeval tv;
Radek Krejci469aab82012-07-22 18:42:20 +02002199 struct sockaddr_un local, remote;
David Kupka8e60a372012-09-04 09:15:20 +02002200 int lsock, client, ret, i, pthread_count = 0;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002201 unsigned int olds = 0, timediff = 0;
David Kupka8e60a372012-09-04 09:15:20 +02002202 socklen_t len;
Radek Krejciae021c12012-07-25 18:03:52 +02002203 mod_netconf_cfg *cfg;
David Kupka8e60a372012-09-04 09:15:20 +02002204 struct pass_to_thread * arg;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002205 pthread_t * ptids = calloc(1, sizeof(pthread_t));
David Kupka8e60a372012-09-04 09:15:20 +02002206 struct timespec maxtime;
2207 pthread_rwlockattr_t lock_attrs;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002208 char *sockname = NULL;
Tomas Cejka404d37e2013-04-13 02:31:35 +02002209 #ifdef WITH_NOTIFICATIONS
2210 char use_notifications = 0;
2211 #endif
David Kupka8e60a372012-09-04 09:15:20 +02002212
Tomas Cejka6b886e02013-07-05 09:53:17 +02002213 http_server = server;
2214
Tomas Cejka404d37e2013-04-13 02:31:35 +02002215 /* wait at most 5 seconds for every thread to terminate */
David Kupka8e60a372012-09-04 09:15:20 +02002216 maxtime.tv_sec = 5;
2217 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02002218
Tomas Cejka04e08f42014-03-27 19:52:34 +01002219#ifdef HAVE_UNIXD_SETUP_CHILD
Radek Krejcif23850c2012-07-23 16:14:17 +02002220 /* change uid and gid of process for security reasons */
Radek Krejci469aab82012-07-22 18:42:20 +02002221 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01002222#else
2223# ifdef SU_GROUP
2224 if (strlen(SU_GROUP) > 0) {
2225 struct group *g = getgrnam(SU_GROUP);
2226 if (g == NULL) {
2227 DEBUG("GID (%s) was not found.", SU_GROUP);
2228 return;
2229 }
2230 if (setgid(g->gr_gid) != 0) {
2231
2232 DEBUG("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
2233 return;
2234 }
2235 }
2236# else
2237 DEBUG("no SU_GROUP");
2238# endif
2239# ifdef SU_USER
2240 if (strlen(SU_USER) > 0) {
2241 struct passwd *p = getpwnam(SU_USER);
2242 if (p == NULL) {
2243 DEBUG("UID (%s) was not found.", SU_USER);
2244 return;
2245 }
2246 if (setuid(p->pw_uid) != 0) {
2247 DEBUG("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
2248 return;
2249 }
2250 }
2251# else
2252 DEBUG("no SU_USER");
2253# endif
2254#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002255
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002256 if (server != NULL) {
2257 cfg = ap_get_module_config(server->module_config, &netconf_module);
2258 if (cfg == NULL) {
2259 DEBUG("Getting mod_netconf configuration failed");
2260 return;
2261 }
2262 sockname = cfg->sockname;
2263 } else {
2264 sockname = SOCKET_FILENAME;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002265 }
Radek Krejci469aab82012-07-22 18:42:20 +02002266
Tomas Cejka04e08f42014-03-27 19:52:34 +01002267 /* try to remove if exists */
2268 unlink(sockname);
2269
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002270 /* create listening UNIX socket to accept incoming connections */
Radek Krejci469aab82012-07-22 18:42:20 +02002271 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002272 DEBUG("Creating socket failed (%s)", strerror(errno));
2273 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002274 }
2275
2276 local.sun_family = AF_UNIX;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002277 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
Radek Krejci469aab82012-07-22 18:42:20 +02002278 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
2279
2280 if (bind(lsock, (struct sockaddr *) &local, len) == -1) {
2281 if (errno == EADDRINUSE) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002282 DEBUG("mod_netconf socket address already in use");
2283 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002284 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002285 DEBUG("Binding socket failed (%s)", strerror(errno));
2286 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002287 }
2288
2289 if (listen(lsock, MAX_SOCKET_CL) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002290 DEBUG("Setting up listen socket failed (%s)", strerror(errno));
2291 goto error_exit;
Radek Krejci469aab82012-07-22 18:42:20 +02002292 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002293 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02002294
Tomas Cejka04e08f42014-03-27 19:52:34 +01002295 uid_t user = -1;
2296 if (strlen(CHOWN_USER) > 0) {
2297 struct passwd *p = getpwnam(CHOWN_USER);
2298 if (p != NULL) {
2299 user = p->pw_uid;
2300 }
2301 }
2302 gid_t group = -1;
2303 if (strlen(CHOWN_GROUP) > 0) {
2304 struct group *g = getgrnam(CHOWN_GROUP);
2305 if (g != NULL) {
2306 group = g->gr_gid;
2307 }
2308 }
2309 if (chown(sockname, user, group) == -1) {
2310 DEBUG("Chown on socket file failed (%s).", strerror(errno));
2311 }
2312
Tomas Cejkaba21b382013-04-13 02:37:32 +02002313 /* prepare internal lists */
2314 netconf_sessions_list = apr_hash_make(pool);
2315
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002316 #ifdef WITH_NOTIFICATIONS
Tomas Cejka47387fd2013-06-10 20:37:46 +02002317 if (notification_init(pool, server) == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002318 DEBUG("libwebsockets initialization failed");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002319 use_notifications = 0;
2320 } else {
2321 use_notifications = 1;
2322 }
2323 #endif
2324
Radek Krejci469aab82012-07-22 18:42:20 +02002325 /* setup libnetconf's callbacks */
2326 nc_verbosity(NC_VERB_DEBUG);
Radek Krejci469aab82012-07-22 18:42:20 +02002327 nc_callback_print(clb_print);
2328 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
2329 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
2330 nc_callback_sshauth_password(netconf_callback_sshauth_password);
Radek Krejcic11fd862012-07-26 12:41:21 +02002331 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02002332
2333 /* disable publickey authentication */
2334 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
2335
David Kupka8e60a372012-09-04 09:15:20 +02002336 /* create mutex protecting session list */
2337 pthread_rwlockattr_init(&lock_attrs);
2338 /* rwlock is shared only with threads in this process */
2339 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
2340 /* create rw lock */
2341 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002342 DEBUG("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
2343 goto error_exit;
David Kupka8e60a372012-09-04 09:15:20 +02002344 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002345 pthread_mutex_init(&ntf_history_lock, NULL);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002346 pthread_mutex_init(&json_lock, NULL);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002347 DEBUG("init of notif_history_key.");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002348 if (pthread_key_create(&notif_history_key, NULL) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002349 DEBUG("init of notif_history_key failed");
Tomas Cejkad016f9c2013-07-10 09:16:16 +02002350 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002351 DEBUG("init of err_reply_key.");
2352 if (pthread_key_create(&err_reply_key, NULL) != 0) {
2353 DEBUG("init of err_reply_key failed");
2354 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02002355
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002356 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002357 while (isterminated == 0) {
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002358 gettimeofday(&tv, NULL);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002359 timediff = (unsigned int)tv.tv_sec - olds;
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002360 #ifdef WITH_NOTIFICATIONS
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002361 if (timediff > 60) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002362 DEBUG("handling notifications");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002363 }
2364 if (use_notifications == 1) {
2365 notification_handle();
2366 }
2367 #endif
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002368 if (timediff > ACTIVITY_CHECK_INTERVAL) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002369 check_timeout_and_close(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002370 }
Radek Krejci469aab82012-07-22 18:42:20 +02002371
2372 /* open incoming connection if any */
David Kupka8e60a372012-09-04 09:15:20 +02002373 len = sizeof(remote);
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002374 if (((unsigned int)tv.tv_sec - olds) > 60) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002375 DEBUG("accepting another client");
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002376 olds = tv.tv_sec;
2377 }
David Kupka8e60a372012-09-04 09:15:20 +02002378 client = accept(lsock, (struct sockaddr *) &remote, &len);
Radek Krejci469aab82012-07-22 18:42:20 +02002379 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
2380 apr_sleep(SLEEP_TIME);
2381 continue;
2382 } else if (client == -1 && (errno == EINTR)) {
2383 continue;
2384 } else if (client == -1) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002385 DEBUG("Accepting mod_netconf client connection failed (%s)", strerror(errno));
Radek Krejci469aab82012-07-22 18:42:20 +02002386 continue;
2387 }
Radek Krejci469aab82012-07-22 18:42:20 +02002388
2389 /* set client's socket as non-blocking */
Radek Krejcif23850c2012-07-23 16:14:17 +02002390 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02002391
David Kupka8e60a372012-09-04 09:15:20 +02002392 arg = malloc (sizeof(struct pass_to_thread));
2393 arg->client = client;
2394 arg->pool = pool;
2395 arg->server = server;
2396 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02002397
David Kupka8e60a372012-09-04 09:15:20 +02002398 /* start new thread. It will serve this particular request and then terminate */
2399 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void*)arg)) != 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002400 DEBUG("Creating POSIX thread failed: %d\n", ret);
David Kupka8e60a372012-09-04 09:15:20 +02002401 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002402 DEBUG("Thread %lu created", ptids[pthread_count]);
David Kupka8e60a372012-09-04 09:15:20 +02002403 pthread_count++;
2404 ptids = realloc (ptids, sizeof(pthread_t)*(pthread_count+1));
2405 ptids[pthread_count] = 0;
2406 }
Radek Krejci469aab82012-07-22 18:42:20 +02002407
David Kupka8e60a372012-09-04 09:15:20 +02002408 /* check if some thread already terminated, free some resources by joining it */
2409 for (i=0; i<pthread_count; i++) {
2410 if (pthread_tryjoin_np (ptids[i], (void**)&arg) == 0) {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002411 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
David Kupka8e60a372012-09-04 09:15:20 +02002412 pthread_count--;
2413 if (pthread_count > 0) {
2414 /* place last Thread ID on the place of joined one */
2415 ptids[i] = ptids[pthread_count];
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002416 }
Radek Krejci469aab82012-07-22 18:42:20 +02002417 }
2418 }
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002419 DEBUG("Running %d threads", pthread_count);
Radek Krejci469aab82012-07-22 18:42:20 +02002420 }
2421
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002422 DEBUG("mod_netconf terminating...");
David Kupka8e60a372012-09-04 09:15:20 +02002423 /* join all threads */
2424 for (i=0; i<pthread_count; i++) {
2425 pthread_timedjoin_np (ptids[i], (void**)&arg, &maxtime);
2426 }
Radek Krejci469aab82012-07-22 18:42:20 +02002427
Tomas Cejkad340dbf2013-03-24 20:36:57 +01002428 #ifdef WITH_NOTIFICATIONS
2429 notification_close();
2430 #endif
2431
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002432 /* close all NETCONF sessions */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002433 close_all_nc_sessions(pool);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02002434
David Kupka8e60a372012-09-04 09:15:20 +02002435 /* destroy rwlock */
2436 pthread_rwlock_destroy(&session_lock);
2437 pthread_rwlockattr_destroy(&lock_attrs);
2438
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002439 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02002440
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002441 free(ptids);
2442 close(lsock);
Radek Krejci469aab82012-07-22 18:42:20 +02002443 exit(APR_SUCCESS);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002444 return;
2445error_exit:
2446 close(lsock);
2447 free(ptids);
2448 return;
Radek Krejci469aab82012-07-22 18:42:20 +02002449}
2450
Radek Krejcif23850c2012-07-23 16:14:17 +02002451static void *mod_netconf_create_conf(apr_pool_t * pool, server_rec * s)
Radek Krejci469aab82012-07-22 18:42:20 +02002452{
Radek Krejcif23850c2012-07-23 16:14:17 +02002453 mod_netconf_cfg *config = apr_pcalloc(pool, sizeof(mod_netconf_cfg));
2454 apr_pool_create(&config->pool, pool);
2455 config->forkproc = NULL;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002456 config->sockname = SOCKET_FILENAME;
Radek Krejcif23850c2012-07-23 16:14:17 +02002457
2458 return (void *)config;
Radek Krejci469aab82012-07-22 18:42:20 +02002459}
2460
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002461#ifndef HTTPD_INDEPENDENT
Radek Krejci469aab82012-07-22 18:42:20 +02002462static int mod_netconf_master_init(apr_pool_t * pconf, apr_pool_t * ptemp,
2463 apr_pool_t * plog, server_rec * s)
2464{
Radek Krejcif23850c2012-07-23 16:14:17 +02002465 mod_netconf_cfg *config;
2466 apr_status_t res;
2467
Radek Krejci469aab82012-07-22 18:42:20 +02002468 /* These two help ensure that we only init once. */
Radek Krejcia332b692012-11-12 16:15:54 +01002469 void *data = NULL;
Radek Krejcif23850c2012-07-23 16:14:17 +02002470 const char *userdata_key = "netconf_ipc_init";
Radek Krejci469aab82012-07-22 18:42:20 +02002471
2472 /*
2473 * The following checks if this routine has been called before.
2474 * This is necessary because the parent process gets initialized
2475 * a couple of times as the server starts up.
2476 */
2477 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2478 if (!data) {
2479 apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
2480 return (OK);
2481 }
2482
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002483 DEBUG("creating mod_netconf daemon");
Radek Krejcif23850c2012-07-23 16:14:17 +02002484 config = ap_get_module_config(s->module_config, &netconf_module);
Radek Krejcidfaa6ea2012-07-23 09:04:43 +02002485
Radek Krejcif23850c2012-07-23 16:14:17 +02002486 if (config && config->forkproc == NULL) {
2487 config->forkproc = apr_pcalloc(config->pool, sizeof(apr_proc_t));
2488 res = apr_proc_fork(config->forkproc, config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002489 switch (res) {
2490 case APR_INCHILD:
2491 /* set signal handler */
Radek Krejcif23850c2012-07-23 16:14:17 +02002492 apr_signal_init(config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02002493 apr_signal(SIGTERM, signal_handler);
2494
2495 /* log start of the separated NETCONF communication process */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002496 DEBUG("mod_netconf daemon started (PID %d)", getpid());
Radek Krejci469aab82012-07-22 18:42:20 +02002497
2498 /* start main loop providing NETCONF communication */
Radek Krejcif23850c2012-07-23 16:14:17 +02002499 forked_proc(config->pool, s);
Radek Krejci469aab82012-07-22 18:42:20 +02002500
Radek Krejcif23850c2012-07-23 16:14:17 +02002501 /* I never should be here, wtf?!? */
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002502 DEBUG("mod_netconf daemon unexpectedly stopped");
Radek Krejci469aab82012-07-22 18:42:20 +02002503 exit(APR_EGENERAL);
2504 break;
2505 case APR_INPARENT:
2506 /* register child to be killed (SIGTERM) when the module config's pool dies */
Radek Krejcif23850c2012-07-23 16:14:17 +02002507 apr_pool_note_subprocess(config->pool, config->forkproc, APR_KILL_AFTER_TIMEOUT);
Radek Krejci469aab82012-07-22 18:42:20 +02002508 break;
2509 default:
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002510 DEBUG("apr_proc_fork() failed");
Radek Krejci469aab82012-07-22 18:42:20 +02002511 break;
2512 }
Radek Krejcif23850c2012-07-23 16:14:17 +02002513 } else {
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002514 DEBUG("mod_netconf misses configuration structure");
Radek Krejci469aab82012-07-22 18:42:20 +02002515 }
2516
2517 return OK;
2518}
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002519#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002520
Radek Krejci469aab82012-07-22 18:42:20 +02002521/**
2522 * Register module hooks
2523 */
2524static void mod_netconf_register_hooks(apr_pool_t * p)
2525{
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002526#ifndef HTTPD_INDEPENDENT
Radek Krejcif23850c2012-07-23 16:14:17 +02002527 ap_hook_post_config(mod_netconf_master_init, NULL, NULL, APR_HOOK_LAST);
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002528#endif
Radek Krejci469aab82012-07-22 18:42:20 +02002529}
2530
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002531static const char* cfg_set_socket_path(cmd_parms* cmd, void* cfg, const char* arg)
2532{
2533 ((mod_netconf_cfg*)cfg)->sockname = apr_pstrdup(cmd->pool, arg);
2534 return NULL;
2535}
2536
2537static const command_rec netconf_cmds[] = {
2538 AP_INIT_TAKE1("NetconfSocket", cfg_set_socket_path, NULL, OR_ALL, "UNIX socket path for mod_netconf communication."),
2539 {NULL}
2540};
2541
Radek Krejci469aab82012-07-22 18:42:20 +02002542/* Dispatch list for API hooks */
2543module AP_MODULE_DECLARE_DATA netconf_module = {
2544 STANDARD20_MODULE_STUFF,
2545 NULL, /* create per-dir config structures */
2546 NULL, /* merge per-dir config structures */
Radek Krejcif23850c2012-07-23 16:14:17 +02002547 mod_netconf_create_conf, /* create per-server config structures */
Radek Krejci469aab82012-07-22 18:42:20 +02002548 NULL, /* merge per-server config structures */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02002549 netconf_cmds, /* table of config file commands */
Radek Krejci469aab82012-07-22 18:42:20 +02002550 mod_netconf_register_hooks /* register hooks */
2551};
Radek Krejcia332b692012-11-12 16:15:54 +01002552
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002553int main(int argc, char **argv)
2554{
2555 apr_pool_t *pool;
2556 apr_app_initialize(&argc, (char const *const **) &argv, NULL);
2557 apr_signal(SIGTERM, signal_handler);
2558 apr_signal(SIGINT, signal_handler);
2559 apr_pool_create(&pool, NULL);
2560 forked_proc(pool, NULL);
2561 apr_pool_destroy(pool);
2562 apr_terminate();
2563 DEBUG("Terminated");
2564 return 0;
2565}