mod_netconf: Every request is processed by new thread. Added session locking.
diff --git a/src/mod_netconf.c b/src/mod_netconf.c
index ca62efa..ceff507 100644
--- a/src/mod_netconf.c
+++ b/src/mod_netconf.c
@@ -48,6 +48,8 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <pthread.h>
+#include <ctype.h>
#include <unixd.h>
#include <httpd.h>
@@ -112,6 +114,20 @@
char* sockname;
} mod_netconf_cfg;
+struct pass_to_thread {
+ int client; /**< opened socket */
+ apr_pool_t * pool; /**< ?? */
+ server_rec * server; /**< ?? */
+ apr_hash_t * netconf_sessions_list; /**< ?? */
+};
+
+struct session_with_mutex {
+ struct nc_session * session; /**< netconf session */
+ pthread_mutex_t lock; /**< mutex protecting the session from multiple access */
+};
+
+pthread_rwlock_t session_lock; /**< mutex protecting netconf_session_list from multiple access errors */
+
volatile int isterminated = 0;
static char* password;
@@ -214,11 +230,17 @@
static 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_session* session;
+ struct session_with_mutex * locked_session;
char *session_key;
+ struct nc_cpblts * cpblts = nc_session_get_cpblts_default ();
+
+ nc_cpblts_add(cpblts, "urn:cesnet:tmc:netopeer:1.0");
/* connect to the requested NETCONF server */
password = (char*)pass;
- session = nc_session_connect(host, (unsigned short) atoi (port), user, NULL);
+ session = nc_session_connect(host, (unsigned short) atoi (port), user, cpblts);
+
+ nc_cpblts_free (cpblts);
/* if connected successful, add session to the list */
if (session != NULL) {
@@ -227,7 +249,28 @@
(host==NULL) ? "localhost" : host,
(port==NULL) ? "830" : port,
nc_session_get_id(session));
- apr_hash_set(conns, apr_pstrdup(pool, session_key), APR_HASH_KEY_STRING, (void *) session);
+
+ if ((locked_session = malloc (sizeof (struct session_with_mutex))) == NULL || pthread_mutex_init (&locked_session->lock, NULL) != 0) {
+ nc_session_free(session);
+ free (locked_session);
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
+ return NULL;
+ }
+ locked_session->session = session;
+ pthread_mutex_init (&locked_session->lock, NULL);
+ /* get exclusive access to sessions_list (conns) */
+ if (pthread_rwlock_wrlock (&session_lock) != 0) {
+ nc_session_free(session);
+ free (locked_session);
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
+ return NULL;
+ }
+ apr_hash_set(conns, apr_pstrdup(pool, session_key), APR_HASH_KEY_STRING, (void *) locked_session);
+ /* end of critical section */
+ if (pthread_rwlock_unlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
+ return NULL;
+ }
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "NETCONF session established");
return (session_key);
} else {
@@ -240,9 +283,20 @@
static int netconf_close(server_rec* server, apr_hash_t* conns, const char* session_key)
{
struct nc_session *ns = NULL;
+ struct session_with_mutex * locked_session;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Key in hash to get: %s", session_key);
- ns = (struct nc_session *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
+ /* get exclusive (write) access to sessions_list (conns) */
+ if (pthread_rwlock_wrlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ locked_session = (struct session_with_mutex *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
+ if (locked_session != NULL) {
+ pthread_mutex_destroy(&locked_session->lock);
+ ns = locked_session->session;
+ free (locked_session);
+ }
if (ns != NULL) {
nc_session_close (ns, "NETCONF session closed by client");
nc_session_free (ns);
@@ -250,6 +304,11 @@
/* remove session from the active sessions list */
apr_hash_set(conns, session_key, APR_HASH_KEY_STRING, NULL);
+ /* end of critical section */
+ if (pthread_rwlock_unlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
+ return EXIT_FAILURE;
+ }
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "NETCONF session closed");
return (EXIT_SUCCESS);
@@ -262,6 +321,7 @@
static int netconf_op(server_rec* server, apr_hash_t* conns, const char* session_key, nc_rpc* rpc)
{
struct nc_session *session = NULL;
+ struct session_with_mutex * locked_session;
nc_reply* reply;
int retval = EXIT_SUCCESS;
@@ -271,21 +331,58 @@
return (EXIT_FAILURE);
}
+ /* get non-exclusive (read) access to sessions_list (conns) */
+ if (pthread_rwlock_rdlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
+ return EXIT_FAILURE;
+ }
/* get session where send the RPC */
- session = (struct nc_session *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
+ locked_session = (struct session_with_mutex *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
+ if (locked_session != NULL) {
+ session = locked_session->session;
+ }
if (session != NULL) {
+ /* get exclusive access to session */
+ if (pthread_mutex_lock(&locked_session->lock) != 0) {
+ /* unlock before returning error */
+ if (pthread_rwlock_unlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ return EXIT_FAILURE;
+ }
/* send the request and get the reply */
nc_session_send_rpc (session, rpc);
if (nc_session_recv_reply (session, &reply) == 0) {
if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: receiving rpc-reply failed");
+ /* first release exclusive lock for this session */
+ pthread_mutex_unlock(&locked_session->lock);
+ /* release read lock, netconf_close will get exclusive access and close this session */
+ if (pthread_rwlock_unlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
+ return EXIT_FAILURE;
+ }
netconf_close(server, conns, session_key);
return (EXIT_FAILURE);
}
-
+ /* first release exclusive lock for this session */
+ pthread_mutex_unlock(&locked_session->lock);
+ /* unlock before returning error */
+ if (pthread_rwlock_unlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
+ return EXIT_FAILURE;
+ }
/* there is error handled by callback */
return (EXIT_FAILURE);
}
+ /* first release exclusive lock for this session */
+ pthread_mutex_unlock(&locked_session->lock);
+ /* end of critical section */
+ if (pthread_rwlock_unlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
+ return EXIT_FAILURE;
+ }
switch (nc_reply_get_type (reply)) {
case NC_REPLY_OK:
@@ -307,6 +404,7 @@
static char* netconf_opdata(server_rec* server, apr_hash_t* conns, const char* session_key, nc_rpc* rpc)
{
struct nc_session *session = NULL;
+ struct session_with_mutex * locked_session;
nc_reply* reply;
char* data;
@@ -316,21 +414,58 @@
return (NULL);
}
+ /* get non-exclusive (read) access to sessions_list (conns) */
+ if (pthread_rwlock_rdlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
+ return NULL;
+ }
/* get session where send the RPC */
- session = (struct nc_session *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
+ locked_session = (struct session_with_mutex *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
+ if (locked_session != NULL) {
+ session = locked_session->session;
+ }
if (session != NULL) {
+ /* get exclusive access to session */
+ if (pthread_mutex_lock(&locked_session->lock) != 0) {
+ /* unlock before returning error */
+ if (pthread_rwlock_unlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while locking rwlock: %d (%s)", errno, strerror(errno));
+ return NULL;
+ }
+ return NULL;
+ }
/* send the request and get the reply */
nc_session_send_rpc (session, rpc);
if (nc_session_recv_reply (session, &reply) == 0) {
if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: receiving rpc-reply failed");
+ /* first release exclusive lock for this session */
+ pthread_mutex_unlock(&locked_session->lock);
+ /* release read lock, netconf_close will get exclusive access and close this session */
+ if (pthread_rwlock_unlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
+ return NULL;
+ }
netconf_close(server, conns, session_key);
return (NULL);
}
-
+ /* first release exclusive lock for this session */
+ pthread_mutex_unlock(&locked_session->lock);
+ /* unlock before returning error */
+ if (pthread_rwlock_unlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
+ return NULL;
+ }
/* there is error handled by callback */
return (NULL);
}
+ /* first release exclusive lock for this session */
+ pthread_mutex_unlock(&locked_session->lock);
+ /* end of critical section */
+ if (pthread_rwlock_unlock (&session_lock) != 0) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
+ return NULL;
+ }
switch (nc_reply_get_type (reply)) {
case NC_REPLY_DATA:
@@ -566,6 +701,494 @@
}
}
+void * thread_routine (void * arg)
+{
+ void * retval = NULL;
+
+ ssize_t buffer_len;
+ struct pollfd fds;
+ int status, buffer_size, ret;
+ json_object *request, *reply, *json_obj;
+ int operation;
+ int i, chunk_len;
+ char* session_key, *data;
+ const char *host, *port, *user, *pass;
+ const char *msgtext, *cpbltstr;
+ const char *target, *source, *filter, *config, *defop, *erropt, *sid;
+ struct nc_session *session = NULL;
+ struct nc_cpblts* cpblts;
+ NC_DATASTORE ds_type_s, ds_type_t;
+ NC_EDIT_DEFOP_TYPE defop_type = 0;
+ NC_EDIT_ERROPT_TYPE erropt_type = 0;
+
+ apr_pool_t * pool = ((struct pass_to_thread*)arg)->pool;
+ apr_hash_t *netconf_sessions_list = ((struct pass_to_thread*)arg)->netconf_sessions_list;
+ server_rec * server = ((struct pass_to_thread*)arg)->server;
+ int client = ((struct pass_to_thread*)arg)->client;
+
+ char * buffer, chunk_len_str[12], *chunked_msg;
+ char c;
+
+ while (!isterminated) {
+ fds.fd = client;
+ fds.events = POLLIN;
+ fds.revents = 0;
+
+ status = poll(&fds, 1, 1000);
+
+ if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
+ /* poll was interrupted - check if the isterminated is set and if not, try poll again */
+ //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "poll interrupted");
+ continue;
+ } else if (status < 0) {
+ /* 0: poll time outed
+ * close socket and ignore this request from the client, it can try it again
+ * -1: poll failed
+ * something wrong happend, close this socket and wait for another request
+ */
+ //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "poll failed, status %d(%d: %s)", status, errno, strerror(errno));
+ close(client);
+ break;
+ }
+ /* status > 0 */
+
+ /* check the status of the socket */
+
+ /* if nothing to read and POLLHUP (EOF) or POLLERR set */
+ if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
+ /* close client's socket (it's probably already closed by client */
+ //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "socket error (%d)", fds.revents);
+ close(client);
+ break;
+ }
+
+ /* read json in chunked framing */
+ buffer_size = 0;
+ buffer_len = 0;
+ buffer = NULL;
+ while (1) {
+ fds.fd = client;
+ fds.events = POLLIN;
+ fds.revents = 0;
+
+ status = poll(&fds, 1, 1000);
+
+ if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
+ /* poll was interrupted - check if the isterminated is set and if not, try poll again */
+ //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "poll interrupted");
+ continue;
+ } else if (status < 0) {
+ /* 0: poll time outed
+ * close socket and ignore this request from the client, it can try it again
+ * -1: poll failed
+ * something wrong happend, close this socket and wait for another request
+ */
+ //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "poll failed, status %d(%d: %s)", status, errno, strerror(errno));
+ close(client);
+ break;
+ }
+ /* status > 0 */
+
+ if (buffer != NULL) {
+ request = json_tokener_parse(buffer);
+ operation = json_object_get_int(json_object_object_get(request, "type"));
+
+ session_key = (char*) json_object_get_string(json_object_object_get(request, "session"));
+ /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
+ if (operation != MSG_CONNECT && session_key == NULL) {
+ reply = json_object_new_object();
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Missing session specification."));
+ msgtext = json_object_to_json_string(reply);
+ send(client, msgtext, strlen(msgtext) + 1, 0);
+ json_object_put(reply);
+ /* there is some stupid client, so close the connection to give a chance to some other client */
+ close(client);
+ break;
+ }
+
+ /* get parameters */
+ ds_type_t = -1;
+ if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
+ if (strcmp(target, "running") == 0) {
+ ds_type_t = NC_DATASTORE_RUNNING;
+ } else if (strcmp(target, "startup") == 0) {
+ ds_type_t = NC_DATASTORE_STARTUP;
+ } else if (strcmp(target, "candidate") == 0) {
+ ds_type_t = NC_DATASTORE_CANDIDATE;
+ }
+ }
+ ds_type_s = -1;
+ if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
+ if (strcmp(source, "running") == 0) {
+ ds_type_s = NC_DATASTORE_RUNNING;
+ } else if (strcmp(source, "startup") == 0) {
+ ds_type_s = NC_DATASTORE_STARTUP;
+ } else if (strcmp(source, "candidate") == 0) {
+ ds_type_s = NC_DATASTORE_CANDIDATE;
+ }
+ }
+
+ /* null global JSON error-reply */
+ err_reply = NULL;
+
+ /* prepare reply envelope */
+ reply = json_object_new_object();
+
+ /* process required operation */
+ switch (operation) {
+ case MSG_CONNECT:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: Connect");
+
+ host = json_object_get_string(json_object_object_get(request, "host"));
+ port = json_object_get_string(json_object_object_get(request, "port"));
+ user = json_object_get_string(json_object_object_get(request, "user"));
+ pass = json_object_get_string(json_object_object_get(request, "pass"));
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "host: %s, port: %s, user: %s", host, port, user);
+ if ((host == NULL) || (user == NULL)) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Cannot connect - insufficient input.");
+ session_key = NULL;
+ } else {
+ session_key = netconf_connect(server, pool, netconf_sessions_list, host, port, user, pass);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "hash: %s", session_key);
+ }
+
+ reply = json_object_new_object();
+ if (session_key == NULL) {
+ /* negative reply */
+ if (err_reply == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
+ } else {
+ /* use filled err_reply from libnetconf's callback */
+ json_object_put(reply);
+ reply = err_reply;
+ }
+ } else {
+ /* positive reply */
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
+ json_object_object_add(reply, "session", json_object_new_string(session_key));
+
+ free(session_key);
+ }
+
+ break;
+ case MSG_GET:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get (session %s)", session_key);
+
+ filter = json_object_get_string(json_object_object_get(request, "filter"));
+
+ //ap_log_error (APLOG_MARK, APLOG_ERR, 0, server, "get filter: %p", filter);
+
+ if ((data = netconf_get(server, netconf_sessions_list, session_key, NULL)) == NULL) {
+ if (err_reply == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("get failed."));
+ } else {
+ /* use filled err_reply from libnetconf's callback */
+ json_object_put(reply);
+ reply = err_reply;
+ }
+ } else {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
+ json_object_object_add(reply, "data", json_object_new_string(data));
+ }
+ break;
+ case MSG_GETCONFIG:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get-config (session %s)", session_key);
+
+ filter = json_object_get_string(json_object_object_get(request, "filter"));
+
+ if (ds_type_s == -1) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Invalid source repository type requested."));
+ break;
+ }
+
+ if ((data = netconf_getconfig(server, netconf_sessions_list, session_key, ds_type_s, filter)) == NULL) {
+ if (err_reply == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("get-config failed."));
+ } else {
+ /* use filled err_reply from libnetconf's callback */
+ json_object_put(reply);
+ reply = err_reply;
+ }
+ } else {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
+ json_object_object_add(reply, "data", json_object_new_string(data));
+ }
+ break;
+ case MSG_EDITCONFIG:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: edit-config (session %s)", session_key);
+
+ defop = json_object_get_string(json_object_object_get(request, "default-operation"));
+ if (defop != NULL) {
+ if (strcmp(defop, "merge") == 0) {
+ defop_type = NC_EDIT_DEFOP_MERGE;
+ } else if (strcmp(defop, "replace") == 0) {
+ defop_type = NC_EDIT_DEFOP_REPLACE;
+ } else if (strcmp(defop, "none") == 0) {
+ defop_type = NC_EDIT_DEFOP_NONE;
+ } else {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Invalid default-operation parameter."));
+ break;
+ }
+ } else {
+ defop_type = 0;
+ }
+
+ erropt = json_object_get_string(json_object_object_get(request, "error-option"));
+ if (erropt != NULL) {
+ if (strcmp(erropt, "continue-on-error") == 0) {
+ erropt_type = NC_EDIT_ERROPT_CONT;
+ } else if (strcmp(erropt, "stop-on-error") == 0) {
+ erropt_type = NC_EDIT_ERROPT_STOP;
+ } else if (strcmp(erropt, "rollback-on-error") == 0) {
+ erropt_type = NC_EDIT_ERROPT_ROLLBACK;
+ } else {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Invalid error-option parameter."));
+ break;
+ }
+ } else {
+ erropt_type = 0;
+ }
+
+ if (ds_type_t == -1) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
+ break;
+ }
+
+ config = json_object_get_string(json_object_object_get(request, "config"));
+ if (config == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Invalid config data parameter."));
+ break;
+ }
+
+ if (netconf_editconfig(server, netconf_sessions_list, session_key, ds_type_t, defop_type, erropt_type, config) != EXIT_SUCCESS) {
+ if (err_reply == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("edit-config failed."));
+ } else {
+ /* use filled err_reply from libnetconf's callback */
+ json_object_put(reply);
+ reply = err_reply;
+ }
+ } else {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
+ }
+ break;
+ case MSG_COPYCONFIG:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: copy-config (session %s)", session_key);
+ config = NULL;
+
+ if (source == NULL) {
+ /* no explicit source specified -> use config data */
+ ds_type_s = NC_DATASTORE_NONE;
+ config = json_object_get_string(json_object_object_get(request, "config"));
+ } else if (ds_type_s == -1) {
+ /* source datastore specified, but it is invalid */
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Invalid source repository type requested."));
+ break;
+ }
+
+ if (ds_type_t == -1) {
+ /* invalid target datastore specified */
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
+ break;
+ }
+
+ if (source == NULL && config == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("invalid input parameters - one of source and config is required."));
+ } else {
+ if (netconf_copyconfig(server, netconf_sessions_list, session_key, ds_type_s, ds_type_t, config) != EXIT_SUCCESS) {
+ if (err_reply == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("copy-config failed."));
+ } else {
+ /* use filled err_reply from libnetconf's callback */
+ json_object_put(reply);
+ reply = err_reply;
+ }
+ } else {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
+ }
+ }
+ break;
+ case MSG_DELETECONFIG:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: delete-config (session %s)", session_key);
+ /* no break - unifying code */
+ case MSG_LOCK:
+ if (operation == MSG_LOCK) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: lock (session %s)", session_key);
+ }
+ /* no break - unifying code */
+ case MSG_UNLOCK:
+ if (operation == MSG_UNLOCK) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: unlock (session %s)", session_key);
+ }
+
+ if (ds_type_t == -1) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
+ break;
+ }
+
+ switch(operation) {
+ case MSG_DELETECONFIG:
+ status = netconf_deleteconfig(server, netconf_sessions_list, session_key, ds_type_t);
+ break;
+ case MSG_LOCK:
+ status = netconf_lock(server, netconf_sessions_list, session_key, ds_type_t);
+ break;
+ case MSG_UNLOCK:
+ status = netconf_unlock(server, netconf_sessions_list, session_key, ds_type_t);
+ break;
+ default:
+ status = -1;
+ break;
+ }
+
+ if (status != EXIT_SUCCESS) {
+ if (err_reply == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("operation failed."));
+ } else {
+ /* use filled err_reply from libnetconf's callback */
+ json_object_put(reply);
+ reply = err_reply;
+ }
+ } else {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
+ }
+ break;
+ case MSG_KILL:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: kill-session, session %s", session_key);
+
+ sid = json_object_get_string(json_object_object_get(request, "session-id"));
+
+ if (sid == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Missing session-id parameter."));
+ break;
+ }
+
+ if (netconf_killsession(server, netconf_sessions_list, session_key, sid) != EXIT_SUCCESS) {
+ if (err_reply == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("kill-session failed."));
+ } else {
+ /* use filled err_reply from libnetconf's callback */
+ json_object_put(reply);
+ reply = err_reply;
+ }
+ } else {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
+ }
+ break;
+ case MSG_DISCONNECT:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: Disconnect session %s", session_key);
+
+ if (netconf_close(server, netconf_sessions_list, session_key) != EXIT_SUCCESS) {
+ if (err_reply == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Invalid session identifier."));
+ } else {
+ /* use filled err_reply from libnetconf's callback */
+ json_object_put(reply);
+ reply = err_reply;
+ }
+ } else {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
+ }
+ break;
+ case MSG_INFO:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get info about session %s", session_key);
+
+ session = (struct nc_session *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
+ if (session != NULL) {
+ json_object_object_add(reply, "sid", json_object_new_string(nc_session_get_id(session)));
+ json_object_object_add(reply, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
+ json_object_object_add(reply, "host", json_object_new_string(nc_session_get_host(session)));
+ json_object_object_add(reply, "port", json_object_new_string(nc_session_get_port(session)));
+ json_object_object_add(reply, "user", json_object_new_string(nc_session_get_user(session)));
+ cpblts = nc_session_get_cpblts (session);
+ if (cpblts != NULL) {
+ json_obj = json_object_new_array();
+ nc_cpblts_iter_start (cpblts);
+ while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
+ json_object_array_add(json_obj, json_object_new_string(cpbltstr));
+ }
+ json_object_object_add(reply, "capabilities", json_obj);
+ }
+ } else {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Invalid session identifier."));
+ }
+
+ break;
+ case MSG_GENERIC:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: generic request for session %s", session_key);
+
+ config = json_object_get_string(json_object_object_get(request, "content"));
+
+ if (config == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Missing content parameter."));
+ break;
+ }
+
+ if (netconf_generic(server, netconf_sessions_list, session_key, config, &data) != EXIT_SUCCESS) {
+ if (err_reply == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("kill-session failed."));
+ } else {
+ /* use filled err_reply from libnetconf's callback */
+ json_object_put(reply);
+ reply = err_reply;
+ }
+ } else {
+ if (data == NULL) {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
+ } else {
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
+ json_object_object_add(reply, "data", json_object_new_string(data));
+ }
+ }
+ break;
+ default:
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown mod_netconf operation requested (%d)", operation);
+ reply = json_object_new_object();
+ json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+ json_object_object_add(reply, "error-message", json_object_new_string("Operation not supported."));
+ break;
+ }
+ json_object_put(request);
+
+ /* send reply to caller */
+ if (reply != NULL) {
+ msgtext = json_object_to_json_string(reply);
+ send(client, msgtext, strlen(msgtext) + 1, 0);
+ json_object_put(reply);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ free (arg);
+
+ return retval;
+}
+
/*
* This is actually implementation of NETCONF client
* - requests are received from UNIX socket in the predefined format
@@ -577,25 +1200,18 @@
static void forked_proc(apr_pool_t * pool, server_rec * server)
{
struct sockaddr_un local, remote;
- int lsock, client;
- socklen_t len, len2;
- struct pollfd fds;
- int status;
+ int lsock, client, ret, i, pthread_count = 0;
+ socklen_t len;
mod_netconf_cfg *cfg;
- json_object *request, *reply, *json_obj;
- int operation;
- char* session_key, *data;
- const char *msgtext, *cpbltstr;
- const char *host, *port, *user, *pass;
- const char *target, *source, *filter, *config, *defop, *erropt, *sid;
- struct nc_session *session = NULL;
- struct nc_cpblts* cpblts;
- NC_DATASTORE ds_type_s, ds_type_t;
- NC_EDIT_DEFOP_TYPE defop_type = 0;
- NC_EDIT_ERROPT_TYPE erropt_type = 0;
-
apr_hash_t *netconf_sessions_list;
- char buffer[BUFFER_SIZE];
+ struct pass_to_thread * arg;
+ pthread_t * ptids = calloc (1,sizeof(pthread_t));
+ struct timespec maxtime;
+ pthread_rwlockattr_t lock_attrs;
+
+ /* wait at most 5 secons for every thread to terminate */
+ maxtime.tv_sec = 5;
+ maxtime.tv_nsec = 0;
/* change uid and gid of process for security reasons */
unixd_setup_child();
@@ -649,12 +1265,23 @@
/* disable publickey authentication */
nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
+ /* create mutex protecting session list */
+ pthread_rwlockattr_init(&lock_attrs);
+ /* rwlock is shared only with threads in this process */
+ pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
+ /* create rw lock */
+ if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Initialization of mutex failed: %d (%s)", errno, strerror(errno));
+ close (lsock);
+ return;
+ }
+
while (isterminated == 0) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "waiting for another client's request");
/* open incoming connection if any */
- len2 = sizeof(remote);
- client = accept(lsock, (struct sockaddr *) &remote, &len2);
+ len = sizeof(remote);
+ client = accept(lsock, (struct sockaddr *) &remote, &len);
if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
apr_sleep(SLEEP_TIME);
continue;
@@ -664,442 +1291,52 @@
ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Accepting mod_netconf client connection failed (%s)", strerror(errno));
continue;
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "client's socket accepted.");
/* set client's socket as non-blocking */
//fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
- while (1) {
- fds.fd = client;
- fds.events = POLLIN;
- fds.revents = 0;
+ arg = malloc (sizeof(struct pass_to_thread));
+ arg->client = client;
+ arg->pool = pool;
+ arg->server = server;
+ arg->netconf_sessions_list = netconf_sessions_list;
- status = poll(&fds, 1, 1000);
+ /* start new thread. It will serve this particular request and then terminate */
+ if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void*)arg)) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Creating POSIX thread failed: %d\n", ret);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Thread %lu created", ptids[pthread_count]);
+ pthread_count++;
+ ptids = realloc (ptids, sizeof(pthread_t)*(pthread_count+1));
+ ptids[pthread_count] = 0;
+ }
- if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
- /* poll was interrupted - check if the isterminated is set and if not, try poll again */
- //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "poll interrupted");
- continue;
- } else if (status < 0) {
- /* 0: poll time outed
- * close socket and ignore this request from the client, it can try it again
- * -1: poll failed
- * something wrong happend, close this socket and wait for another request
- */
- //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "poll failed, status %d(%d: %s)", status, errno, strerror(errno));
- close(client);
- break;
- }
- /* status > 0 */
-
- /* check the status of the socket */
-
- /* if nothing to read and POLLHUP (EOF) or POLLERR set */
- if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
- /* close client's socket (it's probably already closed by client */
- //ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "socket error (%d)", fds.revents);
- close(client);
- break;
- }
-
- if ((len2 = recv(client, buffer, BUFFER_SIZE, 0)) <= 0) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "receiving failed %d (%s)", errno, strerror(errno));
- continue;
- } else {
- request = json_tokener_parse(buffer);
- operation = json_object_get_int(json_object_object_get(request, "type"));
-
- session_key = (char*) json_object_get_string(json_object_object_get(request, "session"));
- /* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
- if (operation != MSG_CONNECT && session_key == NULL) {
- reply = json_object_new_object();
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Missing session specification."));
- msgtext = json_object_to_json_string(reply);
- send(client, msgtext, strlen(msgtext) + 1, 0);
- json_object_put(reply);
- /* there is some stupid client, so close the connection to give a chance to some other client */
- close(client);
- break;
- }
-
- /* get parameters */
- ds_type_t = -1;
- if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
- if (strcmp(target, "running") == 0) {
- ds_type_t = NC_DATASTORE_RUNNING;
- } else if (strcmp(target, "startup") == 0) {
- ds_type_t = NC_DATASTORE_STARTUP;
- } else if (strcmp(target, "candidate") == 0) {
- ds_type_t = NC_DATASTORE_CANDIDATE;
- }
- }
- ds_type_s = -1;
- if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
- if (strcmp(source, "running") == 0) {
- ds_type_s = NC_DATASTORE_RUNNING;
- } else if (strcmp(source, "startup") == 0) {
- ds_type_s = NC_DATASTORE_STARTUP;
- } else if (strcmp(source, "candidate") == 0) {
- ds_type_s = NC_DATASTORE_CANDIDATE;
- }
- }
-
- /* null global JSON error-reply */
- err_reply = NULL;
-
- /* prepare reply envelope */
- reply = json_object_new_object();
-
- /* process required operation */
- switch (operation) {
- case MSG_CONNECT:
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: Connect");
-
- host = json_object_get_string(json_object_object_get(request, "host"));
- port = json_object_get_string(json_object_object_get(request, "port"));
- user = json_object_get_string(json_object_object_get(request, "user"));
- pass = json_object_get_string(json_object_object_get(request, "pass"));
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "host: %s, port: %s, user: %s", host, port, user);
- if ((host == NULL) || (user == NULL)) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Cannot connect - insufficient input.");
- session_key = NULL;
- } else {
- session_key = netconf_connect(server, pool, netconf_sessions_list, host, port, user, pass);
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "hash: %s", session_key);
- }
-
- reply = json_object_new_object();
- if (session_key == NULL) {
- /* negative reply */
- if (err_reply == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
- } else {
- /* use filled err_reply from libnetconf's callback */
- json_object_put(reply);
- reply = err_reply;
- }
- } else {
- /* positive reply */
- json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
- json_object_object_add(reply, "session", json_object_new_string(session_key));
-
- free(session_key);
- }
-
- break;
- case MSG_GET:
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get-config (session %s)", session_key);
-
- filter = json_object_get_string(json_object_object_get(request, "filter"));
-
- if ((data = netconf_get(server, netconf_sessions_list, session_key, filter)) == NULL) {
- if (err_reply == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("get failed."));
- } else {
- /* use filled err_reply from libnetconf's callback */
- json_object_put(reply);
- reply = err_reply;
- }
- } else {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
- json_object_object_add(reply, "data", json_object_new_string(data));
- }
- break;
- case MSG_GETCONFIG:
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get-config (session %s)", session_key);
-
- filter = json_object_get_string(json_object_object_get(request, "filter"));
-
- if (ds_type_s == -1) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Invalid source repository type requested."));
- break;
- }
-
- if ((data = netconf_getconfig(server, netconf_sessions_list, session_key, ds_type_s, filter)) == NULL) {
- if (err_reply == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("get-config failed."));
- } else {
- /* use filled err_reply from libnetconf's callback */
- json_object_put(reply);
- reply = err_reply;
- }
- } else {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
- json_object_object_add(reply, "data", json_object_new_string(data));
- }
- break;
- case MSG_EDITCONFIG:
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: edit-config (session %s)", session_key);
-
- defop = json_object_get_string(json_object_object_get(request, "default-operation"));
- if (defop != NULL) {
- if (strcmp(defop, "merge") == 0) {
- defop_type = NC_EDIT_DEFOP_MERGE;
- } else if (strcmp(defop, "replace") == 0) {
- defop_type = NC_EDIT_DEFOP_REPLACE;
- } else if (strcmp(defop, "none") == 0) {
- defop_type = NC_EDIT_DEFOP_NONE;
- } else {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Invalid default-operation parameter."));
- break;
- }
- } else {
- defop_type = 0;
- }
-
- erropt = json_object_get_string(json_object_object_get(request, "error-option"));
- if (erropt != NULL) {
- if (strcmp(erropt, "continue-on-error") == 0) {
- erropt_type = NC_EDIT_ERROPT_CONT;
- } else if (strcmp(erropt, "stop-on-error") == 0) {
- erropt_type = NC_EDIT_ERROPT_STOP;
- } else if (strcmp(erropt, "rollback-on-error") == 0) {
- erropt_type = NC_EDIT_ERROPT_ROLLBACK;
- } else {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Invalid error-option parameter."));
- break;
- }
- } else {
- erropt_type = 0;
- }
-
- if (ds_type_t == -1) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
- break;
- }
-
- config = json_object_get_string(json_object_object_get(request, "config"));
- if (config == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Invalid config data parameter."));
- break;
- }
-
- if (netconf_editconfig(server, netconf_sessions_list, session_key, ds_type_t, defop_type, erropt_type, config) != EXIT_SUCCESS) {
- if (err_reply == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("edit-config failed."));
- } else {
- /* use filled err_reply from libnetconf's callback */
- json_object_put(reply);
- reply = err_reply;
- }
- } else {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
- }
- break;
- case MSG_COPYCONFIG:
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: copy-config (session %s)", session_key);
- config = NULL;
-
- if (source == NULL) {
- /* no explicit source specified -> use config data */
- ds_type_s = NC_DATASTORE_NONE;
- config = json_object_get_string(json_object_object_get(request, "config"));
- } else if (ds_type_s == -1) {
- /* source datastore specified, but it is invalid */
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Invalid source repository type requested."));
- break;
- }
-
- if (ds_type_t == -1) {
- /* invalid target datastore specified */
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
- break;
- }
-
- if (source == NULL && config == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("invalid input parameters - one of source and config is required."));
- } else {
- if (netconf_copyconfig(server, netconf_sessions_list, session_key, ds_type_s, ds_type_t, config) != EXIT_SUCCESS) {
- if (err_reply == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("copy-config failed."));
- } else {
- /* use filled err_reply from libnetconf's callback */
- json_object_put(reply);
- reply = err_reply;
- }
- } else {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
- }
- }
- break;
- case MSG_DELETECONFIG:
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: delete-config (session %s)", session_key);
- /* no break - unifying code */
- case MSG_LOCK:
- if (operation == MSG_LOCK) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: lock (session %s)", session_key);
- }
- /* no break - unifying code */
- case MSG_UNLOCK:
- if (operation == MSG_UNLOCK) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: unlock (session %s)", session_key);
- }
-
- if (ds_type_t == -1) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
- break;
- }
-
- switch(operation) {
- case MSG_DELETECONFIG:
- status = netconf_deleteconfig(server, netconf_sessions_list, session_key, ds_type_t);
- break;
- case MSG_LOCK:
- status = netconf_lock(server, netconf_sessions_list, session_key, ds_type_t);
- break;
- case MSG_UNLOCK:
- status = netconf_unlock(server, netconf_sessions_list, session_key, ds_type_t);
- break;
- default:
- status = -1;
- break;
- }
-
- if (status != EXIT_SUCCESS) {
- if (err_reply == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("operation failed."));
- } else {
- /* use filled err_reply from libnetconf's callback */
- json_object_put(reply);
- reply = err_reply;
- }
- } else {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
- }
- break;
- case MSG_KILL:
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: kill-session, session %s", session_key);
-
- sid = json_object_get_string(json_object_object_get(request, "session-id"));
-
- if (sid == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Missing session-id parameter."));
- break;
- }
-
- if (netconf_killsession(server, netconf_sessions_list, session_key, sid) != EXIT_SUCCESS) {
- if (err_reply == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("kill-session failed."));
- } else {
- /* use filled err_reply from libnetconf's callback */
- json_object_put(reply);
- reply = err_reply;
- }
- } else {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
- }
- break;
- case MSG_DISCONNECT:
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: Disconnect session %s", session_key);
-
- if (netconf_close(server, netconf_sessions_list, session_key) != EXIT_SUCCESS) {
- if (err_reply == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Invalid session identifier."));
- } else {
- /* use filled err_reply from libnetconf's callback */
- json_object_put(reply);
- reply = err_reply;
- }
- } else {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
- }
- break;
- case MSG_INFO:
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get info about session %s", session_key);
-
- session = (struct nc_session *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
- if (session != NULL) {
- json_object_object_add(reply, "sid", json_object_new_string(nc_session_get_id(session)));
- json_object_object_add(reply, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
- json_object_object_add(reply, "host", json_object_new_string(nc_session_get_host(session)));
- json_object_object_add(reply, "port", json_object_new_string(nc_session_get_port(session)));
- json_object_object_add(reply, "user", json_object_new_string(nc_session_get_user(session)));
- cpblts = nc_session_get_cpblts (session);
- if (cpblts != NULL) {
- json_obj = json_object_new_array();
- nc_cpblts_iter_start (cpblts);
- while ((cpbltstr = nc_cpblts_iter_next (cpblts)) != NULL) {
- json_object_array_add(json_obj, json_object_new_string(cpbltstr));
- }
- json_object_object_add(reply, "capabilities", json_obj);
- }
- } else {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Invalid session identifier."));
- }
-
- break;
- case MSG_GENERIC:
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: generic request for session %s", session_key);
-
- config = json_object_get_string(json_object_object_get(request, "content"));
-
- if (config == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Missing content parameter."));
- break;
- }
-
- if (netconf_generic(server, netconf_sessions_list, session_key, config, &data) != EXIT_SUCCESS) {
- if (err_reply == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("kill-session failed."));
- } else {
- /* use filled err_reply from libnetconf's callback */
- json_object_put(reply);
- reply = err_reply;
- }
- } else {
- if (data == NULL) {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
- } else {
- json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
- json_object_object_add(reply, "data", json_object_new_string(data));
- }
- }
- break;
- default:
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown mod_netconf operation requested (%d)", operation);
- reply = json_object_new_object();
- json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
- json_object_object_add(reply, "error-message", json_object_new_string("Operation not supported."));
- break;
- }
- json_object_put(request);
-
- /* send reply to caller */
- if (reply != NULL) {
- msgtext = json_object_to_json_string(reply);
- send(client, msgtext, strlen(msgtext) + 1, 0);
- json_object_put(reply);
- } else {
- break;
+ /* check if some thread already terminated, free some resources by joining it */
+ for (i=0; i<pthread_count; i++) {
+ if (pthread_tryjoin_np (ptids[i], (void**)&arg) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Thread %lu joined with retval %p", ptids[i], arg);
+ pthread_count--;
+ if (pthread_count > 0) {
+ /* place last Thread ID on the place of joined one */
+ ptids[i] = ptids[pthread_count];
}
}
}
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Running %d threads", pthread_count);
}
+ /* join all threads */
+ for (i=0; i<pthread_count; i++) {
+ pthread_timedjoin_np (ptids[i], (void**)&arg, &maxtime);
+ }
+ free (ptids);
+
close(lsock);
+ /* destroy rwlock */
+ pthread_rwlock_destroy(&session_lock);
+ pthread_rwlockattr_destroy(&lock_attrs);
+
ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Exiting from the mod_netconf daemon");
exit(APR_SUCCESS);