blob: 43080a89484326a00819e8873068a1b4e20bbe8e [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
8 */
9/*
10 * Copyright (C) 2011-2012 CESNET
11 *
12 * LICENSE TERMS
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in
21 * the documentation and/or other materials provided with the
22 * distribution.
23 * 3. Neither the name of the Company nor the names of its contributors
24 * may be used to endorse or promote products derived from this
25 * software without specific prior written permission.
26 *
27 * ALTERNATIVELY, provided that this notice is retained in full, this
28 * product may be distributed under the terms of the GNU General Public
29 * License (GPL) version 2 or later, in which case the provisions
30 * of the GPL apply INSTEAD OF those given above.
31 *
32 * This software is provided ``as is'', and any express or implied
33 * warranties, including, but not limited to, the implied warranties of
34 * merchantability and fitness for a particular purpose are disclaimed.
35 * In no event shall the company or contributors be liable for any
36 * direct, indirect, incidental, special, exemplary, or consequential
37 * damages (including, but not limited to, procurement of substitute
38 * goods or services; loss of use, data, or profits; or business
39 * interruption) however caused and on any theory of liability, whether
40 * in contract, strict liability, or tort (including negligence or
41 * otherwise) arising in any way out of the use of this software, even
42 * if advised of the possibility of such damage.
43 *
44 */
45
46#include "httpd.h"
47#include "http_config.h"
48#include "http_protocol.h"
49#include "http_request.h"
50#include "ap_config.h"
51#include "http_log.h"
52#include "apu.h"
53#include "apr_general.h"
54#include "apr_sha1.h"
55#include "apr_file_io.h"
56
57#include <unixd.h>
58#include <apr_base64.h>
59#include <apr_pools.h>
60#include <apr_general.h>
61#include <apr_hash.h>
62#include <apr_strings.h>
63#include <apr_thread_proc.h>
64#include <apr_signal.h>
65
66#include <sys/types.h>
67#include <sys/socket.h>
68#include <sys/un.h>
69#include <stdlib.h>
70#include <stdio.h>
71#include <poll.h>
72#include <unistd.h>
73#include <string.h>
74#include <errno.h>
75
Radek Krejci8fd1f5e2012-07-24 17:33:36 +020076#include <json/json.h>
77
Radek Krejci469aab82012-07-22 18:42:20 +020078#include <libnetconf.h>
79#include <libxml/tree.h>
80#include <libxml/parser.h>
81
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
86
87/* sleep in master process for non-blocking socket reading */
88#define SLEEP_TIME 200
89
90#ifndef offsetof
91#define offsetof(type, member) ((size_t) ((type *) 0)->member)
92#endif
93
94struct timeval timeout = { 1, 0 };
95
Radek Krejci8fd1f5e2012-07-24 17:33:36 +020096typedef enum MSG_TYPE {
97 REPLY_OK,
98 REPLY_DATA,
99 REPLY_ERROR,
Radek Krejci9e04c7b2012-07-26 15:54:25 +0200100 REPLY_INFO,
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200101 MSG_CONNECT,
102 MSG_DISCONNECT,
103 MSG_GET,
104 MSG_GETCONFIG,
105 MSG_EDITCONFIG,
106 MSG_COPYCONFIG,
107 MSG_DELETECONFIG,
108 MSG_LOCK,
109 MSG_UNLOCK,
Radek Krejci9e04c7b2012-07-26 15:54:25 +0200110 MSG_KILL,
111 MSG_INFO
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200112} MSG_TYPE;
113
Radek Krejci469aab82012-07-22 18:42:20 +0200114#define MSG_OK 0
115#define MSG_OPEN 1
116#define MSG_DATA 2
117#define MSG_CLOSE 3
118#define MSG_ERROR 4
119#define MSG_UNKNOWN 5
120
Radek Krejci469aab82012-07-22 18:42:20 +0200121module AP_MODULE_DECLARE_DATA netconf_module;
122
123typedef struct {
Radek Krejci469aab82012-07-22 18:42:20 +0200124 apr_pool_t *pool;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200125 apr_proc_t *forkproc;
126 char* sockname;
Radek Krejcif23850c2012-07-23 16:14:17 +0200127} mod_netconf_cfg;
Radek Krejci469aab82012-07-22 18:42:20 +0200128
129volatile int isterminated = 0;
130
131static char* password;
132
133
134static void signal_handler(int sign)
135{
136 switch (sign) {
137 case SIGTERM:
138 isterminated = 1;
Radek Krejci469aab82012-07-22 18:42:20 +0200139 break;
140 }
141}
142
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200143static char* gen_ncsession_hash( const char* hostname, const char* port, const char* sid)
Radek Krejci469aab82012-07-22 18:42:20 +0200144{
Radek Krejcif23850c2012-07-23 16:14:17 +0200145 unsigned char hash_raw[APR_SHA1_DIGESTSIZE];
146 int i;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200147 char* hash;
Radek Krejcif23850c2012-07-23 16:14:17 +0200148
Radek Krejci469aab82012-07-22 18:42:20 +0200149 apr_sha1_ctx_t sha1_ctx;
150 apr_sha1_init(&sha1_ctx);
151 apr_sha1_update(&sha1_ctx, hostname, strlen(hostname));
152 apr_sha1_update(&sha1_ctx, port, strlen(port));
153 apr_sha1_update(&sha1_ctx, sid, strlen(sid));
Radek Krejcif23850c2012-07-23 16:14:17 +0200154 apr_sha1_final(hash_raw, &sha1_ctx);
Radek Krejci469aab82012-07-22 18:42:20 +0200155
Radek Krejcif23850c2012-07-23 16:14:17 +0200156 /* convert binary hash into hex string, which is printable */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200157 hash = malloc(sizeof(char) * ((2*APR_SHA1_DIGESTSIZE)+1));
Radek Krejcif23850c2012-07-23 16:14:17 +0200158 for (i = 0; i < APR_SHA1_DIGESTSIZE; i++) {
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200159 snprintf(hash + (2*i), 3, "%02x", hash_raw[i]);
Radek Krejcif23850c2012-07-23 16:14:17 +0200160 }
161 //hash[2*APR_SHA1_DIGESTSIZE] = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200162
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200163 return (hash);
Radek Krejci469aab82012-07-22 18:42:20 +0200164}
165
166int netconf_callback_ssh_hostkey_check (const char* hostname, int keytype, const char* fingerprint)
167{
168 /* always approve */
169 return (EXIT_SUCCESS);
170}
171
172char* netconf_callback_sshauth_password (const char* username, const char* hostname)
173{
174 char* buf;
175
176 buf = malloc ((strlen(password) + 1) * sizeof(char));
177 apr_cpystrn(buf, password, strlen(password) + 1);
178
179 return (buf);
180}
181
182void netconf_callback_sshauth_interactive (const char* name,
183 int name_len,
184 const char* instruction,
185 int instruction_len,
186 int num_prompts,
187 const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts,
188 LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses,
189 void** abstract)
190{
191 int i;
192
193 for (i=0; i<num_prompts; i++) {
194 responses[i].text = malloc ((strlen(password) + 1) * sizeof(char));
195 apr_cpystrn(responses[i].text, password, strlen(password) + 1);
196 responses[i].length = strlen(responses[i].text) + 1;
197 }
198
199 return;
200}
201
Radek Krejcic11fd862012-07-26 12:41:21 +0200202static json_object *err_reply = NULL;
203void netconf_callback_error_process(const char* tag,
204 const char* type,
205 const char* severity,
206 const char* apptag,
207 const char* path,
208 const char* message,
209 const char* attribute,
210 const char* element,
211 const char* ns,
212 const char* sid)
213{
214 err_reply = json_object_new_object();
215 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
216 if (tag) json_object_object_add(err_reply, "error-tag", json_object_new_string(tag));
217 if (type) json_object_object_add(err_reply, "error-type", json_object_new_string(type));
218 if (severity) json_object_object_add(err_reply, "error-severity", json_object_new_string(severity));
219 if (apptag) json_object_object_add(err_reply, "error-app-tag", json_object_new_string(apptag));
220 if (path) json_object_object_add(err_reply, "error-path", json_object_new_string(path));
221 if (message) json_object_object_add(err_reply, "error-message", json_object_new_string(message));
222 if (attribute) json_object_object_add(err_reply, "bad-attribute", json_object_new_string(attribute));
223 if (element) json_object_object_add(err_reply, "bad-element", json_object_new_string(element));
224 if (ns) json_object_object_add(err_reply, "bad-namespace", json_object_new_string(ns));
225 if (sid) json_object_object_add(err_reply, "session-id", json_object_new_string(sid));
226}
227
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200228static 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)
Radek Krejci469aab82012-07-22 18:42:20 +0200229{
Radek Krejci469aab82012-07-22 18:42:20 +0200230 struct nc_session* session;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200231 char *sid;
Radek Krejcif23850c2012-07-23 16:14:17 +0200232 char *session_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200233
234 /* connect to the requested NETCONF server */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200235 password = (char*)pass;
236 session = nc_session_connect(host, (unsigned short) atoi (port), user, NULL);
Radek Krejci469aab82012-07-22 18:42:20 +0200237
238 /* if connected successful, add session to the list */
239 if (session != NULL) {
240 /* generate hash for the session */
241 sid = nc_session_get_id(session);
Radek Krejcic11fd862012-07-26 12:41:21 +0200242 session_key = gen_ncsession_hash(
243 (host==NULL) ? "localhost" : host,
244 (port==NULL) ? "830" : port,
245 sid);
Radek Krejci469aab82012-07-22 18:42:20 +0200246 free(sid);
Radek Krejcif23850c2012-07-23 16:14:17 +0200247 apr_hash_set(conns, apr_pstrdup(pool, session_key), APR_HASH_KEY_STRING, (void *) session);
Radek Krejci469aab82012-07-22 18:42:20 +0200248 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "NETCONF session established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200249 return (session_key);
Radek Krejci469aab82012-07-22 18:42:20 +0200250 } else {
251 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Connection could not be established");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200252 return (NULL);
Radek Krejci469aab82012-07-22 18:42:20 +0200253 }
254
Radek Krejci469aab82012-07-22 18:42:20 +0200255}
256
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200257static int netconf_close(server_rec* server, apr_hash_t* conns, char* session_key)
Radek Krejci469aab82012-07-22 18:42:20 +0200258{
259 struct nc_session *ns = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +0200260
Radek Krejcif23850c2012-07-23 16:14:17 +0200261 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Key in hash to get: %s", session_key);
Radek Krejci469aab82012-07-22 18:42:20 +0200262 ns = (struct nc_session *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
263 if (ns != NULL) {
264 nc_session_close (ns, "NETCONF session closed by client");
265 nc_session_free (ns);
266 ns = NULL;
267
268 /* remove session from the active sessions list */
269 apr_hash_set(conns, session_key, APR_HASH_KEY_STRING, NULL);
270 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "NETCONF session closed");
Radek Krejcif23850c2012-07-23 16:14:17 +0200271
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200272 return (EXIT_SUCCESS);
Radek Krejci469aab82012-07-22 18:42:20 +0200273 } else {
274 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown session to close");
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200275 return (EXIT_FAILURE);
Radek Krejci469aab82012-07-22 18:42:20 +0200276 }
277}
278
Radek Krejci8e4632a2012-07-26 13:40:34 +0200279static int netconf_op(server_rec* server, apr_hash_t* conns, char* session_key, nc_rpc* rpc)
Radek Krejci469aab82012-07-22 18:42:20 +0200280{
281 struct nc_session *session = NULL;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200282 nc_reply* reply;
283 int retval = EXIT_SUCCESS;
284
Radek Krejci8e4632a2012-07-26 13:40:34 +0200285 /* check requests */
286 if (rpc == NULL) {
287 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: rpc is not created");
288 return (EXIT_FAILURE);
289 }
290
291 /* get session where send the RPC */
Radek Krejci035bf4e2012-07-25 10:59:09 +0200292 session = (struct nc_session *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
293 if (session != NULL) {
Radek Krejci035bf4e2012-07-25 10:59:09 +0200294 /* send the request and get the reply */
295 nc_session_send_rpc (session, rpc);
Radek Krejci035bf4e2012-07-25 10:59:09 +0200296 if (nc_session_recv_reply (session, &reply) == 0) {
Radek Krejci035bf4e2012-07-25 10:59:09 +0200297 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
Radek Krejci035bf4e2012-07-25 10:59:09 +0200298 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: receiving rpc-reply failed");
Radek Krejci035bf4e2012-07-25 10:59:09 +0200299 netconf_close(server, conns, session_key);
Radek Krejci035bf4e2012-07-25 10:59:09 +0200300 return (EXIT_FAILURE);
301 }
302
303 /* there is error handled by callback */
304 return (EXIT_FAILURE);
305 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200306
307 switch (nc_reply_get_type (reply)) {
308 case NC_REPLY_OK:
309 retval = EXIT_SUCCESS;
310 break;
311 default:
312 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected rpc-reply");
313 retval = EXIT_FAILURE;
314 break;
315 }
316 nc_reply_free(reply);
317 return (retval);
318 } else {
319 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown session to process.");
320 return (EXIT_FAILURE);
321 }
322}
Radek Krejci8e4632a2012-07-26 13:40:34 +0200323static char* netconf_opdata(server_rec* server, apr_hash_t* conns, char* session_key, nc_rpc* rpc)
324{
325 struct nc_session *session = NULL;
326 nc_reply* reply;
327 char* data;
328
329 /* check requests */
330 if (rpc == NULL) {
331 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: rpc is not created");
332 return (NULL);
333 }
334
335 /* get session where send the RPC */
336 session = (struct nc_session *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
337 if (session != NULL) {
338 /* send the request and get the reply */
339 nc_session_send_rpc (session, rpc);
340 if (nc_session_recv_reply (session, &reply) == 0) {
341 nc_rpc_free (rpc);
342 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
343 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: receiving rpc-reply failed");
344 netconf_close(server, conns, session_key);
345 return (NULL);
346 }
347
348 /* there is error handled by callback */
349 return (NULL);
350 }
351 nc_rpc_free (rpc);
352
353 switch (nc_reply_get_type (reply)) {
354 case NC_REPLY_DATA:
355 if ((data = nc_reply_get_data (reply)) == NULL) {
356 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: no data from reply");
357 return (NULL);
358 }
359 break;
360 default:
361 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected rpc-reply");
362 return (NULL);
363 }
364 nc_reply_free(reply);
365
366 return (data);
367 } else {
368 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown session to process.");
369 return (NULL);
370 }
371}
372
373static char* netconf_getconfig(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE source, const char* filter)
374{
375 nc_rpc* rpc;
376 struct nc_filter *f = NULL;
377 char* data = NULL;
378
379 /* create filter if set */
380 if (filter != NULL) {
381 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
382 }
383
384 /* create requests */
385 rpc = nc_rpc_getconfig (source, f);
386 nc_filter_free(f);
387 if (rpc == NULL) {
388 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
389 return (NULL);
390 }
391
392 data = netconf_opdata(server, conns, session_key, rpc);
393 nc_rpc_free (rpc);
394 return (data);
395}
396
397static char* netconf_get(server_rec* server, apr_hash_t* conns, char* session_key, const char* filter)
398{
399 nc_rpc* rpc;
400 struct nc_filter *f = NULL;
401 char* data = NULL;
402
403 /* create filter if set */
404 if (filter != NULL) {
405 f = nc_filter_new(NC_FILTER_SUBTREE, filter);
406 }
407
408 /* create requests */
409 rpc = nc_rpc_get (f);
410 nc_filter_free(f);
411 if (rpc == NULL) {
412 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
413 return (NULL);
414 }
415
416 data = netconf_opdata(server, conns, session_key, rpc);
417 nc_rpc_free (rpc);
418 return (data);
419}
420
421static int netconf_copyconfig(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE source, NC_DATASTORE target, const char* config)
422{
423 nc_rpc* rpc;
424 int retval = EXIT_SUCCESS;
425
426 /* create requests */
427 rpc = nc_rpc_copyconfig(source, target, config);
428 if (rpc == NULL) {
429 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
430 return (EXIT_FAILURE);
431 }
432
433 retval = netconf_op(server, conns, session_key, rpc);
434 nc_rpc_free (rpc);
435 return (retval);
436}
Radek Krejci035bf4e2012-07-25 10:59:09 +0200437
Radek Krejci62ab34b2012-07-26 13:42:05 +0200438static int netconf_editconfig(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE target, NC_EDIT_DEFOP_TYPE defop, NC_EDIT_ERROPT_TYPE erropt, const char* config)
439{
440 nc_rpc* rpc;
441 int retval = EXIT_SUCCESS;
442
443 /* create requests */
444 rpc = nc_rpc_editconfig(target, defop, erropt, config);
445 if (rpc == NULL) {
446 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
447 return (EXIT_FAILURE);
448 }
449
450 retval = netconf_op(server, conns, session_key, rpc);
451 nc_rpc_free (rpc);
452 return (retval);
453}
454
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200455static int netconf_killsession(server_rec* server, apr_hash_t* conns, char* session_key, const char* sid)
456{
457 nc_rpc* rpc;
458 int retval = EXIT_SUCCESS;
459
460 /* create requests */
461 rpc = nc_rpc_killsession(sid);
462 if (rpc == NULL) {
463 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
464 return (EXIT_FAILURE);
465 }
466
467 retval = netconf_op(server, conns, session_key, rpc);
468 nc_rpc_free (rpc);
469 return (retval);
470}
471
Radek Krejci5cd7d422012-07-26 14:50:29 +0200472static int netconf_onlytargetop(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE target, nc_rpc* (*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +0200473{
474 nc_rpc* rpc;
475 int retval = EXIT_SUCCESS;
476
477 /* create requests */
Radek Krejci5cd7d422012-07-26 14:50:29 +0200478 rpc = op_func(target);
Radek Krejci2f318372012-07-26 14:22:35 +0200479 if (rpc == NULL) {
480 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
481 return (EXIT_FAILURE);
482 }
483
484 retval = netconf_op(server, conns, session_key, rpc);
485 nc_rpc_free (rpc);
486 return (retval);
487}
488
Radek Krejci5cd7d422012-07-26 14:50:29 +0200489static int netconf_deleteconfig(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE target)
490{
491 return (netconf_onlytargetop(server, conns, session_key, target, nc_rpc_deleteconfig));
492}
493
494static int netconf_lock(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE target)
495{
496 return (netconf_onlytargetop(server, conns, session_key, target, nc_rpc_lock));
497}
498
499static int netconf_unlock(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE target)
500{
501 return (netconf_onlytargetop(server, conns, session_key, target, nc_rpc_unlock));
502}
503
Radek Krejci469aab82012-07-22 18:42:20 +0200504server_rec* clb_print_server;
505int clb_print(const char* msg)
506{
507 ap_log_error(APLOG_MARK, APLOG_INFO, 0, clb_print_server, msg);
508 return (0);
509}
510
Radek Krejcif23850c2012-07-23 16:14:17 +0200511/*
512 * This is actually implementation of NETCONF client
513 * - requests are received from UNIX socket in the predefined format
514 * - results are replied through the same way
515 * - the daemon run as a separate process, but it is started and stopped
516 * automatically by Apache.
517 *
518 */
Radek Krejci469aab82012-07-22 18:42:20 +0200519static void forked_proc(apr_pool_t * pool, server_rec * server)
520{
521 struct sockaddr_un local, remote;
522 int lsock, client;
523 socklen_t len, len2;
524 struct pollfd fds;
525 int status;
Radek Krejciae021c12012-07-25 18:03:52 +0200526 mod_netconf_cfg *cfg;
Radek Krejci9e04c7b2012-07-26 15:54:25 +0200527 json_object *request, *reply, *json_obj;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200528 int operation;
529 char* session_key, *data;
530 const char *msgtext;
531 const char *host, *port, *user, *pass;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200532 const char *target, *source, *filter, *config, *defop, *erropt, *sid;
Radek Krejci9e04c7b2012-07-26 15:54:25 +0200533 struct nc_session *session = NULL;
534 struct nc_cpblts* cpblts;
Radek Krejcifa891e72012-07-26 14:04:27 +0200535 NC_DATASTORE ds_type_s, ds_type_t;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200536 NC_EDIT_DEFOP_TYPE defop_type = 0;
537 NC_EDIT_ERROPT_TYPE erropt_type = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200538
539 apr_hash_t *netconf_sessions_list;
540 char buffer[BUFFER_SIZE];
Radek Krejci469aab82012-07-22 18:42:20 +0200541
Radek Krejcif23850c2012-07-23 16:14:17 +0200542 /* change uid and gid of process for security reasons */
Radek Krejci469aab82012-07-22 18:42:20 +0200543 unixd_setup_child();
544
Radek Krejciae021c12012-07-25 18:03:52 +0200545 cfg = ap_get_module_config(server->module_config, &netconf_module);
546 if (cfg == NULL) {
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200547 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Getting mod_netconf configuration failed");
548 return;
549 }
Radek Krejci469aab82012-07-22 18:42:20 +0200550
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200551 /* create listening UNIX socket to accept incoming connections */
Radek Krejci469aab82012-07-22 18:42:20 +0200552 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
553 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Creating socket failed (%s)", strerror(errno));
554 return;
555 }
556
557 local.sun_family = AF_UNIX;
Radek Krejciae021c12012-07-25 18:03:52 +0200558 strncpy(local.sun_path, cfg->sockname, sizeof(local.sun_path));
Radek Krejci469aab82012-07-22 18:42:20 +0200559 unlink(local.sun_path);
560 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
561
562 if (bind(lsock, (struct sockaddr *) &local, len) == -1) {
563 if (errno == EADDRINUSE) {
564 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "mod_netconf socket address already in use");
565 close(lsock);
566 exit(0);
567 }
568 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Binding socket failed (%s)", strerror(errno));
569 close(lsock);
570 return;
571 }
572
573 if (listen(lsock, MAX_SOCKET_CL) == -1) {
574 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Setting up listen socket failed (%s)", strerror(errno));
575 close(lsock);
576 return;
577 }
578
579 /* prepare internal lists */
580 netconf_sessions_list = apr_hash_make(pool);
581
582 /* setup libnetconf's callbacks */
583 nc_verbosity(NC_VERB_DEBUG);
584 clb_print_server = server;
585 nc_callback_print(clb_print);
586 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
587 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
588 nc_callback_sshauth_password(netconf_callback_sshauth_password);
Radek Krejcic11fd862012-07-26 12:41:21 +0200589 nc_callback_error_reply(netconf_callback_error_process);
Radek Krejci469aab82012-07-22 18:42:20 +0200590
591 /* disable publickey authentication */
592 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
593
594 while (isterminated == 0) {
595 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "waiting for another client's request");
596
597 /* open incoming connection if any */
598 len2 = sizeof(remote);
599 client = accept(lsock, (struct sockaddr *) &remote, &len2);
600 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
601 apr_sleep(SLEEP_TIME);
602 continue;
603 } else if (client == -1 && (errno == EINTR)) {
604 continue;
605 } else if (client == -1) {
606 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Accepting mod_netconf client connection failed (%s)", strerror(errno));
607 continue;
608 }
609 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "client's socket accepted.");
610
611 /* set client's socket as non-blocking */
Radek Krejcif23850c2012-07-23 16:14:17 +0200612 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +0200613
614 while (1) {
615 fds.fd = client;
616 fds.events = POLLIN;
617 fds.revents = 0;
618
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200619 status = poll(&fds, 1, 1000);
Radek Krejci469aab82012-07-22 18:42:20 +0200620
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200621 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
Radek Krejci469aab82012-07-22 18:42:20 +0200622 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200623 //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "poll interrupted");
Radek Krejci469aab82012-07-22 18:42:20 +0200624 continue;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200625 } else if (status < 0) {
Radek Krejci469aab82012-07-22 18:42:20 +0200626 /* 0: poll time outed
627 * close socket and ignore this request from the client, it can try it again
628 * -1: poll failed
629 * something wrong happend, close this socket and wait for another request
630 */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200631 //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "poll failed, status %d(%d: %s)", status, errno, strerror(errno));
Radek Krejci469aab82012-07-22 18:42:20 +0200632 close(client);
633 break;
634 }
635 /* status > 0 */
636
637 /* check the status of the socket */
638
639 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
640 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
641 /* close client's socket (it's probably already closed by client */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200642 //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "socket error (%d)", fds.revents);
Radek Krejci469aab82012-07-22 18:42:20 +0200643 close(client);
644 break;
645 }
646
647 if ((len2 = recv(client, buffer, BUFFER_SIZE, 0)) <= 0) {
648 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "receiving failed %d (%s)", errno, strerror(errno));
649 continue;
650 } else {
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200651 request = json_tokener_parse(buffer);
652 operation = json_object_get_int(json_object_object_get(request, "type"));
Radek Krejci469aab82012-07-22 18:42:20 +0200653
Radek Krejci035bf4e2012-07-25 10:59:09 +0200654 session_key = (char*) json_object_get_string(json_object_object_get(request, "session"));
655 /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
656 if (operation != MSG_CONNECT && session_key == NULL) {
657 reply = json_object_new_object();
658 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
659 json_object_object_add(reply, "error-message", json_object_new_string("Missing session specification."));
660 msgtext = json_object_to_json_string(reply);
661 send(client, msgtext, strlen(msgtext) + 1, 0);
662 json_object_put(reply);
663 /* there is some stupid client, so close the connection to give a chance to some other client */
664 close(client);
665 break;
666 }
667
Radek Krejcifa891e72012-07-26 14:04:27 +0200668 /* get parameters */
669 ds_type_t = -1;
670 if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
671 if (strcmp(target, "running") == 0) {
672 ds_type_t = NC_DATASTORE_RUNNING;
673 } else if (strcmp(target, "startup") == 0) {
674 ds_type_t = NC_DATASTORE_STARTUP;
675 } else if (strcmp(target, "candidate") == 0) {
676 ds_type_t = NC_DATASTORE_CANDIDATE;
677 }
678 }
679 ds_type_s = -1;
680 if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
681 if (strcmp(source, "running") == 0) {
682 ds_type_s = NC_DATASTORE_RUNNING;
683 } else if (strcmp(source, "startup") == 0) {
684 ds_type_s = NC_DATASTORE_STARTUP;
685 } else if (strcmp(source, "candidate") == 0) {
686 ds_type_s = NC_DATASTORE_CANDIDATE;
687 }
688 }
689
Radek Krejcic11fd862012-07-26 12:41:21 +0200690 /* null global JSON error-reply */
691 err_reply = NULL;
692
Radek Krejci9e04c7b2012-07-26 15:54:25 +0200693 /* prepare reply envelope */
694 reply = json_object_new_object();
695
Radek Krejcic11fd862012-07-26 12:41:21 +0200696 /* process required operation */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200697 switch (operation) {
698 case MSG_CONNECT:
699 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: Connect");
700
701 host = json_object_get_string(json_object_object_get(request, "host"));
702 port = json_object_get_string(json_object_object_get(request, "port"));
703 user = json_object_get_string(json_object_object_get(request, "user"));
704 pass = json_object_get_string(json_object_object_get(request, "pass"));
705 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "host: %s, port: %s, user: %s", host, port, user);
706 session_key = netconf_connect(server, pool, netconf_sessions_list, host, port, user, pass);
707 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "hash: %s", session_key);
708
709 reply = json_object_new_object();
710 if (session_key == NULL) {
711 /* negative reply */
Radek Krejcic11fd862012-07-26 12:41:21 +0200712 if (err_reply == NULL) {
713 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
714 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
715 } else {
716 /* use filled err_reply from libnetconf's callback */
717 json_object_put(reply);
718 reply = err_reply;
719 }
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200720 } else {
721 /* positive reply */
722 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
723 json_object_object_add(reply, "session", json_object_new_string(session_key));
Radek Krejcie31ad212012-07-26 12:51:15 +0200724
725 free(session_key);
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200726 }
727
Radek Krejci469aab82012-07-22 18:42:20 +0200728 break;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200729 case MSG_GET:
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200730 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get-config (session %s)", session_key);
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200731
732 filter = json_object_get_string(json_object_object_get(request, "filter"));
733
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200734 if ((data = netconf_get(server, netconf_sessions_list, session_key, filter)) == NULL) {
Radek Krejcic11fd862012-07-26 12:41:21 +0200735 if (err_reply == NULL) {
736 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
737 json_object_object_add(reply, "error-message", json_object_new_string("get failed."));
738 } else {
739 /* use filled err_reply from libnetconf's callback */
740 json_object_put(reply);
741 reply = err_reply;
742 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200743 } else {
744 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
745 json_object_object_add(reply, "data", json_object_new_string(data));
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200746 }
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200747 break;
748 case MSG_GETCONFIG:
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200749 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get-config (session %s)", session_key);
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200750
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200751 filter = json_object_get_string(json_object_object_get(request, "filter"));
752
Radek Krejcifa891e72012-07-26 14:04:27 +0200753 if (ds_type_s == -1) {
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200754 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
Radek Krejci035bf4e2012-07-25 10:59:09 +0200755 json_object_object_add(reply, "error-message", json_object_new_string("Invalid source repository type requested."));
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200756 break;
757 }
758
Radek Krejcifa891e72012-07-26 14:04:27 +0200759 if ((data = netconf_getconfig(server, netconf_sessions_list, session_key, ds_type_s, filter)) == NULL) {
Radek Krejcic11fd862012-07-26 12:41:21 +0200760 if (err_reply == NULL) {
761 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
762 json_object_object_add(reply, "error-message", json_object_new_string("get-config failed."));
763 } else {
764 /* use filled err_reply from libnetconf's callback */
765 json_object_put(reply);
766 reply = err_reply;
767 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200768 } else {
769 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
770 json_object_object_add(reply, "data", json_object_new_string(data));
771 }
772 break;
Radek Krejci62ab34b2012-07-26 13:42:05 +0200773 case MSG_EDITCONFIG:
774 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: edit-config (session %s)", session_key);
775
Radek Krejci62ab34b2012-07-26 13:42:05 +0200776 defop = json_object_get_string(json_object_object_get(request, "default-operation"));
777 if (defop != NULL) {
778 if (strcmp(defop, "merge") == 0) {
779 defop_type = NC_EDIT_DEFOP_MERGE;
780 } else if (strcmp(defop, "replace") == 0) {
781 defop_type = NC_EDIT_DEFOP_REPLACE;
782 } else if (strcmp(defop, "none") == 0) {
783 defop_type = NC_EDIT_DEFOP_NONE;
784 } else {
785 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
786 json_object_object_add(reply, "error-message", json_object_new_string("Invalid default-operation parameter."));
787 break;
788 }
789 } else {
790 defop_type = 0;
791 }
792
793 erropt = json_object_get_string(json_object_object_get(request, "error-option"));
794 if (erropt != NULL) {
795 if (strcmp(erropt, "continue-on-error") == 0) {
796 erropt_type = NC_EDIT_ERROPT_CONT;
797 } else if (strcmp(erropt, "stop-on-error") == 0) {
798 erropt_type = NC_EDIT_ERROPT_STOP;
799 } else if (strcmp(erropt, "rollback-on-error") == 0) {
800 erropt_type = NC_EDIT_ERROPT_ROLLBACK;
801 } else {
802 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
803 json_object_object_add(reply, "error-message", json_object_new_string("Invalid error-option parameter."));
804 break;
805 }
806 } else {
807 erropt_type = 0;
808 }
809
Radek Krejcifa891e72012-07-26 14:04:27 +0200810 if (ds_type_t == -1) {
Radek Krejci62ab34b2012-07-26 13:42:05 +0200811 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
812 json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
813 break;
814 }
815
816 config = json_object_get_string(json_object_object_get(request, "config"));
817 if (config == NULL) {
818 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
819 json_object_object_add(reply, "error-message", json_object_new_string("Invalid config data parameter."));
820 break;
821 }
822
Radek Krejcifa891e72012-07-26 14:04:27 +0200823 if (netconf_editconfig(server, netconf_sessions_list, session_key, ds_type_t, defop_type, erropt_type, config) != EXIT_SUCCESS) {
Radek Krejci62ab34b2012-07-26 13:42:05 +0200824 if (err_reply == NULL) {
825 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
826 json_object_object_add(reply, "error-message", json_object_new_string("edit-config failed."));
827 } else {
828 /* use filled err_reply from libnetconf's callback */
829 json_object_put(reply);
830 reply = err_reply;
831 }
832 } else {
833 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
834 }
835 break;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200836 case MSG_COPYCONFIG:
837 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: copy-config (session %s)", session_key);
Radek Krejcicebb7af2012-07-26 14:58:12 +0200838 config = NULL;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200839
Radek Krejcifa891e72012-07-26 14:04:27 +0200840 if (source == NULL) {
841 /* no explicit source specified -> use config data */
842 ds_type_s = NC_DATASTORE_NONE;
Radek Krejciae021c12012-07-25 18:03:52 +0200843 config = json_object_get_string(json_object_object_get(request, "config"));
Radek Krejcifa891e72012-07-26 14:04:27 +0200844 } else if (ds_type_s == -1) {
845 /* source datastore specified, but it is invalid */
846 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
847 json_object_object_add(reply, "error-message", json_object_new_string("Invalid source repository type requested."));
848 break;
Radek Krejci035bf4e2012-07-25 10:59:09 +0200849 }
Radek Krejciae021c12012-07-25 18:03:52 +0200850
Radek Krejcifa891e72012-07-26 14:04:27 +0200851 if (ds_type_t == -1) {
852 /* invalid target datastore specified */
Radek Krejci035bf4e2012-07-25 10:59:09 +0200853 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
854 json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200855 break;
856 }
857
Radek Krejcifa891e72012-07-26 14:04:27 +0200858 if (source == NULL && config == NULL) {
Radek Krejci035bf4e2012-07-25 10:59:09 +0200859 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
Radek Krejcifa891e72012-07-26 14:04:27 +0200860 json_object_object_add(reply, "error-message", json_object_new_string("invalid input parameters - one of source and config is required."));
Radek Krejci035bf4e2012-07-25 10:59:09 +0200861 } else {
Radek Krejcifa891e72012-07-26 14:04:27 +0200862 if (netconf_copyconfig(server, netconf_sessions_list, session_key, ds_type_s, ds_type_t, config) != EXIT_SUCCESS) {
Radek Krejcic11fd862012-07-26 12:41:21 +0200863 if (err_reply == NULL) {
864 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
865 json_object_object_add(reply, "error-message", json_object_new_string("copy-config failed."));
866 } else {
867 /* use filled err_reply from libnetconf's callback */
868 json_object_put(reply);
869 reply = err_reply;
870 }
Radek Krejciae021c12012-07-25 18:03:52 +0200871 } else {
872 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
873 }
Radek Krejci035bf4e2012-07-25 10:59:09 +0200874 }
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200875 break;
Radek Krejci2f318372012-07-26 14:22:35 +0200876 case MSG_DELETECONFIG:
877 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: delete-config (session %s)", session_key);
Radek Krejci5cd7d422012-07-26 14:50:29 +0200878 /* no break - unifying code */
879 case MSG_LOCK:
880 if (operation == MSG_LOCK) {
881 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: lock (session %s)", session_key);
882 }
883 /* no break - unifying code */
884 case MSG_UNLOCK:
885 if (operation == MSG_UNLOCK) {
886 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: unlock (session %s)", session_key);
887 }
Radek Krejci2f318372012-07-26 14:22:35 +0200888
Radek Krejci2f318372012-07-26 14:22:35 +0200889 if (ds_type_t == -1) {
890 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
891 json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
892 break;
893 }
894
Radek Krejci5cd7d422012-07-26 14:50:29 +0200895 switch(operation) {
896 case MSG_DELETECONFIG:
897 status = netconf_deleteconfig(server, netconf_sessions_list, session_key, ds_type_t);
898 break;
899 case MSG_LOCK:
900 status = netconf_lock(server, netconf_sessions_list, session_key, ds_type_t);
901 break;
902 case MSG_UNLOCK:
903 status = netconf_unlock(server, netconf_sessions_list, session_key, ds_type_t);
904 break;
905 default:
906 status = -1;
907 break;
908 }
909
910 if (status != EXIT_SUCCESS) {
Radek Krejci2f318372012-07-26 14:22:35 +0200911 if (err_reply == NULL) {
912 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
Radek Krejci5cd7d422012-07-26 14:50:29 +0200913 json_object_object_add(reply, "error-message", json_object_new_string("operation failed."));
Radek Krejci2f318372012-07-26 14:22:35 +0200914 } else {
915 /* use filled err_reply from libnetconf's callback */
916 json_object_put(reply);
917 reply = err_reply;
918 }
919 } else {
920 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
921 }
922 break;
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200923 case MSG_KILL:
924 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: kill-session, session %s", session_key);
925
926 sid = json_object_get_string(json_object_object_get(request, "session-id"));
927
Radek Krejcie34d3eb2012-07-26 15:05:53 +0200928 if (sid == NULL) {
929 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
930 json_object_object_add(reply, "error-message", json_object_new_string("Missing session-id parameter."));
931 break;
932 }
933
934 if (netconf_killsession(server, netconf_sessions_list, session_key, sid) != EXIT_SUCCESS) {
935 if (err_reply == NULL) {
936 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
937 json_object_object_add(reply, "error-message", json_object_new_string("kill-session failed."));
938 } else {
939 /* use filled err_reply from libnetconf's callback */
940 json_object_put(reply);
941 reply = err_reply;
942 }
943 } else {
944 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
945 }
946 break;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200947 case MSG_DISCONNECT:
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200948 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: Disconnect session %s", session_key);
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200949
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200950 if (netconf_close(server, netconf_sessions_list, session_key) != EXIT_SUCCESS) {
Radek Krejcic11fd862012-07-26 12:41:21 +0200951 if (err_reply == NULL) {
952 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
953 json_object_object_add(reply, "error-message", json_object_new_string("Invalid session identifier."));
954 } else {
955 /* use filled err_reply from libnetconf's callback */
956 json_object_put(reply);
957 reply = err_reply;
958 }
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200959 } else {
960 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
961 }
962 break;
Radek Krejci9e04c7b2012-07-26 15:54:25 +0200963 case MSG_INFO:
964 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get info about session %s", session_key);
965
966 session = (struct nc_session *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
967 if (session != NULL) {
968 json_object_object_add(reply, "sid", json_object_new_string(data = nc_session_get_id(session)));
969 if (data) free(data);
970 json_object_object_add(reply, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
971 json_object_object_add(reply, "host", json_object_new_string(data = nc_session_get_host(session)));
972 if (data) free(data);
973 json_object_object_add(reply, "port", json_object_new_string(data = nc_session_get_port(session)));
974 if (data) free(data);
975 json_object_object_add(reply, "user", json_object_new_string(data = nc_session_get_user(session)));
976 if (data) free(data);
977 cpblts = nc_session_get_cpblts (session);
978 if (cpblts != NULL) {
979 json_obj = json_object_new_array();
980 nc_cpblts_iter_start (cpblts);
981 while ((data = nc_cpblts_iter_next (cpblts)) != NULL) {
982 json_object_array_add(json_obj, json_object_new_string(data));
983 free (data);
984 }
985 json_object_object_add(reply, "capabilities", json_obj);
986 }
987 } else {
988 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
989 json_object_object_add(reply, "error-message", json_object_new_string("Invalid session identifier."));
990 }
991
992 break;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +0200993 default:
994 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown mod_netconf operation requested (%d)", operation);
995 reply = json_object_new_object();
996 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
997 json_object_object_add(reply, "error-message", json_object_new_string("Operation not supported."));
998 break;
999 }
1000 json_object_put(request);
1001
1002 /* send reply to caller */
1003 if (reply != NULL) {
1004 msgtext = json_object_to_json_string(reply);
1005 send(client, msgtext, strlen(msgtext) + 1, 0);
1006 json_object_put(reply);
1007 } else {
1008 break;
1009 }
Radek Krejci469aab82012-07-22 18:42:20 +02001010 }
1011 }
1012 }
1013
1014 close(lsock);
1015
1016 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Exiting from the mod_netconf daemon");
1017
1018 exit(APR_SUCCESS);
1019}
1020
Radek Krejcif23850c2012-07-23 16:14:17 +02001021static void *mod_netconf_create_conf(apr_pool_t * pool, server_rec * s)
Radek Krejci469aab82012-07-22 18:42:20 +02001022{
Radek Krejcif23850c2012-07-23 16:14:17 +02001023 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Init netconf module config");
1024
1025 mod_netconf_cfg *config = apr_pcalloc(pool, sizeof(mod_netconf_cfg));
1026 apr_pool_create(&config->pool, pool);
1027 config->forkproc = NULL;
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02001028 config->sockname = SOCKET_FILENAME;
Radek Krejcif23850c2012-07-23 16:14:17 +02001029
1030 return (void *)config;
Radek Krejci469aab82012-07-22 18:42:20 +02001031}
1032
1033static int mod_netconf_master_init(apr_pool_t * pconf, apr_pool_t * ptemp,
1034 apr_pool_t * plog, server_rec * s)
1035{
Radek Krejcif23850c2012-07-23 16:14:17 +02001036 mod_netconf_cfg *config;
1037 apr_status_t res;
1038
Radek Krejci469aab82012-07-22 18:42:20 +02001039 /* These two help ensure that we only init once. */
1040 void *data;
Radek Krejcif23850c2012-07-23 16:14:17 +02001041 const char *userdata_key = "netconf_ipc_init";
Radek Krejci469aab82012-07-22 18:42:20 +02001042
1043 /*
1044 * The following checks if this routine has been called before.
1045 * This is necessary because the parent process gets initialized
1046 * a couple of times as the server starts up.
1047 */
1048 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1049 if (!data) {
1050 apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
1051 return (OK);
1052 }
1053
Radek Krejcif23850c2012-07-23 16:14:17 +02001054 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "creating mod_netconf daemon");
1055 config = ap_get_module_config(s->module_config, &netconf_module);
Radek Krejcidfaa6ea2012-07-23 09:04:43 +02001056
Radek Krejcif23850c2012-07-23 16:14:17 +02001057 if (config && config->forkproc == NULL) {
1058 config->forkproc = apr_pcalloc(config->pool, sizeof(apr_proc_t));
1059 res = apr_proc_fork(config->forkproc, config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02001060 switch (res) {
1061 case APR_INCHILD:
1062 /* set signal handler */
Radek Krejcif23850c2012-07-23 16:14:17 +02001063 apr_signal_init(config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +02001064 apr_signal(SIGTERM, signal_handler);
1065
1066 /* log start of the separated NETCONF communication process */
Radek Krejcif23850c2012-07-23 16:14:17 +02001067 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "mod_netconf daemon started (PID %d)", getpid());
Radek Krejci469aab82012-07-22 18:42:20 +02001068
1069 /* start main loop providing NETCONF communication */
Radek Krejcif23850c2012-07-23 16:14:17 +02001070 forked_proc(config->pool, s);
Radek Krejci469aab82012-07-22 18:42:20 +02001071
Radek Krejcif23850c2012-07-23 16:14:17 +02001072 /* I never should be here, wtf?!? */
1073 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_netconf daemon unexpectedly stopped");
Radek Krejci469aab82012-07-22 18:42:20 +02001074 exit(APR_EGENERAL);
1075 break;
1076 case APR_INPARENT:
1077 /* register child to be killed (SIGTERM) when the module config's pool dies */
Radek Krejcif23850c2012-07-23 16:14:17 +02001078 apr_pool_note_subprocess(config->pool, config->forkproc, APR_KILL_AFTER_TIMEOUT);
Radek Krejci469aab82012-07-22 18:42:20 +02001079 break;
1080 default:
1081 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "apr_proc_fork() failed");
1082 break;
1083 }
Radek Krejcif23850c2012-07-23 16:14:17 +02001084 } else {
1085 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_netconf misses configuration structure");
Radek Krejci469aab82012-07-22 18:42:20 +02001086 }
1087
1088 return OK;
1089}
1090
Radek Krejci469aab82012-07-22 18:42:20 +02001091/**
1092 * Register module hooks
1093 */
1094static void mod_netconf_register_hooks(apr_pool_t * p)
1095{
Radek Krejcif23850c2012-07-23 16:14:17 +02001096 ap_hook_post_config(mod_netconf_master_init, NULL, NULL, APR_HOOK_LAST);
Radek Krejci469aab82012-07-22 18:42:20 +02001097}
1098
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02001099static const char* cfg_set_socket_path(cmd_parms* cmd, void* cfg, const char* arg)
1100{
1101 ((mod_netconf_cfg*)cfg)->sockname = apr_pstrdup(cmd->pool, arg);
1102 return NULL;
1103}
1104
1105static const command_rec netconf_cmds[] = {
1106 AP_INIT_TAKE1("NetconfSocket", cfg_set_socket_path, NULL, OR_ALL, "UNIX socket path for mod_netconf communication."),
1107 {NULL}
1108};
1109
Radek Krejci469aab82012-07-22 18:42:20 +02001110/* Dispatch list for API hooks */
1111module AP_MODULE_DECLARE_DATA netconf_module = {
1112 STANDARD20_MODULE_STUFF,
1113 NULL, /* create per-dir config structures */
1114 NULL, /* merge per-dir config structures */
Radek Krejcif23850c2012-07-23 16:14:17 +02001115 mod_netconf_create_conf, /* create per-server config structures */
Radek Krejci469aab82012-07-22 18:42:20 +02001116 NULL, /* merge per-server config structures */
Radek Krejci8fd1f5e2012-07-24 17:33:36 +02001117 netconf_cmds, /* table of config file commands */
Radek Krejci469aab82012-07-22 18:42:20 +02001118 mod_netconf_register_hooks /* register hooks */
1119};