blob: e9217a64ab805ccfebb296e9c9fb98c46d3becae [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 */
Tomas Cejka689a1042013-01-16 15:08:25 +010046static const char rcsid[] __attribute__((used)) ="$Id: "__FILE__": "ARCSID" $";
Radek Krejci469aab82012-07-22 18:42:20 +020047
Radek Krejci7b4ddd02012-07-30 08:09:58 +020048#include <unistd.h>
49#include <poll.h>
Radek Krejci469aab82012-07-22 18:42:20 +020050#include <sys/types.h>
51#include <sys/socket.h>
52#include <sys/un.h>
Tomas Cejkad340dbf2013-03-24 20:36:57 +010053#include <sys/fcntl.h>
David Kupka8e60a372012-09-04 09:15:20 +020054#include <pthread.h>
55#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020056
57#include <unixd.h>
58#include <httpd.h>
59#include <http_log.h>
60#include <http_config.h>
61
62#include <apr_sha1.h>
63#include <apr_hash.h>
64#include <apr_signal.h>
65#include <apr_strings.h>
Radek Krejci469aab82012-07-22 18:42:20 +020066
Radek Krejci8fd1f5e2012-07-24 17:33:36 +020067#include <json/json.h>
68
Radek Krejci469aab82012-07-22 18:42:20 +020069#include <libnetconf.h>
Tomas Cejkab3cc64f2013-05-03 19:44:54 +020070#include <libnetconf_ssh.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020071
Tomas Cejka45ab59f2013-05-15 00:10:49 +020072struct nc_session *nc_session_connect_channel(struct nc_session *session, const struct nc_cpblts* cpblts);
73
Tomas Cejkad340dbf2013-03-24 20:36:57 +010074#ifdef WITH_NOTIFICATIONS
75#include "notification_module.h"
76#endif
77
Tomas Cejka94da2c52013-01-08 18:20:30 +010078#include "message_type.h"
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020079#include "mod_netconf.h"
Radek Krejci469aab82012-07-22 18:42:20 +020080
81#define MAX_PROCS 5
Radek Krejci6cb08982012-07-25 18:01:06 +020082#define SOCKET_FILENAME "/tmp/mod_netconf.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020083#define MAX_SOCKET_CL 10
84#define BUFFER_SIZE 4096
Tomas Cejkaaf7a1562013-04-13 02:27:43 +020085#define NOTIFICATION_QUEUE_SIZE 10
86#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
Tomas Cejka04e39952013-04-19 11:49:38 +020087#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020088
89/* sleep in master process for non-blocking socket reading */
90#define SLEEP_TIME 200
91
92#ifndef offsetof
93#define offsetof(type, member) ((size_t) ((type *) 0)->member)
94#endif
95
Tomas Cejka027f3bc2012-11-10 20:28:36 +010096/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020097struct timeval timeout = { 1, 0 };
98
Tomas Cejka5064c232013-01-17 09:30:58 +010099#define NCWITHDEFAULTS NCWD_MODE_NOTSET
100
101
Radek Krejci469aab82012-07-22 18:42:20 +0200102#define MSG_OK 0
103#define MSG_OPEN 1
104#define MSG_DATA 2
105#define MSG_CLOSE 3
106#define MSG_ERROR 4
107#define MSG_UNKNOWN 5
108
Radek Krejci469aab82012-07-22 18:42:20 +0200109module AP_MODULE_DECLARE_DATA netconf_module;
110
David Kupka8e60a372012-09-04 09:15:20 +0200111pthread_rwlock_t session_lock; /**< mutex protecting netconf_session_list from multiple access errors */
112
Radek Krejci469aab82012-07-22 18:42:20 +0200113volatile int isterminated = 0;
114
115static char* password;
116
Radek Krejci469aab82012-07-22 18:42:20 +0200117static void signal_handler(int sign)
118{
119 switch (sign) {
120 case SIGTERM:
121 isterminated = 1;
Radek Krejci469aab82012-07-22 18:42:20 +0200122 break;
123 }
124}
125
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200126static char* gen_ncsession_hash( const char* hostname, const char* port, const char* sid)
Radek Krejci469aab82012-07-22 18:42:20 +0200127{
Radek Krejcif23850c2012-07-23 16:14:17 +0200128 unsigned char hash_raw[APR_SHA1_DIGESTSIZE];
129 int i;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200130 char* hash;
Radek Krejcif23850c2012-07-23 16:14:17 +0200131
Radek Krejci469aab82012-07-22 18:42:20 +0200132 apr_sha1_ctx_t sha1_ctx;
133 apr_sha1_init(&sha1_ctx);
134 apr_sha1_update(&sha1_ctx, hostname, strlen(hostname));
135 apr_sha1_update(&sha1_ctx, port, strlen(port));
136 apr_sha1_update(&sha1_ctx, sid, strlen(sid));
Radek Krejcif23850c2012-07-23 16:14:17 +0200137 apr_sha1_final(hash_raw, &sha1_ctx);
Radek Krejci469aab82012-07-22 18:42:20 +0200138
Radek Krejcif23850c2012-07-23 16:14:17 +0200139 /* convert binary hash into hex string, which is printable */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200140 hash = malloc(sizeof(char) * ((2*APR_SHA1_DIGESTSIZE)+1));
Radek Krejcif23850c2012-07-23 16:14:17 +0200141 for (i = 0; i < APR_SHA1_DIGESTSIZE; i++) {
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200142 snprintf(hash + (2*i), 3, "%02x", hash_raw[i]);
Radek Krejcif23850c2012-07-23 16:14:17 +0200143 }
144 //hash[2*APR_SHA1_DIGESTSIZE] = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200145
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200146 return (hash);
Radek Krejci469aab82012-07-22 18:42:20 +0200147}
148
149int netconf_callback_ssh_hostkey_check (const char* hostname, int keytype, const char* fingerprint)
150{
151 /* always approve */
152 return (EXIT_SUCCESS);
153}
154
155char* netconf_callback_sshauth_password (const char* username, const char* hostname)
156{
157 char* buf;
158
159 buf = malloc ((strlen(password) + 1) * sizeof(char));
160 apr_cpystrn(buf, password, strlen(password) + 1);
161
162 return (buf);
163}
164
165void netconf_callback_sshauth_interactive (const char* name,
166 int name_len,
167 const char* instruction,
168 int instruction_len,
169 int num_prompts,
170 const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts,
171 LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses,
172 void** abstract)
173{
174 int i;
175
176 for (i=0; i<num_prompts; i++) {
177 responses[i].text = malloc ((strlen(password) + 1) * sizeof(char));
178 apr_cpystrn(responses[i].text, password, strlen(password) + 1);
179 responses[i].length = strlen(responses[i].text) + 1;
180 }
181
182 return;
183}
184
Radek Krejcic11fd862012-07-26 12:41:21 +0200185static json_object *err_reply = NULL;
186void netconf_callback_error_process(const char* tag,
187 const char* type,
188 const char* severity,
189 const char* apptag,
190 const char* path,
191 const char* message,
192 const char* attribute,
193 const char* element,
194 const char* ns,
195 const char* sid)
196{
197 err_reply = json_object_new_object();
198 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
199 if (tag) json_object_object_add(err_reply, "error-tag", json_object_new_string(tag));
200 if (type) json_object_object_add(err_reply, "error-type", json_object_new_string(type));
201 if (severity) json_object_object_add(err_reply, "error-severity", json_object_new_string(severity));
202 if (apptag) json_object_object_add(err_reply, "error-app-tag", json_object_new_string(apptag));
203 if (path) json_object_object_add(err_reply, "error-path", json_object_new_string(path));
204 if (message) json_object_object_add(err_reply, "error-message", json_object_new_string(message));
205 if (attribute) json_object_object_add(err_reply, "bad-attribute", json_object_new_string(attribute));
206 if (element) json_object_object_add(err_reply, "bad-element", json_object_new_string(element));
207 if (ns) json_object_object_add(err_reply, "bad-namespace", json_object_new_string(ns));
208 if (sid) json_object_object_add(err_reply, "session-id", json_object_new_string(sid));
209}
210
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200211void prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
212{
213 json_object *json_obj;
214 const char *cpbltstr;
215 struct nc_cpblts* cpblts = NULL;
216 if (s->hello_message != NULL) {
217 json_object_object_del(s->hello_message, NULL);
218 }
219 s->hello_message = json_object_new_object();
220 if (session != NULL) {
221 json_object_object_add(s->hello_message, "sid", json_object_new_string(nc_session_get_id(session)));
222 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
223 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
224 json_object_object_add(s->hello_message, "port", json_object_new_string(nc_session_get_port(session)));
225 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_user(session)));
226 cpblts = nc_session_get_cpblts (session);
227 if (cpblts != NULL) {
228 json_obj = json_object_new_array();
229 nc_cpblts_iter_start (cpblts);
230 while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
231 json_object_array_add(json_obj, json_object_new_string(cpbltstr));
232 }
233 json_object_object_add(s->hello_message, "capabilities", json_obj);
234 }
235 } else {
236 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
237 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
238 }
239
240}
241
242
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200243/**
244 * \defgroup netconf_operations NETCONF operations
245 * The list of NETCONF operations that mod_netconf supports.
246 * @{
247 */
248
249/**
250 * \brief Connect to NETCONF server
251 *
252 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
253 */
Kupka David00b9c5c2012-09-05 09:45:50 +0200254static char* netconf_connect(server_rec* server, apr_pool_t* pool, apr_hash_t* conns, const char* host, const char* port, const char* user, const char* pass, struct nc_cpblts * cpblts)
Radek Krejci469aab82012-07-22 18:42:20 +0200255{
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200256 struct nc_session* session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200257 struct session_with_mutex * locked_session;
Radek Krejcif23850c2012-07-23 16:14:17 +0200258 char *session_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200259
260 /* connect to the requested NETCONF server */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200261 password = (char*)pass;
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200262 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "prepare to connect %s@%s:%s", user, host, port);
263 nc_verbosity(NC_VERB_DEBUG);
David Kupka8e60a372012-09-04 09:15:20 +0200264 session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
Tomas Cejkab3cc64f2013-05-03 19:44:54 +0200265 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "nc_session_connect done (%p)", session);
David Kupka8e60a372012-09-04 09:15:20 +0200266
Radek Krejci469aab82012-07-22 18:42:20 +0200267 /* if connected successful, add session to the list */
268 if (session != NULL) {
269 /* generate hash for the session */
Radek Krejcic11fd862012-07-26 12:41:21 +0200270 session_key = gen_ncsession_hash(
271 (host==NULL) ? "localhost" : host,
272 (port==NULL) ? "830" : port,
Radek Krejcia282bed2012-07-27 14:43:45 +0200273 nc_session_get_id(session));
David Kupka8e60a372012-09-04 09:15:20 +0200274
Tomas Cejkaba21b382013-04-13 02:37:32 +0200275 /** \todo allocate from apr_pool */
David Kupka8e60a372012-09-04 09:15:20 +0200276 if ((locked_session = malloc (sizeof (struct session_with_mutex))) == NULL || pthread_mutex_init (&locked_session->lock, NULL) != 0) {
277 nc_session_free(session);
278 free (locked_session);
279 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
280 return NULL;
281 }
282 locked_session->session = session;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200283 locked_session->last_activity = apr_time_now();
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200284 locked_session->hello_message = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200285 pthread_mutex_init (&locked_session->lock, NULL);
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100286 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "Before session_lock");
David Kupka8e60a372012-09-04 09:15:20 +0200287 /* get exclusive access to sessions_list (conns) */
288 if (pthread_rwlock_wrlock (&session_lock) != 0) {
289 nc_session_free(session);
290 free (locked_session);
291 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
292 return NULL;
293 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200294 locked_session->notifications = apr_array_make(pool, NOTIFICATION_QUEUE_SIZE, sizeof(notification_t));
Tomas Cejka654f84e2013-04-19 11:55:01 +0200295 locked_session->ntfc_subscribed = 0;
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100296 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "Add connection to the list");
David Kupka8e60a372012-09-04 09:15:20 +0200297 apr_hash_set(conns, apr_pstrdup(pool, session_key), APR_HASH_KEY_STRING, (void *) locked_session);
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100298 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "Before session_unlock");
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200299
300 /* store information about session from hello message for future usage */
301 prepare_status_message(locked_session, session);
302
David Kupka8e60a372012-09-04 09:15:20 +0200303 /* end of critical section */
304 if (pthread_rwlock_unlock (&session_lock) != 0) {
305 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
306 return NULL;
307 }
Radek Krejci469aab82012-07-22 18:42:20 +0200308 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "NETCONF session established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200309 return (session_key);
Radek Krejci469aab82012-07-22 18:42:20 +0200310 } else {
311 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Connection could not be established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200312 return (NULL);
Radek Krejci469aab82012-07-22 18:42:20 +0200313 }
314
Radek Krejci469aab82012-07-22 18:42:20 +0200315}
316
Radek Krejci80c10d92012-07-30 08:38:50 +0200317static int netconf_close(server_rec* server, apr_hash_t* conns, const char* session_key)
Radek Krejci469aab82012-07-22 18:42:20 +0200318{
319 struct nc_session *ns = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200320 struct session_with_mutex * locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +0200321
Radek Krejcif23850c2012-07-23 16:14:17 +0200322 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Key in hash to get: %s", session_key);
David Kupka8e60a372012-09-04 09:15:20 +0200323 /* get exclusive (write) access to sessions_list (conns) */
324 if (pthread_rwlock_wrlock (&session_lock) != 0) {
325 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
326 return EXIT_FAILURE;
327 }
328 locked_session = (struct session_with_mutex *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
329 if (locked_session != NULL) {
Tomas Cejkaba21b382013-04-13 02:37:32 +0200330 /** \todo free all notifications from queue */
331 apr_array_clear(locked_session->notifications);
David Kupka8e60a372012-09-04 09:15:20 +0200332 pthread_mutex_destroy(&locked_session->lock);
333 ns = locked_session->session;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200334 if (locked_session->hello_message != NULL) {
335 json_object_object_del(locked_session->hello_message, NULL);
336 }
David Kupka8e60a372012-09-04 09:15:20 +0200337 free (locked_session);
338 }
Radek Krejci469aab82012-07-22 18:42:20 +0200339 if (ns != NULL) {
Tomas Cejka027f3bc2012-11-10 20:28:36 +0100340 nc_session_close (ns, NC_SESSION_TERM_CLOSED);
Radek Krejci469aab82012-07-22 18:42:20 +0200341 nc_session_free (ns);
342 ns = NULL;
343
344 /* remove session from the active sessions list */
345 apr_hash_set(conns, session_key, APR_HASH_KEY_STRING, NULL);
David Kupka8e60a372012-09-04 09:15:20 +0200346 /* end of critical section */
347 if (pthread_rwlock_unlock (&session_lock) != 0) {
348 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
349 return EXIT_FAILURE;
350 }
Radek Krejci469aab82012-07-22 18:42:20 +0200351 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "NETCONF session closed");
Radek Krejcif23850c2012-07-23 16:14:17 +0200352
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200353 return (EXIT_SUCCESS);
Radek Krejci469aab82012-07-22 18:42:20 +0200354 } else {
Tomas Cejka6c4609b2012-10-12 22:29:47 +0200355 if (pthread_rwlock_unlock (&session_lock) != 0) {
356 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
357 return EXIT_FAILURE;
358 }
Radek Krejci469aab82012-07-22 18:42:20 +0200359 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown session to close");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200360 return (EXIT_FAILURE);
Radek Krejci469aab82012-07-22 18:42:20 +0200361 }
362}
363
Radek Krejci80c10d92012-07-30 08:38:50 +0200364static int netconf_op(server_rec* server, apr_hash_t* conns, const char* session_key, nc_rpc* rpc)
Radek Krejci469aab82012-07-22 18:42:20 +0200365{
366 struct nc_session *session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200367 struct session_with_mutex * locked_session;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200368 nc_reply* reply;
369 int retval = EXIT_SUCCESS;
Radek Krejcia332b692012-11-12 16:15:54 +0100370 NC_MSG_TYPE msgt;
371 NC_REPLY_TYPE replyt;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200372
Radek Krejci8e4632a2012-07-26 13:40:34 +0200373 /* check requests */
374 if (rpc == NULL) {
375 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: rpc is not created");
376 return (EXIT_FAILURE);
377 }
378
David Kupka8e60a372012-09-04 09:15:20 +0200379 /* get non-exclusive (read) access to sessions_list (conns) */
380 if (pthread_rwlock_rdlock (&session_lock) != 0) {
381 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
382 return EXIT_FAILURE;
383 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200384 /* get session where send the RPC */
David Kupka8e60a372012-09-04 09:15:20 +0200385 locked_session = (struct session_with_mutex *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
386 if (locked_session != NULL) {
387 session = locked_session->session;
388 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200389 if (session != NULL) {
David Kupka8e60a372012-09-04 09:15:20 +0200390 /* get exclusive access to session */
391 if (pthread_mutex_lock(&locked_session->lock) != 0) {
392 /* unlock before returning error */
393 if (pthread_rwlock_unlock (&session_lock) != 0) {
394 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
395 return EXIT_FAILURE;
396 }
397 return EXIT_FAILURE;
398 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200399 locked_session->last_activity = apr_time_now();
Radek Krejci035bf4e2012-07-25 10:59:09 +0200400 /* send the request and get the reply */
Radek Krejcia332b692012-11-12 16:15:54 +0100401 msgt = nc_session_send_recv(session, rpc, &reply);
402
David Kupka8e60a372012-09-04 09:15:20 +0200403 /* first release exclusive lock for this session */
404 pthread_mutex_unlock(&locked_session->lock);
405 /* end of critical section */
406 if (pthread_rwlock_unlock (&session_lock) != 0) {
407 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
408 return EXIT_FAILURE;
409 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200410
Radek Krejcia332b692012-11-12 16:15:54 +0100411 /* process the result of the operation */
412 switch (msgt) {
413 case NC_MSG_UNKNOWN:
414 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
415 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: receiving rpc-reply failed");
416 netconf_close(server, conns, session_key);
417 return (EXIT_FAILURE);
418 }
419 /* no break */
420 case NC_MSG_NONE:
421 /* there is error handled by callback */
422 return (EXIT_FAILURE);
423 break;
424 case NC_MSG_REPLY:
425 switch (replyt = nc_reply_get_type(reply)) {
426 case NC_REPLY_OK:
427 retval = EXIT_SUCCESS;
428 break;
429 default:
430 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected rpc-reply (%d)", replyt);
431 retval = EXIT_FAILURE;
432 break;
433 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200434 break;
435 default:
Radek Krejcia332b692012-11-12 16:15:54 +0100436 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected reply message received (%d)", msgt);
Radek Krejci035bf4e2012-07-25 10:59:09 +0200437 retval = EXIT_FAILURE;
438 break;
439 }
440 nc_reply_free(reply);
441 return (retval);
442 } else {
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100443 /* release lock on failure */
444 if (pthread_rwlock_unlock (&session_lock) != 0) {
445 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
446 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200447 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown session to process.");
448 return (EXIT_FAILURE);
449 }
450}
Radek Krejci80c10d92012-07-30 08:38:50 +0200451
452static char* netconf_opdata(server_rec* server, apr_hash_t* conns, const char* session_key, nc_rpc* rpc)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200453{
454 struct nc_session *session = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200455 struct session_with_mutex * locked_session;
Radek Krejcia332b692012-11-12 16:15:54 +0100456 nc_reply* reply = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200457 char* data;
Radek Krejcia332b692012-11-12 16:15:54 +0100458 NC_MSG_TYPE msgt;
459 NC_REPLY_TYPE replyt;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200460
461 /* check requests */
462 if (rpc == NULL) {
463 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: rpc is not created");
464 return (NULL);
465 }
466
David Kupka8e60a372012-09-04 09:15:20 +0200467 /* get non-exclusive (read) access to sessions_list (conns) */
468 if (pthread_rwlock_rdlock (&session_lock) != 0) {
469 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
470 return NULL;
471 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200472 /* get session where send the RPC */
David Kupka8e60a372012-09-04 09:15:20 +0200473 locked_session = (struct session_with_mutex *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
474 if (locked_session != NULL) {
475 session = locked_session->session;
476 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200477 if (session != NULL) {
David Kupka8e60a372012-09-04 09:15:20 +0200478 /* get exclusive access to session */
479 if (pthread_mutex_lock(&locked_session->lock) != 0) {
480 /* unlock before returning error */
481 if (pthread_rwlock_unlock (&session_lock) != 0) {
482 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
483 return NULL;
484 }
485 return NULL;
486 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +0200487 locked_session->last_activity = apr_time_now();
Radek Krejci8e4632a2012-07-26 13:40:34 +0200488 /* send the request and get the reply */
Radek Krejcia332b692012-11-12 16:15:54 +0100489 msgt = nc_session_send_recv(session, rpc, &reply);
490
David Kupka8e60a372012-09-04 09:15:20 +0200491 /* first release exclusive lock for this session */
492 pthread_mutex_unlock(&locked_session->lock);
493 /* end of critical section */
494 if (pthread_rwlock_unlock (&session_lock) != 0) {
495 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
Radek Krejcia332b692012-11-12 16:15:54 +0100496 return (NULL);
David Kupka8e60a372012-09-04 09:15:20 +0200497 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200498
Radek Krejcia332b692012-11-12 16:15:54 +0100499 /* process the result of the operation */
500 switch (msgt) {
501 case NC_MSG_UNKNOWN:
502 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
503 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: receiving rpc-reply failed");
504 netconf_close(server, conns, session_key);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200505 return (NULL);
506 }
Radek Krejcia332b692012-11-12 16:15:54 +0100507 /* no break */
508 case NC_MSG_NONE:
509 /* there is error handled by callback */
510 return (NULL);
511 break;
512 case NC_MSG_REPLY:
513 switch (replyt = nc_reply_get_type(reply)) {
514 case NC_REPLY_DATA:
515 if ((data = nc_reply_get_data (reply)) == NULL) {
516 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: no data from reply");
517 data = NULL;
518 }
519 break;
520 default:
521 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected rpc-reply (%d)", replyt);
522 data = NULL;
523 break;
524 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200525 break;
526 default:
Radek Krejcia332b692012-11-12 16:15:54 +0100527 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected reply message received (%d)", msgt);
528 data = NULL;
529 break;
Radek Krejci8e4632a2012-07-26 13:40:34 +0200530 }
531 nc_reply_free(reply);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200532 return (data);
533 } else {
Tomas Cejkabcdc1142012-11-14 01:12:43 +0100534 /* release lock on failure */
535 if (pthread_rwlock_unlock (&session_lock) != 0) {
536 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
537 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200538 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown session to process.");
539 return (NULL);
540 }
541}
542
Radek Krejci80c10d92012-07-30 08:38:50 +0200543static char* netconf_getconfig(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE source, const char* filter)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200544{
545 nc_rpc* rpc;
546 struct nc_filter *f = NULL;
547 char* data = NULL;
548
549 /* create filter if set */
550 if (filter != NULL) {
551 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
552 }
553
554 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100555 rpc = nc_rpc_getconfig (source, f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200556 nc_filter_free(f);
557 if (rpc == NULL) {
558 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
559 return (NULL);
560 }
561
562 data = netconf_opdata(server, conns, session_key, rpc);
563 nc_rpc_free (rpc);
564 return (data);
565}
566
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100567static char* netconf_getschema(server_rec* server, apr_hash_t* conns, const char* session_key, const char* identifier, const char* version, const char* format)
568{
569 nc_rpc* rpc;
570 char* data = NULL;
571
572 /* create requests */
Tomas Cejka94da2c52013-01-08 18:20:30 +0100573 rpc = nc_rpc_getschema(identifier, version, format);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +0100574 if (rpc == NULL) {
575 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
576 return (NULL);
577 }
578
579 data = netconf_opdata(server, conns, session_key, rpc);
580 nc_rpc_free (rpc);
581 return (data);
582}
583
Radek Krejci80c10d92012-07-30 08:38:50 +0200584static char* netconf_get(server_rec* server, apr_hash_t* conns, const char* session_key, const char* filter)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200585{
586 nc_rpc* rpc;
587 struct nc_filter *f = NULL;
588 char* data = NULL;
589
590 /* create filter if set */
591 if (filter != NULL) {
592 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
593 }
594
595 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100596 rpc = nc_rpc_get (f);
Radek Krejci8e4632a2012-07-26 13:40:34 +0200597 nc_filter_free(f);
598 if (rpc == NULL) {
599 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
600 return (NULL);
601 }
602
603 data = netconf_opdata(server, conns, session_key, rpc);
604 nc_rpc_free (rpc);
605 return (data);
606}
607
Tomas Cejka5064c232013-01-17 09:30:58 +0100608static int netconf_copyconfig(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE source, NC_DATASTORE target, const char* config, const char *url)
Radek Krejci8e4632a2012-07-26 13:40:34 +0200609{
610 nc_rpc* rpc;
611 int retval = EXIT_SUCCESS;
612
613 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100614 if ((source == NC_DATASTORE_CONFIG) || (source == NC_DATASTORE_URL)) {
615 if (target == NC_DATASTORE_URL) {
616 rpc = nc_rpc_copyconfig(source, target, config, url);
617 } else {
618 rpc = nc_rpc_copyconfig(source, target, config);
619 }
620 } else {
621 if (target == NC_DATASTORE_URL) {
622 rpc = nc_rpc_copyconfig(source, target, url);
623 } else {
624 rpc = nc_rpc_copyconfig(source, target);
625 }
626 }
Radek Krejci8e4632a2012-07-26 13:40:34 +0200627 if (rpc == NULL) {
628 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
629 return (EXIT_FAILURE);
630 }
631
632 retval = netconf_op(server, conns, session_key, rpc);
633 nc_rpc_free (rpc);
634 return (retval);
635}
Radek Krejci035bf4e2012-07-25 10:59:09 +0200636
Tomas Cejka5064c232013-01-17 09:30:58 +0100637static int netconf_editconfig(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE target, NC_EDIT_DEFOP_TYPE defop, NC_EDIT_ERROPT_TYPE erropt, NC_EDIT_TESTOPT_TYPE testopt, const char* config)
Radek Krejci62ab34b2012-07-26 13:42:05 +0200638{
639 nc_rpc* rpc;
640 int retval = EXIT_SUCCESS;
641
642 /* create requests */
Tomas Cejka5064c232013-01-17 09:30:58 +0100643 /* TODO source NC_DATASTORE_CONFIG / NC_DATASTORE_URL */
644 rpc = nc_rpc_editconfig(target, NC_DATASTORE_CONFIG, defop, erropt, testopt, config);
Radek Krejci62ab34b2012-07-26 13:42:05 +0200645 if (rpc == NULL) {
646 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
647 return (EXIT_FAILURE);
648 }
649
650 retval = netconf_op(server, conns, session_key, rpc);
651 nc_rpc_free (rpc);
652 return (retval);
653}
654
Radek Krejci80c10d92012-07-30 08:38:50 +0200655static int netconf_killsession(server_rec* server, apr_hash_t* conns, const char* session_key, const char* sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200656{
657 nc_rpc* rpc;
658 int retval = EXIT_SUCCESS;
659
660 /* create requests */
661 rpc = nc_rpc_killsession(sid);
662 if (rpc == NULL) {
663 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
664 return (EXIT_FAILURE);
665 }
666
667 retval = netconf_op(server, conns, session_key, rpc);
668 nc_rpc_free (rpc);
669 return (retval);
670}
671
Radek Krejci80c10d92012-07-30 08:38:50 +0200672static int netconf_onlytargetop(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE target, nc_rpc* (*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +0200673{
674 nc_rpc* rpc;
675 int retval = EXIT_SUCCESS;
676
677 /* create requests */
Radek Krejci5cd7d422012-07-26 14:50:29 +0200678 rpc = op_func(target);
Radek Krejci2f318372012-07-26 14:22:35 +0200679 if (rpc == NULL) {
680 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
681 return (EXIT_FAILURE);
682 }
683
684 retval = netconf_op(server, conns, session_key, rpc);
685 nc_rpc_free (rpc);
686 return (retval);
687}
688
Radek Krejci80c10d92012-07-30 08:38:50 +0200689static int netconf_deleteconfig(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200690{
Tomas Cejka404d37e2013-04-13 02:31:35 +0200691 nc_rpc *rpc = NULL;
692 if (target != NC_DATASTORE_URL) {
693 rpc = nc_rpc_deleteconfig(target);
694 } else {
695 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
696 /* rpc = nc_rpc_deleteconfig(target, const char *url); */
697 return (EXIT_FAILURE);
698 }
699
700 return netconf_op(server, conns, session_key, rpc);
Radek Krejci5cd7d422012-07-26 14:50:29 +0200701}
702
Radek Krejci80c10d92012-07-30 08:38:50 +0200703static int netconf_lock(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200704{
705 return (netconf_onlytargetop(server, conns, session_key, target, nc_rpc_lock));
706}
707
Radek Krejci80c10d92012-07-30 08:38:50 +0200708static int netconf_unlock(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +0200709{
710 return (netconf_onlytargetop(server, conns, session_key, target, nc_rpc_unlock));
711}
712
Radek Krejci80c10d92012-07-30 08:38:50 +0200713/**
714 * @return REPLY_OK: 0, *data == NULL
715 * REPLY_DATA: 0, *data != NULL
716 * REPLY_ERROR: 1, *data == NULL
717 */
718static int netconf_generic(server_rec* server, apr_hash_t* conns, const char* session_key, const char* content, char** data)
719{
720 struct nc_session *session = NULL;
721 nc_reply* reply;
722 nc_rpc* rpc;
723 int retval = EXIT_SUCCESS;
Radek Krejcia332b692012-11-12 16:15:54 +0100724 NC_MSG_TYPE msgt;
725 NC_REPLY_TYPE replyt;
Radek Krejci80c10d92012-07-30 08:38:50 +0200726
727 /* create requests */
728 rpc = nc_rpc_generic(content);
729 if (rpc == NULL) {
730 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
731 return (EXIT_FAILURE);
732 }
733
Radek Krejcia332b692012-11-12 16:15:54 +0100734 if (data != NULL) {
735 *data = NULL;
736 }
Radek Krejci80c10d92012-07-30 08:38:50 +0200737
738 /* get session where send the RPC */
739 session = (struct nc_session *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
740 if (session != NULL) {
741 /* send the request and get the reply */
Radek Krejcia332b692012-11-12 16:15:54 +0100742 msgt = nc_session_send_recv(session, rpc, &reply);
743 nc_rpc_free (rpc);
744
745 /* process the result of the operation */
746 switch (msgt) {
747 case NC_MSG_UNKNOWN:
Radek Krejci80c10d92012-07-30 08:38:50 +0200748 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
749 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: receiving rpc-reply failed");
750 netconf_close(server, conns, session_key);
751 return (EXIT_FAILURE);
752 }
Radek Krejcia332b692012-11-12 16:15:54 +0100753 /* no break */
754 case NC_MSG_NONE:
Radek Krejci80c10d92012-07-30 08:38:50 +0200755 /* there is error handled by callback */
756 return (EXIT_FAILURE);
Radek Krejci80c10d92012-07-30 08:38:50 +0200757 break;
Radek Krejcia332b692012-11-12 16:15:54 +0100758 case NC_MSG_REPLY:
759 switch (replyt = nc_reply_get_type(reply)) {
760 case NC_REPLY_DATA:
761 if ((data != NULL) && (*data = nc_reply_get_data (reply)) == NULL) {
762 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: no data from reply");
763 nc_reply_free(reply);
764 return (EXIT_FAILURE);
765 }
766 retval = EXIT_SUCCESS;
767 break;
768 case NC_REPLY_OK:
769 retval = EXIT_SUCCESS;
770 break;
771 default:
772 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected rpc-reply (%d)", replyt);
773 retval = EXIT_FAILURE;
774 break;
775 }
Radek Krejci80c10d92012-07-30 08:38:50 +0200776 break;
777 default:
Radek Krejcia332b692012-11-12 16:15:54 +0100778 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected reply message received (%d)", msgt);
Radek Krejci80c10d92012-07-30 08:38:50 +0200779 retval = EXIT_FAILURE;
780 break;
781 }
782 nc_reply_free(reply);
783
784 return (retval);
785 } else {
786 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown session to process.");
787 return (EXIT_FAILURE);
788 }
789}
790
Tomas Cejka0a4bba82013-04-19 11:51:28 +0200791/**
792 * @}
793 *//* netconf_operations */
794
Radek Krejci469aab82012-07-22 18:42:20 +0200795server_rec* clb_print_server;
Radek Krejci7338bde2012-08-10 12:57:30 +0200796void clb_print(NC_VERB_LEVEL level, const char* msg)
Radek Krejci469aab82012-07-22 18:42:20 +0200797{
Radek Krejci7338bde2012-08-10 12:57:30 +0200798 switch (level) {
799 case NC_VERB_ERROR:
Tomas Cejka404d37e2013-04-13 02:31:35 +0200800 ap_log_error(APLOG_MARK, APLOG_ERR, 0, clb_print_server, "%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200801 break;
802 case NC_VERB_WARNING:
Tomas Cejka404d37e2013-04-13 02:31:35 +0200803 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, clb_print_server, "%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200804 break;
805 case NC_VERB_VERBOSE:
Tomas Cejka404d37e2013-04-13 02:31:35 +0200806 ap_log_error(APLOG_MARK, APLOG_INFO, 0, clb_print_server, "%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200807 break;
808 case NC_VERB_DEBUG:
Tomas Cejka404d37e2013-04-13 02:31:35 +0200809 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, clb_print_server, "%s", msg);
Radek Krejci7338bde2012-08-10 12:57:30 +0200810 break;
811 }
Radek Krejci469aab82012-07-22 18:42:20 +0200812}
813
David Kupka8e60a372012-09-04 09:15:20 +0200814void * thread_routine (void * arg)
815{
816 void * retval = NULL;
817
818 ssize_t buffer_len;
819 struct pollfd fds;
820 int status, buffer_size, ret;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200821 json_object *request, *reply, *capabilities;
David Kupka8e60a372012-09-04 09:15:20 +0200822 int operation;
Kupka David00b9c5c2012-09-05 09:45:50 +0200823 int i, chunk_len, len = 0;
David Kupka8e60a372012-09-04 09:15:20 +0200824 char* session_key, *data;
825 const char *host, *port, *user, *pass;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200826 const char *msgtext;
David Kupka8e60a372012-09-04 09:15:20 +0200827 const char *target, *source, *filter, *config, *defop, *erropt, *sid;
Tomas Cejka94da2c52013-01-08 18:20:30 +0100828 const char *identifier, *version, *format;
Kupka David00b9c5c2012-09-05 09:45:50 +0200829 struct nc_cpblts* cpblts = NULL;
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200830 struct session_with_mutex * locked_session;
David Kupka8e60a372012-09-04 09:15:20 +0200831 NC_DATASTORE ds_type_s, ds_type_t;
Tomas Cejka5064c232013-01-17 09:30:58 +0100832 NC_EDIT_DEFOP_TYPE defop_type = NC_EDIT_DEFOP_NOTSET;
David Kupka8e60a372012-09-04 09:15:20 +0200833 NC_EDIT_ERROPT_TYPE erropt_type = 0;
834
835 apr_pool_t * pool = ((struct pass_to_thread*)arg)->pool;
836 apr_hash_t *netconf_sessions_list = ((struct pass_to_thread*)arg)->netconf_sessions_list;
837 server_rec * server = ((struct pass_to_thread*)arg)->server;
838 int client = ((struct pass_to_thread*)arg)->client;
839
840 char * buffer, chunk_len_str[12], *chunked_msg;
841 char c;
842
843 while (!isterminated) {
844 fds.fd = client;
845 fds.events = POLLIN;
846 fds.revents = 0;
847
848 status = poll(&fds, 1, 1000);
849
850 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
851 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
852 //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "poll interrupted");
853 continue;
854 } else if (status < 0) {
855 /* 0: poll time outed
856 * close socket and ignore this request from the client, it can try it again
857 * -1: poll failed
858 * something wrong happend, close this socket and wait for another request
859 */
860 //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "poll failed, status %d(%d: %s)", status, errno, strerror(errno));
861 close(client);
862 break;
863 }
864 /* status > 0 */
865
866 /* check the status of the socket */
867
868 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
869 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
870 /* close client's socket (it's probably already closed by client */
871 //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "socket error (%d)", fds.revents);
872 close(client);
873 break;
874 }
875
876 /* read json in chunked framing */
877 buffer_size = 0;
878 buffer_len = 0;
879 buffer = NULL;
880 while (1) {
David Kupka1e3e4c82012-09-04 09:32:15 +0200881 /* read chunk length */
882 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
883 free (buffer);
884 buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +0200885 break;
886 }
David Kupka1e3e4c82012-09-04 09:32:15 +0200887 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
888 free (buffer);
889 buffer = NULL;
890 break;
891 }
892 i=0;
893 memset (chunk_len_str, 0, 12);
894 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
895 if (i==0 && c == '#') {
896 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
897 /* end but invalid */
898 free (buffer);
899 buffer = NULL;
900 }
901 /* end of message, double-loop break */
902 goto msg_complete;
903 }
904 chunk_len_str[i++] = c;
905 }
906 if (c != '\n') {
907 free (buffer);
908 buffer = NULL;
909 break;
910 }
911 if ((chunk_len = atoi (chunk_len_str)) == 0) {
912 free (buffer);
913 buffer = NULL;
914 break;
915 }
916 buffer_size += chunk_len+1;
917 buffer = realloc (buffer, sizeof(char)*buffer_size);
918 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
919 free (buffer);
920 buffer = NULL;
921 break;
922 }
923 buffer_len += ret;
924 }
925msg_complete:
David Kupka8e60a372012-09-04 09:15:20 +0200926
927 if (buffer != NULL) {
928 request = json_tokener_parse(buffer);
929 operation = json_object_get_int(json_object_object_get(request, "type"));
930
931 session_key = (char*) json_object_get_string(json_object_object_get(request, "session"));
932 /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
933 if (operation != MSG_CONNECT && session_key == NULL) {
934 reply = json_object_new_object();
935 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
936 json_object_object_add(reply, "error-message", json_object_new_string("Missing session specification."));
937 msgtext = json_object_to_json_string(reply);
938 send(client, msgtext, strlen(msgtext) + 1, 0);
939 json_object_put(reply);
940 /* there is some stupid client, so close the connection to give a chance to some other client */
941 close(client);
942 break;
943 }
944
945 /* get parameters */
Tomas Cejka5064c232013-01-17 09:30:58 +0100946 /* TODO NC_DATASTORE_URL */
David Kupka8e60a372012-09-04 09:15:20 +0200947 ds_type_t = -1;
948 if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
949 if (strcmp(target, "running") == 0) {
950 ds_type_t = NC_DATASTORE_RUNNING;
951 } else if (strcmp(target, "startup") == 0) {
952 ds_type_t = NC_DATASTORE_STARTUP;
953 } else if (strcmp(target, "candidate") == 0) {
954 ds_type_t = NC_DATASTORE_CANDIDATE;
955 }
956 }
957 ds_type_s = -1;
958 if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
959 if (strcmp(source, "running") == 0) {
960 ds_type_s = NC_DATASTORE_RUNNING;
961 } else if (strcmp(source, "startup") == 0) {
962 ds_type_s = NC_DATASTORE_STARTUP;
963 } else if (strcmp(source, "candidate") == 0) {
964 ds_type_s = NC_DATASTORE_CANDIDATE;
965 }
966 }
967
968 /* null global JSON error-reply */
969 err_reply = NULL;
970
971 /* prepare reply envelope */
972 reply = json_object_new_object();
973
974 /* process required operation */
975 switch (operation) {
976 case MSG_CONNECT:
977 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: Connect");
978
979 host = json_object_get_string(json_object_object_get(request, "host"));
980 port = json_object_get_string(json_object_object_get(request, "port"));
981 user = json_object_get_string(json_object_object_get(request, "user"));
982 pass = json_object_get_string(json_object_object_get(request, "pass"));
Tomas Cejkaf34f3912012-09-05 18:14:06 +0200983 capabilities = json_object_object_get(request, "capabilities");
Tomas Cejkaae9efe52013-04-23 13:37:36 +0200984 if ((capabilities != NULL) && ((len = json_object_array_length(capabilities)) > 0)) {
985 cpblts = nc_cpblts_new (NULL);
986 for (i=0; i<len; i++) {
987 nc_cpblts_add (cpblts, json_object_get_string(json_object_array_get_idx(capabilities, i)));
988 }
989 } else {
990 ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "no capabilities specified");
991 }
David Kupka8e60a372012-09-04 09:15:20 +0200992 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "host: %s, port: %s, user: %s", host, port, user);
993 if ((host == NULL) || (user == NULL)) {
994 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Cannot connect - insufficient input.");
995 session_key = NULL;
996 } else {
Kupka David00b9c5c2012-09-05 09:45:50 +0200997 session_key = netconf_connect(server, pool, netconf_sessions_list, host, port, user, pass, cpblts);
998 nc_cpblts_free (cpblts);
David Kupka8e60a372012-09-04 09:15:20 +0200999 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "hash: %s", session_key);
1000 }
1001
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001002 /** \todo check if this is neccessary... probably leads to memory leaks */
David Kupka8e60a372012-09-04 09:15:20 +02001003 reply = json_object_new_object();
1004 if (session_key == NULL) {
1005 /* negative reply */
1006 if (err_reply == NULL) {
1007 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1008 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
Tomas Cejka1490ef12012-12-10 00:16:13 +01001009 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Connection failed.");
David Kupka8e60a372012-09-04 09:15:20 +02001010 } else {
1011 /* use filled err_reply from libnetconf's callback */
1012 json_object_put(reply);
1013 reply = err_reply;
Tomas Cejka1490ef12012-12-10 00:16:13 +01001014 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Connect - error from libnetconf's callback.");
David Kupka8e60a372012-09-04 09:15:20 +02001015 }
1016 } else {
1017 /* positive reply */
1018 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1019 json_object_object_add(reply, "session", json_object_new_string(session_key));
1020
1021 free(session_key);
1022 }
1023
1024 break;
1025 case MSG_GET:
1026 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get (session %s)", session_key);
1027
1028 filter = json_object_get_string(json_object_object_get(request, "filter"));
1029
1030 //ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "get filter: %p", filter);
1031
Tomas Cejkacdc274e2012-09-05 18:15:33 +02001032 if ((data = netconf_get(server, netconf_sessions_list, session_key, filter)) == NULL) {
David Kupka8e60a372012-09-04 09:15:20 +02001033 if (err_reply == NULL) {
1034 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1035 json_object_object_add(reply, "error-message", json_object_new_string("get failed."));
1036 } else {
1037 /* use filled err_reply from libnetconf's callback */
1038 json_object_put(reply);
1039 reply = err_reply;
1040 }
1041 } else {
1042 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1043 json_object_object_add(reply, "data", json_object_new_string(data));
1044 }
1045 break;
1046 case MSG_GETCONFIG:
1047 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get-config (session %s)", session_key);
1048
1049 filter = json_object_get_string(json_object_object_get(request, "filter"));
1050
1051 if (ds_type_s == -1) {
1052 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1053 json_object_object_add(reply, "error-message", json_object_new_string("Invalid source repository type requested."));
1054 break;
1055 }
1056
1057 if ((data = netconf_getconfig(server, netconf_sessions_list, session_key, ds_type_s, filter)) == NULL) {
1058 if (err_reply == NULL) {
1059 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1060 json_object_object_add(reply, "error-message", json_object_new_string("get-config failed."));
1061 } else {
1062 /* use filled err_reply from libnetconf's callback */
1063 json_object_put(reply);
1064 reply = err_reply;
1065 }
1066 } else {
1067 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1068 json_object_object_add(reply, "data", json_object_new_string(data));
1069 }
1070 break;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001071 case MSG_GETSCHEMA:
1072 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get-schema (session %s)", session_key);
1073 identifier = json_object_get_string(json_object_object_get(request, "identifier"));
1074 if (identifier == NULL) {
1075 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1076 json_object_object_add(reply, "error-message", json_object_new_string("No identifier for get-schema supplied."));
1077 break;
1078 }
Tomas Cejka94da2c52013-01-08 18:20:30 +01001079 version = json_object_get_string(json_object_object_get(request, "version"));
1080 format = json_object_get_string(json_object_object_get(request, "format"));
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001081
Tomas Cejka94da2c52013-01-08 18:20:30 +01001082 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "get-schema(version: %s, format: %s)", version, format);
Tomas Cejkaafe46072013-01-09 16:55:58 +01001083 if ((data = netconf_getschema(server, netconf_sessions_list, session_key, identifier, version, format)) == NULL) {
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001084 if (err_reply == NULL) {
1085 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1086 json_object_object_add(reply, "error-message", json_object_new_string("get-config failed."));
1087 } else {
1088 /* use filled err_reply from libnetconf's callback */
1089 json_object_put(reply);
1090 reply = err_reply;
1091 }
1092 } else {
1093 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1094 json_object_object_add(reply, "data", json_object_new_string(data));
1095 }
1096 break;
David Kupka8e60a372012-09-04 09:15:20 +02001097 case MSG_EDITCONFIG:
1098 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: edit-config (session %s)", session_key);
1099
1100 defop = json_object_get_string(json_object_object_get(request, "default-operation"));
1101 if (defop != NULL) {
1102 if (strcmp(defop, "merge") == 0) {
1103 defop_type = NC_EDIT_DEFOP_MERGE;
1104 } else if (strcmp(defop, "replace") == 0) {
1105 defop_type = NC_EDIT_DEFOP_REPLACE;
1106 } else if (strcmp(defop, "none") == 0) {
1107 defop_type = NC_EDIT_DEFOP_NONE;
1108 } else {
1109 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1110 json_object_object_add(reply, "error-message", json_object_new_string("Invalid default-operation parameter."));
1111 break;
1112 }
1113 } else {
Tomas Cejka5064c232013-01-17 09:30:58 +01001114 defop_type = NC_EDIT_DEFOP_NOTSET;
David Kupka8e60a372012-09-04 09:15:20 +02001115 }
1116
1117 erropt = json_object_get_string(json_object_object_get(request, "error-option"));
1118 if (erropt != NULL) {
1119 if (strcmp(erropt, "continue-on-error") == 0) {
1120 erropt_type = NC_EDIT_ERROPT_CONT;
1121 } else if (strcmp(erropt, "stop-on-error") == 0) {
1122 erropt_type = NC_EDIT_ERROPT_STOP;
1123 } else if (strcmp(erropt, "rollback-on-error") == 0) {
1124 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
1125 } else {
1126 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1127 json_object_object_add(reply, "error-message", json_object_new_string("Invalid error-option parameter."));
1128 break;
1129 }
1130 } else {
1131 erropt_type = 0;
1132 }
1133
1134 if (ds_type_t == -1) {
1135 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1136 json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
1137 break;
1138 }
1139
1140 config = json_object_get_string(json_object_object_get(request, "config"));
1141 if (config == NULL) {
1142 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1143 json_object_object_add(reply, "error-message", json_object_new_string("Invalid config data parameter."));
1144 break;
1145 }
1146
Tomas Cejka5064c232013-01-17 09:30:58 +01001147 /* TODO url capability see netconf_editconfig */
1148 /* TODO TESTSET - :validate:1.1 capability? http://tools.ietf.org/html/rfc6241#section-7.2 */
1149 if (netconf_editconfig(server, netconf_sessions_list, session_key, ds_type_t, defop_type, erropt_type, NC_EDIT_TESTOPT_NOTSET, config) != EXIT_SUCCESS) {
David Kupka8e60a372012-09-04 09:15:20 +02001150 if (err_reply == NULL) {
1151 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1152 json_object_object_add(reply, "error-message", json_object_new_string("edit-config failed."));
1153 } else {
1154 /* use filled err_reply from libnetconf's callback */
1155 json_object_put(reply);
1156 reply = err_reply;
1157 }
1158 } else {
1159 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1160 }
1161 break;
1162 case MSG_COPYCONFIG:
1163 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: copy-config (session %s)", session_key);
1164 config = NULL;
1165
1166 if (source == NULL) {
1167 /* no explicit source specified -> use config data */
Tomas Cejka027f3bc2012-11-10 20:28:36 +01001168 ds_type_s = NC_DATASTORE_CONFIG;
David Kupka8e60a372012-09-04 09:15:20 +02001169 config = json_object_get_string(json_object_object_get(request, "config"));
1170 } else if (ds_type_s == -1) {
1171 /* source datastore specified, but it is invalid */
1172 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1173 json_object_object_add(reply, "error-message", json_object_new_string("Invalid source repository type requested."));
1174 break;
1175 }
1176
1177 if (ds_type_t == -1) {
1178 /* invalid target datastore specified */
1179 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1180 json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
1181 break;
1182 }
1183
1184 if (source == NULL && config == NULL) {
1185 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1186 json_object_object_add(reply, "error-message", json_object_new_string("invalid input parameters - one of source and config is required."));
1187 } else {
Tomas Cejka4ce5d0a2013-01-17 19:23:54 +01001188 if (netconf_copyconfig(server, netconf_sessions_list, session_key, ds_type_s, ds_type_t, config, "") != EXIT_SUCCESS) {
David Kupka8e60a372012-09-04 09:15:20 +02001189 if (err_reply == NULL) {
1190 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1191 json_object_object_add(reply, "error-message", json_object_new_string("copy-config failed."));
1192 } else {
1193 /* use filled err_reply from libnetconf's callback */
1194 json_object_put(reply);
1195 reply = err_reply;
1196 }
1197 } else {
1198 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1199 }
1200 }
1201 break;
1202 case MSG_DELETECONFIG:
1203 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: delete-config (session %s)", session_key);
1204 /* no break - unifying code */
1205 case MSG_LOCK:
1206 if (operation == MSG_LOCK) {
1207 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: lock (session %s)", session_key);
1208 }
1209 /* no break - unifying code */
1210 case MSG_UNLOCK:
1211 if (operation == MSG_UNLOCK) {
1212 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: unlock (session %s)", session_key);
1213 }
1214
1215 if (ds_type_t == -1) {
1216 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1217 json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
1218 break;
1219 }
1220
1221 switch(operation) {
1222 case MSG_DELETECONFIG:
1223 status = netconf_deleteconfig(server, netconf_sessions_list, session_key, ds_type_t);
1224 break;
1225 case MSG_LOCK:
1226 status = netconf_lock(server, netconf_sessions_list, session_key, ds_type_t);
1227 break;
1228 case MSG_UNLOCK:
1229 status = netconf_unlock(server, netconf_sessions_list, session_key, ds_type_t);
1230 break;
1231 default:
1232 status = -1;
1233 break;
1234 }
1235
1236 if (status != EXIT_SUCCESS) {
1237 if (err_reply == NULL) {
1238 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1239 json_object_object_add(reply, "error-message", json_object_new_string("operation failed."));
1240 } else {
1241 /* use filled err_reply from libnetconf's callback */
1242 json_object_put(reply);
1243 reply = err_reply;
1244 }
1245 } else {
1246 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1247 }
1248 break;
1249 case MSG_KILL:
1250 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: kill-session, session %s", session_key);
1251
1252 sid = json_object_get_string(json_object_object_get(request, "session-id"));
1253
1254 if (sid == NULL) {
1255 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1256 json_object_object_add(reply, "error-message", json_object_new_string("Missing session-id parameter."));
1257 break;
1258 }
1259
1260 if (netconf_killsession(server, netconf_sessions_list, session_key, sid) != EXIT_SUCCESS) {
1261 if (err_reply == NULL) {
1262 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1263 json_object_object_add(reply, "error-message", json_object_new_string("kill-session failed."));
1264 } else {
1265 /* use filled err_reply from libnetconf's callback */
1266 json_object_put(reply);
1267 reply = err_reply;
1268 }
1269 } else {
1270 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1271 }
1272 break;
1273 case MSG_DISCONNECT:
1274 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: Disconnect session %s", session_key);
1275
1276 if (netconf_close(server, netconf_sessions_list, session_key) != EXIT_SUCCESS) {
1277 if (err_reply == NULL) {
1278 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1279 json_object_object_add(reply, "error-message", json_object_new_string("Invalid session identifier."));
1280 } else {
1281 /* use filled err_reply from libnetconf's callback */
1282 json_object_put(reply);
1283 reply = err_reply;
1284 }
1285 } else {
1286 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1287 }
1288 break;
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001289 case MSG_RELOADHELLO:
David Kupka8e60a372012-09-04 09:15:20 +02001290 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get info about session %s", session_key);
1291
Kupka Davidda134a12012-09-06 14:12:16 +02001292 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001293 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
1294 struct nc_session *temp_session = nc_session_connect_channel(locked_session->session, NULL);
1295 prepare_status_message(locked_session, temp_session);
1296 nc_session_close(temp_session, NC_SESSION_TERM_CLOSED);
David Kupka8e60a372012-09-04 09:15:20 +02001297 } else {
1298 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1299 json_object_object_add(reply, "error-message", json_object_new_string("Invalid session identifier."));
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001300 break;
David Kupka8e60a372012-09-04 09:15:20 +02001301 }
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001302 /* do NOT insert "break" here, we want to give new info back */;
1303 case MSG_INFO:
1304 if (operation != MSG_INFO) {
1305 json_object_object_del(reply, NULL);
1306 reply = locked_session->hello_message;
1307 } else {
1308 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get info about session %s", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02001309
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001310 locked_session = (struct session_with_mutex *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
1311 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
1312 json_object_object_del(reply, NULL);
1313 reply = locked_session->hello_message;
1314 } else {
1315 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1316 json_object_object_add(reply, "error-message", json_object_new_string("Invalid session identifier."));
1317 }
1318 }
David Kupka8e60a372012-09-04 09:15:20 +02001319 break;
1320 case MSG_GENERIC:
1321 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: generic request for session %s", session_key);
1322
1323 config = json_object_get_string(json_object_object_get(request, "content"));
1324
1325 if (config == NULL) {
1326 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1327 json_object_object_add(reply, "error-message", json_object_new_string("Missing content parameter."));
1328 break;
1329 }
1330
1331 if (netconf_generic(server, netconf_sessions_list, session_key, config, &data) != EXIT_SUCCESS) {
1332 if (err_reply == NULL) {
1333 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1334 json_object_object_add(reply, "error-message", json_object_new_string("kill-session failed."));
1335 } else {
1336 /* use filled err_reply from libnetconf's callback */
1337 json_object_put(reply);
1338 reply = err_reply;
1339 }
1340 } else {
1341 if (data == NULL) {
1342 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
1343 } else {
1344 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
1345 json_object_object_add(reply, "data", json_object_new_string(data));
1346 }
1347 }
1348 break;
1349 default:
1350 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown mod_netconf operation requested (%d)", operation);
1351 reply = json_object_new_object();
1352 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
1353 json_object_object_add(reply, "error-message", json_object_new_string("Operation not supported."));
1354 break;
1355 }
1356 json_object_put(request);
1357
David Kupka1e3e4c82012-09-04 09:32:15 +02001358 /* send reply to caller */
1359 if (reply != NULL) {
1360 msgtext = json_object_to_json_string(reply);
1361 if (asprintf (&chunked_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
1362 free (buffer);
David Kupka8e60a372012-09-04 09:15:20 +02001363 break;
1364 }
David Kupka1e3e4c82012-09-04 09:32:15 +02001365 send(client, chunked_msg, strlen(chunked_msg) + 1, 0);
1366 json_object_put(reply);
1367 free (chunked_msg);
1368 free (buffer);
1369 } else {
1370 break;
David Kupka8e60a372012-09-04 09:15:20 +02001371 }
1372 }
1373 }
David Kupka8e60a372012-09-04 09:15:20 +02001374 free (arg);
1375
1376 return retval;
1377}
1378
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001379/**
1380 * \brief Close all open NETCONF sessions.
1381 *
1382 * During termination of mod_netconf, it is useful to close all remaining
1383 * sessions. This function iterates over the list of sessions and close them
1384 * all.
1385 *
1386 * \param[in] server pointer to server_rec for logging
1387 * \param[in] p apr pool needed for hash table iterating
1388 * \param[in] ht hash table of session_with_mutex structs
1389 */
1390static void close_all_nc_sessions(server_rec* server, apr_pool_t *p, apr_hash_t *ht)
1391{
1392 apr_hash_index_t *hi;
1393 void *val = NULL;
1394 struct session_with_mutex *swm = NULL;
1395 struct nc_session *ns = NULL;
1396 const char *hashed_key = NULL;
1397 apr_ssize_t hashed_key_length;
1398
1399 for (hi = apr_hash_first(p, ht); hi; hi = apr_hash_next(hi)) {
1400 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
1401 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Closing NETCONF session (%s).", hashed_key);
1402 swm = (struct session_with_mutex *) val;
1403 if (swm != NULL) {
1404 pthread_mutex_lock(&swm->lock);
1405 if (swm->session != NULL) {
1406 ns = swm->session;
1407 nc_session_close(ns, NC_SESSION_TERM_CLOSED);
1408 nc_session_free(ns);
1409 swm->session = NULL;
1410 }
1411 pthread_mutex_unlock(&swm->lock);
1412 }
1413 }
1414}
1415
1416static void check_timeout_and_close(server_rec* server, apr_pool_t *p, apr_hash_t *ht)
1417{
1418 apr_hash_index_t *hi;
1419 void *val = NULL;
1420 struct nc_session *ns = NULL;
1421 struct session_with_mutex *swm = NULL;
1422 const char *hashed_key = NULL;
1423 apr_ssize_t hashed_key_length;
1424 apr_time_t current_time = apr_time_now();
1425
1426 for (hi = apr_hash_first(p, ht); hi; hi = apr_hash_next(hi)) {
1427 apr_hash_this(hi, (const void **) &hashed_key, &hashed_key_length, &val);
1428 swm = (struct session_with_mutex *) val;
1429 if (swm == NULL) {
1430 continue;
1431 }
1432 ns = swm->session;
1433 if (ns == NULL) {
1434 continue;
1435 }
1436 pthread_mutex_lock(&swm->lock);
1437 if ((current_time - swm->last_activity) > apr_time_from_sec(ACTIVITY_TIMEOUT)) {
1438 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Closing NETCONF session (%s).", hashed_key);
1439 nc_session_close(ns, NC_SESSION_TERM_CLOSED);
1440 nc_session_free (ns);
1441 ns = NULL;
1442 /* remove session from the active sessions list */
1443 apr_hash_set(ht, hashed_key, APR_HASH_KEY_STRING, NULL);
1444 }
1445 pthread_mutex_unlock(&swm->lock);
1446 }
1447}
1448
1449
1450/**
Radek Krejcif23850c2012-07-23 16:14:17 +02001451 * This is actually implementation of NETCONF client
1452 * - requests are received from UNIX socket in the predefined format
1453 * - results are replied through the same way
1454 * - the daemon run as a separate process, but it is started and stopped
1455 * automatically by Apache.
1456 *
1457 */
Radek Krejci469aab82012-07-22 18:42:20 +02001458static void forked_proc(apr_pool_t * pool, server_rec * server)
1459{
Tomas Cejkad340dbf2013-03-24 20:36:57 +01001460 struct timeval tv;
Radek Krejci469aab82012-07-22 18:42:20 +02001461 struct sockaddr_un local, remote;
David Kupka8e60a372012-09-04 09:15:20 +02001462 int lsock, client, ret, i, pthread_count = 0;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001463 unsigned int olds = 0, timediff = 0;
David Kupka8e60a372012-09-04 09:15:20 +02001464 socklen_t len;
Radek Krejciae021c12012-07-25 18:03:52 +02001465 mod_netconf_cfg *cfg;
Radek Krejci469aab82012-07-22 18:42:20 +02001466 apr_hash_t *netconf_sessions_list;
David Kupka8e60a372012-09-04 09:15:20 +02001467 struct pass_to_thread * arg;
1468 pthread_t * ptids = calloc (1,sizeof(pthread_t));
1469 struct timespec maxtime;
1470 pthread_rwlockattr_t lock_attrs;
Tomas Cejka404d37e2013-04-13 02:31:35 +02001471 #ifdef WITH_NOTIFICATIONS
1472 char use_notifications = 0;
1473 #endif
David Kupka8e60a372012-09-04 09:15:20 +02001474
Tomas Cejka404d37e2013-04-13 02:31:35 +02001475 /* wait at most 5 seconds for every thread to terminate */
David Kupka8e60a372012-09-04 09:15:20 +02001476 maxtime.tv_sec = 5;
1477 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02001478
Radek Krejcif23850c2012-07-23 16:14:17 +02001479 /* change uid and gid of process for security reasons */
Radek Krejci469aab82012-07-22 18:42:20 +02001480 unixd_setup_child();
1481
Radek Krejciae021c12012-07-25 18:03:52 +02001482 cfg = ap_get_module_config(server->module_config, &netconf_module);
1483 if (cfg == NULL) {
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02001484 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Getting mod_netconf configuration failed");
1485 return;
1486 }
Radek Krejci469aab82012-07-22 18:42:20 +02001487
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02001488 /* create listening UNIX socket to accept incoming connections */
Radek Krejci469aab82012-07-22 18:42:20 +02001489 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
1490 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Creating socket failed (%s)", strerror(errno));
1491 return;
1492 }
1493
1494 local.sun_family = AF_UNIX;
Radek Krejciae021c12012-07-25 18:03:52 +02001495 strncpy(local.sun_path, cfg->sockname, sizeof(local.sun_path));
Radek Krejci469aab82012-07-22 18:42:20 +02001496 unlink(local.sun_path);
1497 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
1498
1499 if (bind(lsock, (struct sockaddr *) &local, len) == -1) {
1500 if (errno == EADDRINUSE) {
1501 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "mod_netconf socket address already in use");
1502 close(lsock);
1503 exit(0);
1504 }
1505 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Binding socket failed (%s)", strerror(errno));
1506 close(lsock);
1507 return;
1508 }
1509
1510 if (listen(lsock, MAX_SOCKET_CL) == -1) {
1511 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Setting up listen socket failed (%s)", strerror(errno));
1512 close(lsock);
1513 return;
1514 }
1515
Tomas Cejkaba21b382013-04-13 02:37:32 +02001516 /* prepare internal lists */
1517 netconf_sessions_list = apr_hash_make(pool);
1518
Tomas Cejkad340dbf2013-03-24 20:36:57 +01001519 #ifdef WITH_NOTIFICATIONS
Tomas Cejkaba21b382013-04-13 02:37:32 +02001520 if (notification_init(pool, server, netconf_sessions_list) == -1) {
Tomas Cejkad340dbf2013-03-24 20:36:57 +01001521 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "libwebsockets initialization failed");
1522 use_notifications = 0;
1523 } else {
1524 use_notifications = 1;
1525 }
1526 #endif
1527
Radek Krejci469aab82012-07-22 18:42:20 +02001528 /* setup libnetconf's callbacks */
1529 nc_verbosity(NC_VERB_DEBUG);
1530 clb_print_server = server;
1531 nc_callback_print(clb_print);
1532 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
1533 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
1534 nc_callback_sshauth_password(netconf_callback_sshauth_password);
Radek Krejcic11fd862012-07-26 12:41:21 +02001535 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +02001536
1537 /* disable publickey authentication */
1538 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
1539
Tomas Cejka44e71db2013-04-21 15:50:47 +02001540 ncdflt_set_basic_mode(NCWD_MODE_ALL);
1541
David Kupka8e60a372012-09-04 09:15:20 +02001542 /* create mutex protecting session list */
1543 pthread_rwlockattr_init(&lock_attrs);
1544 /* rwlock is shared only with threads in this process */
1545 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
1546 /* create rw lock */
1547 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
1548 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Initialization of mutex failed: %d (%s)", errno, strerror(errno));
1549 close (lsock);
1550 return;
1551 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +01001552
1553 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02001554 while (isterminated == 0) {
Tomas Cejkad340dbf2013-03-24 20:36:57 +01001555 gettimeofday(&tv, NULL);
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001556 timediff = (unsigned int)tv.tv_sec - olds;
Tomas Cejkad340dbf2013-03-24 20:36:57 +01001557 #ifdef WITH_NOTIFICATIONS
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001558 if (timediff > 60) {
Tomas Cejkad340dbf2013-03-24 20:36:57 +01001559 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "handling notifications");
1560 }
1561 if (use_notifications == 1) {
1562 notification_handle();
1563 }
1564 #endif
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001565 if (timediff > ACTIVITY_CHECK_INTERVAL) {
1566 check_timeout_and_close(server, pool, netconf_sessions_list);
1567 }
Radek Krejci469aab82012-07-22 18:42:20 +02001568
1569 /* open incoming connection if any */
David Kupka8e60a372012-09-04 09:15:20 +02001570 len = sizeof(remote);
Tomas Cejkad340dbf2013-03-24 20:36:57 +01001571 if (((unsigned int)tv.tv_sec - olds) > 60) {
1572 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "accepting another client");
1573 olds = tv.tv_sec;
1574 }
David Kupka8e60a372012-09-04 09:15:20 +02001575 client = accept(lsock, (struct sockaddr *) &remote, &len);
Radek Krejci469aab82012-07-22 18:42:20 +02001576 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
1577 apr_sleep(SLEEP_TIME);
1578 continue;
1579 } else if (client == -1 && (errno == EINTR)) {
1580 continue;
1581 } else if (client == -1) {
1582 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Accepting mod_netconf client connection failed (%s)", strerror(errno));
1583 continue;
1584 }
Radek Krejci469aab82012-07-22 18:42:20 +02001585
1586 /* set client's socket as non-blocking */
Radek Krejcif23850c2012-07-23 16:14:17 +02001587 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02001588
David Kupka8e60a372012-09-04 09:15:20 +02001589 arg = malloc (sizeof(struct pass_to_thread));
1590 arg->client = client;
1591 arg->pool = pool;
1592 arg->server = server;
1593 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02001594
David Kupka8e60a372012-09-04 09:15:20 +02001595 /* start new thread. It will serve this particular request and then terminate */
1596 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void*)arg)) != 0) {
1597 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Creating POSIX thread failed: %d\n", ret);
1598 } else {
1599 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Thread %lu created", ptids[pthread_count]);
1600 pthread_count++;
1601 ptids = realloc (ptids, sizeof(pthread_t)*(pthread_count+1));
1602 ptids[pthread_count] = 0;
1603 }
Radek Krejci469aab82012-07-22 18:42:20 +02001604
David Kupka8e60a372012-09-04 09:15:20 +02001605 /* check if some thread already terminated, free some resources by joining it */
1606 for (i=0; i<pthread_count; i++) {
1607 if (pthread_tryjoin_np (ptids[i], (void**)&arg) == 0) {
1608 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Thread %lu joined with retval %p", ptids[i], arg);
1609 pthread_count--;
1610 if (pthread_count > 0) {
1611 /* place last Thread ID on the place of joined one */
1612 ptids[i] = ptids[pthread_count];
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02001613 }
Radek Krejci469aab82012-07-22 18:42:20 +02001614 }
1615 }
David Kupka8e60a372012-09-04 09:15:20 +02001616 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Running %d threads", pthread_count);
Radek Krejci469aab82012-07-22 18:42:20 +02001617 }
1618
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001619 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf terminating...");
David Kupka8e60a372012-09-04 09:15:20 +02001620 /* join all threads */
1621 for (i=0; i<pthread_count; i++) {
1622 pthread_timedjoin_np (ptids[i], (void**)&arg, &maxtime);
1623 }
1624 free (ptids);
1625
Radek Krejci469aab82012-07-22 18:42:20 +02001626 close(lsock);
1627
Tomas Cejkad340dbf2013-03-24 20:36:57 +01001628 #ifdef WITH_NOTIFICATIONS
1629 notification_close();
1630 #endif
1631
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02001632 /* close all NETCONF sessions */
1633 close_all_nc_sessions(server, pool, netconf_sessions_list);
1634
David Kupka8e60a372012-09-04 09:15:20 +02001635 /* destroy rwlock */
1636 pthread_rwlock_destroy(&session_lock);
1637 pthread_rwlockattr_destroy(&lock_attrs);
1638
Radek Krejci469aab82012-07-22 18:42:20 +02001639 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Exiting from the mod_netconf daemon");
1640
1641 exit(APR_SUCCESS);
1642}
1643
Radek Krejcif23850c2012-07-23 16:14:17 +02001644static void *mod_netconf_create_conf(apr_pool_t * pool, server_rec * s)
Radek Krejci469aab82012-07-22 18:42:20 +02001645{
Radek Krejcif23850c2012-07-23 16:14:17 +02001646 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Init netconf module config");
1647
1648 mod_netconf_cfg *config = apr_pcalloc(pool, sizeof(mod_netconf_cfg));
1649 apr_pool_create(&config->pool, pool);
1650 config->forkproc = NULL;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02001651 config->sockname = SOCKET_FILENAME;
Radek Krejcif23850c2012-07-23 16:14:17 +02001652
1653 return (void *)config;
Radek Krejci469aab82012-07-22 18:42:20 +02001654}
1655
1656static int mod_netconf_master_init(apr_pool_t * pconf, apr_pool_t * ptemp,
1657 apr_pool_t * plog, server_rec * s)
1658{
Radek Krejcif23850c2012-07-23 16:14:17 +02001659 mod_netconf_cfg *config;
1660 apr_status_t res;
1661
Radek Krejci469aab82012-07-22 18:42:20 +02001662 /* These two help ensure that we only init once. */
Radek Krejcia332b692012-11-12 16:15:54 +01001663 void *data = NULL;
Radek Krejcif23850c2012-07-23 16:14:17 +02001664 const char *userdata_key = "netconf_ipc_init";
Radek Krejci469aab82012-07-22 18:42:20 +02001665
1666 /*
1667 * The following checks if this routine has been called before.
1668 * This is necessary because the parent process gets initialized
1669 * a couple of times as the server starts up.
1670 */
1671 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1672 if (!data) {
1673 apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
1674 return (OK);
1675 }
1676
Radek Krejcif23850c2012-07-23 16:14:17 +02001677 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "creating mod_netconf daemon");
1678 config = ap_get_module_config(s->module_config, &netconf_module);
Radek Krejcidfaa6ea2012-07-23 09:04:43 +02001679
Radek Krejcif23850c2012-07-23 16:14:17 +02001680 if (config && config->forkproc == NULL) {
1681 config->forkproc = apr_pcalloc(config->pool, sizeof(apr_proc_t));
1682 res = apr_proc_fork(config->forkproc, config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02001683 switch (res) {
1684 case APR_INCHILD:
1685 /* set signal handler */
Radek Krejcif23850c2012-07-23 16:14:17 +02001686 apr_signal_init(config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02001687 apr_signal(SIGTERM, signal_handler);
1688
1689 /* log start of the separated NETCONF communication process */
Radek Krejcif23850c2012-07-23 16:14:17 +02001690 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "mod_netconf daemon started (PID %d)", getpid());
Radek Krejci469aab82012-07-22 18:42:20 +02001691
1692 /* start main loop providing NETCONF communication */
Radek Krejcif23850c2012-07-23 16:14:17 +02001693 forked_proc(config->pool, s);
Radek Krejci469aab82012-07-22 18:42:20 +02001694
Radek Krejcif23850c2012-07-23 16:14:17 +02001695 /* I never should be here, wtf?!? */
1696 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_netconf daemon unexpectedly stopped");
Radek Krejci469aab82012-07-22 18:42:20 +02001697 exit(APR_EGENERAL);
1698 break;
1699 case APR_INPARENT:
1700 /* register child to be killed (SIGTERM) when the module config's pool dies */
Radek Krejcif23850c2012-07-23 16:14:17 +02001701 apr_pool_note_subprocess(config->pool, config->forkproc, APR_KILL_AFTER_TIMEOUT);
Radek Krejci469aab82012-07-22 18:42:20 +02001702 break;
1703 default:
1704 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "apr_proc_fork() failed");
1705 break;
1706 }
Radek Krejcif23850c2012-07-23 16:14:17 +02001707 } else {
1708 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_netconf misses configuration structure");
Radek Krejci469aab82012-07-22 18:42:20 +02001709 }
1710
1711 return OK;
1712}
1713
Radek Krejci469aab82012-07-22 18:42:20 +02001714/**
1715 * Register module hooks
1716 */
1717static void mod_netconf_register_hooks(apr_pool_t * p)
1718{
Radek Krejcif23850c2012-07-23 16:14:17 +02001719 ap_hook_post_config(mod_netconf_master_init, NULL, NULL, APR_HOOK_LAST);
Radek Krejci469aab82012-07-22 18:42:20 +02001720}
1721
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02001722static const char* cfg_set_socket_path(cmd_parms* cmd, void* cfg, const char* arg)
1723{
1724 ((mod_netconf_cfg*)cfg)->sockname = apr_pstrdup(cmd->pool, arg);
1725 return NULL;
1726}
1727
1728static const command_rec netconf_cmds[] = {
1729 AP_INIT_TAKE1("NetconfSocket", cfg_set_socket_path, NULL, OR_ALL, "UNIX socket path for mod_netconf communication."),
1730 {NULL}
1731};
1732
Radek Krejci469aab82012-07-22 18:42:20 +02001733/* Dispatch list for API hooks */
1734module AP_MODULE_DECLARE_DATA netconf_module = {
1735 STANDARD20_MODULE_STUFF,
1736 NULL, /* create per-dir config structures */
1737 NULL, /* merge per-dir config structures */
Radek Krejcif23850c2012-07-23 16:14:17 +02001738 mod_netconf_create_conf, /* create per-server config structures */
Radek Krejci469aab82012-07-22 18:42:20 +02001739 NULL, /* merge per-server config structures */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02001740 netconf_cmds, /* table of config file commands */
Radek Krejci469aab82012-07-22 18:42:20 +02001741 mod_netconf_register_hooks /* register hooks */
1742};
Radek Krejcia332b692012-11-12 16:15:54 +01001743