BUGFIX json-c is not thread-safe :-(
discussed in https://github.com/json-c/json-c/issues/116
diff --git a/src/mod_netconf.c b/src/mod_netconf.c
index d4eeb9c..5093f67 100644
--- a/src/mod_netconf.c
+++ b/src/mod_netconf.c
@@ -110,6 +110,8 @@
pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
+pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
+
apr_hash_t *netconf_sessions_list = NULL;
static pthread_key_t notif_history_key;
@@ -201,6 +203,7 @@
{
json_object *array = NULL;
if (err_reply == NULL) {
+ pthread_mutex_lock(&json_lock);
err_reply = json_object_new_object();
array = json_object_new_array();
json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
@@ -208,15 +211,19 @@
if (message != NULL) {
json_object_array_add(array, json_object_new_string(message));
}
+ pthread_mutex_unlock(&json_lock);
return;
} else {
+ pthread_mutex_lock(&json_lock);
array = json_object_object_get(err_reply, "errors");
if (array != NULL) {
if (message != NULL) {
json_object_array_add(array, json_object_new_string(message));
}
}
+ pthread_mutex_unlock(&json_lock);
}
+ return;
}
/**
@@ -235,6 +242,7 @@
return;
}
+ pthread_mutex_lock(&json_lock);
if (s->hello_message != NULL) {
DEBUG("clean previous hello message");
//json_object_put(s->hello_message);
@@ -275,6 +283,7 @@
json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
}
DEBUG("Status info from hello message prepared");
+ pthread_mutex_unlock(&json_lock);
}
@@ -1011,28 +1020,34 @@
json_object *create_error(const char *errmess)
{
+ pthread_mutex_lock(&json_lock);
json_object *reply = json_object_new_object();
json_object *array = json_object_new_array();
json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
json_object_array_add(array, json_object_new_string(errmess));
json_object_object_add(reply, "errors", array);
+ pthread_mutex_unlock(&json_lock);
return reply;
}
json_object *create_data(const char *data)
{
+ pthread_mutex_lock(&json_lock);
json_object *reply = json_object_new_object();
json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
json_object_object_add(reply, "data", json_object_new_string(data));
+ pthread_mutex_unlock(&json_lock);
return reply;
}
json_object *create_ok()
{
+ pthread_mutex_lock(&json_lock);
json_object *reply = json_object_new_object();
reply = json_object_new_object();
json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
+ pthread_mutex_unlock(&json_lock);
return reply;
}
@@ -1049,6 +1064,7 @@
unsigned int len, i;
DEBUG("Request: Connect");
+ pthread_mutex_lock(&json_lock);
host = json_object_get_string(json_object_object_get((json_object *) request, "host"));
port = json_object_get_string(json_object_object_get((json_object *) request, "port"));
user = json_object_get_string(json_object_object_get((json_object *) request, "user"));
@@ -1063,6 +1079,7 @@
} else {
DEBUG("no capabilities specified");
}
+ pthread_mutex_unlock(&json_lock);
DEBUG("host: %s, port: %s, user: %s", host, port, user);
if ((host == NULL) || (user == NULL)) {
@@ -1076,6 +1093,7 @@
nc_cpblts_free(cpblts);
}
+ pthread_mutex_lock(&json_lock);
if (session_key_hash == NULL) {
/* negative reply */
if (err_reply == NULL) {
@@ -1096,6 +1114,7 @@
free(session_key_hash);
}
+ pthread_mutex_unlock(&json_lock);
return reply;
}
@@ -1107,7 +1126,9 @@
DEBUG("Request: get (session %s)", session_key);
+ pthread_mutex_lock(&json_lock);
filter = json_object_get_string(json_object_object_get(request, "filter"));
+ pthread_mutex_unlock(&json_lock);
if ((data = netconf_get(session_key, filter, &reply)) == NULL) {
if (reply == NULL) {
@@ -1135,11 +1156,13 @@
DEBUG("Request: get-config (session %s)", session_key);
+ pthread_mutex_lock(&json_lock);
filter = json_object_get_string(json_object_object_get(request, "filter"));
if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
ds_type_s = parse_datastore(source);
}
+ pthread_mutex_unlock(&json_lock);
if (ds_type_s == -1) {
return create_error("Invalid source repository type requested.");
}
@@ -1169,12 +1192,15 @@
json_object *reply = NULL;
DEBUG("Request: get-schema (session %s)", session_key);
+ pthread_mutex_lock(&json_lock);
identifier = json_object_get_string(json_object_object_get(request, "identifier"));
+ version = json_object_get_string(json_object_object_get(request, "version"));
+ format = json_object_get_string(json_object_object_get(request, "format"));
+ pthread_mutex_lock(&json_lock);
+
if (identifier == NULL) {
return create_error("No identifier for get-schema supplied.");
}
- version = json_object_get_string(json_object_object_get(request, "version"));
- format = json_object_get_string(json_object_object_get(request, "format"));
DEBUG("get-schema(version: %s, format: %s)", version, format);
if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
@@ -1210,7 +1236,23 @@
DEBUG("Request: edit-config (session %s)", session_key);
+ pthread_mutex_lock(&json_lock);
defop = json_object_get_string(json_object_object_get(request, "default-operation"));
+ erropt = json_object_get_string(json_object_object_get(request, "error-option"));
+ /* get parameters */
+ if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
+ ds_type_t = parse_datastore(target);
+ }
+ if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
+ ds_type_s = parse_datastore(source);
+ } else {
+ /* source is optional, default value is config */
+ ds_type_s = NC_DATASTORE_CONFIG;
+ }
+ config = json_object_get_string(json_object_object_get(request, "config"));
+ testopt = json_object_get_string(json_object_object_get(request, "test-option"));
+ pthread_mutex_unlock(&json_lock);
+
if (defop != NULL) {
if (strcmp(defop, "merge") == 0) {
defop_type = NC_EDIT_DEFOP_MERGE;
@@ -1225,7 +1267,6 @@
defop_type = NC_EDIT_DEFOP_NOTSET;
}
- 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;
@@ -1240,32 +1281,19 @@
erropt_type = 0;
}
- /* get parameters */
- if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
- ds_type_t = parse_datastore(target);
- }
- if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
- ds_type_s = parse_datastore(source);
- } else {
- /* source is optional, default value is config */
- ds_type_s = NC_DATASTORE_CONFIG;
- }
if (ds_type_t == -1) {
return create_error("Invalid target repository type requested.");
}
if (ds_type_s == NC_DATASTORE_CONFIG) {
- config = json_object_get_string(json_object_object_get(request, "config"));
if (config == NULL) {
return create_error("Invalid config data parameter.");
}
} else if (ds_type_s == NC_DATASTORE_URL){
- config = json_object_get_string(json_object_object_get(request, "uri-source"));
if (config == NULL) {
config = "";
}
}
- testopt = json_object_get_string(json_object_object_get(request, "test-option"));
if (testopt != NULL) {
testopt_type = parse_testopt(testopt);
} else {
@@ -1296,6 +1324,7 @@
DEBUG("Request: copy-config (session %s)", session_key);
+ pthread_mutex_lock(&json_lock);
/* get parameters */
if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
ds_type_t = parse_datastore(target);
@@ -1303,10 +1332,14 @@
if ((source = json_object_get_string(json_object_object_get(request, "source"))) != NULL) {
ds_type_s = parse_datastore(source);
}
+ config = json_object_get_string(json_object_object_get(request, "config"));
+ uri_src = json_object_get_string(json_object_object_get(request, "uri-source"));
+ uri_trg = json_object_get_string(json_object_object_get(request, "uri-target"));
+ pthread_mutex_unlock(&json_lock);
+
if (source == NULL) {
/* no explicit source specified -> use config data */
ds_type_s = NC_DATASTORE_CONFIG;
- config = json_object_get_string(json_object_object_get(request, "config"));
} else if (ds_type_s == -1) {
/* source datastore specified, but it is invalid */
return create_error("Invalid source repository type requested.");
@@ -1324,13 +1357,11 @@
}
if (ds_type_s == NC_DATASTORE_URL) {
- uri_src = json_object_get_string(json_object_object_get(request, "uri-source"));
if (uri_src == NULL) {
uri_src = "";
}
}
if (ds_type_t == NC_DATASTORE_URL) {
- uri_trg = json_object_get_string(json_object_object_get(request, "uri-target"));
if (uri_trg == NULL) {
uri_trg = "";
}
@@ -1353,7 +1384,9 @@
DEBUG("Request: generic request for session %s", session_key);
+ pthread_mutex_lock(&json_lock);
config = json_object_get_string(json_object_object_get(request, "content"));
+ pthread_mutex_unlock(&json_lock);
if (config == NULL) {
return create_error("Missing content parameter.");
@@ -1368,8 +1401,10 @@
}
} else {
if (data == NULL) {
+ pthread_mutex_lock(&json_lock);
reply = json_object_new_object();
json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
+ pthread_mutex_unlock(&json_lock);
} else {
reply = create_data(data);
free(data);
@@ -1406,7 +1441,9 @@
DEBUG("Request: kill-session, session %s", session_key);
+ pthread_mutex_lock(&json_lock);
sid = json_object_get_string(json_object_object_get(request, "session-id"));
+ pthread_mutex_unlock(&json_lock);
if (sid == NULL) {
return create_error("Missing session-id parameter.");
@@ -1516,14 +1553,17 @@
return;
}
DEBUG("Got notification from history %lu.", (long unsigned) eventtime);
+ pthread_mutex_lock(&json_lock);
json_object *notif = json_object_new_object();
if (notif == NULL) {
DEBUG("Could not allocate memory for notification (json).");
- return;
+ goto failed;
}
json_object_object_add(notif, "eventtime", json_object_new_int64(eventtime));
json_object_object_add(notif, "content", json_object_new_string(content));
json_object_array_add(notif_history_array, notif);
+failed:
+ pthread_mutex_unlock(&json_lock);
}
json_object *handle_op_ntfgethistory(apr_pool_t *pool, json_object *request, const char *session_key)
@@ -1539,9 +1579,11 @@
DEBUG("Request: get notification history, session %s", session_key);
+ pthread_mutex_lock(&json_lock);
sid = json_object_get_string(json_object_object_get(request, "session"));
from = json_object_get_int64(json_object_object_get(request, "from"));
to = json_object_get_int64(json_object_object_get(request, "to"));
+ pthread_mutex_unlock(&json_lock);
start = time(NULL) + from;
stop = time(NULL) + to;
@@ -1592,16 +1634,20 @@
pthread_mutex_unlock(&locked_session->lock);
DEBUG("LOCK mutex %s", __func__);
pthread_mutex_lock(&ntf_history_lock);
+ pthread_mutex_lock(&json_lock);
json_object *notif_history_array = json_object_new_array();
+ pthread_mutex_unlock(&json_lock);
if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
DEBUG("notif_history: cannot set thread-specific hash value.");
}
ncntf_dispatch_receive(temp_session, notification_history);
+ pthread_mutex_lock(&json_lock);
reply = json_object_new_object();
json_object_object_add(reply, "notifications", notif_history_array);
//json_object_put(notif_history_array);
+ pthread_mutex_unlock(&json_lock);
DEBUG("UNLOCK mutex %s", __func__);
pthread_mutex_unlock(&ntf_history_lock);
@@ -1637,9 +1683,11 @@
DEBUG("Request: validate datastore, session %s", session_key);
+ pthread_mutex_lock(&json_lock);
sid = json_object_get_string(json_object_object_get(request, "session"));
target = json_object_get_string(json_object_object_get(request, "target"));
url = json_object_get_string(json_object_object_get(request, "url"));
+ pthread_mutex_unlock(&json_lock);
if ((sid == NULL) || (target == NULL)) {
@@ -1738,21 +1786,31 @@
DEBUG("Check read buffer.");
if (buffer != NULL) {
enum json_tokener_error jerr;
+ pthread_mutex_lock(&json_lock);
request = json_tokener_parse_verbose(buffer, &jerr);
if (jerr != json_tokener_success) {
DEBUG("JSON parsing error");
+ pthread_mutex_unlock(&json_lock);
continue;
}
- operation = json_object_get_int(json_object_object_get(request, "type"));
+ operation = json_object_get_int(json_object_object_get(request, "type"));
session_key = json_object_get_string(json_object_object_get(request, "session"));
+ pthread_mutex_unlock(&json_lock);
+
DEBUG("operation %d session_key %s.", operation, session_key);
/* DO NOT FREE session_key HERE, IT IS PART OF REQUEST */
if (operation != MSG_CONNECT && session_key == NULL) {
reply = create_error("Missing session specification.");
+ pthread_mutex_lock(&json_lock);
msgtext = json_object_to_json_string(reply);
+ pthread_mutex_unlock(&json_lock);
+
send(client, msgtext, strlen(msgtext) + 1, 0);
+
+ pthread_mutex_lock(&json_lock);
json_object_put(reply);
+ pthread_mutex_unlock(&json_lock);
/* there is some stupid client, so close the connection to give a chance to some other client */
close(client);
break;
@@ -1763,7 +1821,9 @@
/* prepare reply envelope */
if (reply != NULL) {
+ pthread_mutex_lock(&json_lock);
json_object_put(reply);
+ pthread_mutex_unlock(&json_lock);
}
reply = NULL;
@@ -1792,9 +1852,11 @@
case MSG_LOCK:
case MSG_UNLOCK:
/* get parameters */
+ pthread_mutex_lock(&json_lock);
if ((target = json_object_get_string(json_object_object_get(request, "target"))) != NULL) {
ds_type_t = parse_datastore(target);
}
+ pthread_mutex_unlock(&json_lock);
if (ds_type_t == -1) {
reply = create_error("Invalid target repository type requested.");
@@ -1803,7 +1865,9 @@
switch(operation) {
case MSG_DELETECONFIG:
DEBUG("Request: delete-config (session %s)", session_key);
+ pthread_mutex_lock(&json_lock);
url = json_object_get_string(json_object_object_get(request, "url"));
+ pthread_mutex_unlock(&json_lock);
reply = netconf_deleteconfig(session_key, ds_type_t, url);
break;
case MSG_LOCK:
@@ -1822,8 +1886,10 @@
if (reply == NULL) {
if (err_reply == NULL) {
/** \todo more clever error message wanted */
+ pthread_mutex_lock(&json_lock);
reply = json_object_new_object();
json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
+ pthread_mutex_unlock(&json_lock);
} else {
/* use filled err_reply from libnetconf's callback */
reply = err_reply;
@@ -1857,8 +1923,8 @@
break;
}
DEBUG("Clean request json object.");
+ pthread_mutex_lock(&json_lock);
json_object_put(request);
-
DEBUG("Send reply json object.");
/* send reply to caller */
if (reply != NULL) {
@@ -1870,9 +1936,12 @@
}
break;
}
+ pthread_mutex_unlock(&json_lock);
+
DEBUG("Send framed reply json object.");
send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
DEBUG("Clean reply json object.");
+ pthread_mutex_lock(&json_lock);
json_object_put(reply);
reply = NULL;
DEBUG("Clean message buffer.");
@@ -1886,7 +1955,9 @@
json_object_put(err_reply);
err_reply = NULL;
}
+ pthread_mutex_unlock(&json_lock);
} else {
+ pthread_mutex_unlock(&json_lock);
DEBUG("Reply is NULL, shouldn't be...");
continue;
}
@@ -2101,6 +2172,7 @@
goto error_exit;
}
pthread_mutex_init(&ntf_history_lock, NULL);
+ pthread_mutex_init(&json_lock, NULL);
DEBUG("init of notif_history_key.");
if (pthread_key_create(¬if_history_key, NULL) != 0) {
DEBUG("init of notif_history_key failed");