This fixes a race condition in para_server:
parent child
real_query
real_query
mysql_store_result
This could cause para_server to hang in case the child process issued
a query which does not yield a result. It's hard to hit though. Fix is
to take the lock before the call to real_query, and release it after
mysql_store_result().
The patch also fixes a format string bug which was introduced by the patch
that switched to my_ulonglong for suitable variables in mysql_selector.c.
These need "%lli".
#include "string.h"
#include "user_list.h"
#include "mysql_selector_command_list.h"
#include "string.h"
#include "user_list.h"
#include "mysql_selector_command_list.h"
/** pointer to the shared memory area */
extern struct misc_meta_data *mmd;
static void *mysql_ptr = NULL;
/** pointer to the shared memory area */
extern struct misc_meta_data *mmd;
static void *mysql_ptr = NULL;
/**
* contains name/replacement pairs used by s_a_r_list()
/**
* contains name/replacement pairs used by s_a_r_list()
-static int real_query(const char *query)
+static int lockless_real_query(const char *query)
{
if (!mysql_ptr)
return -E_NOTCONN;
{
if (!mysql_ptr)
return -E_NOTCONN;
+static int real_query(const char *query)
+{
+ int ret;
+
+ mutex_lock(mysql_lock);
+ ret = lockless_real_query(query);
+ mutex_unlock(mysql_lock);
+ return ret;
+}
+
/*
* Use open connection given by mysql_ptr to query server. Returns a
* result pointer on succes and NULL on errors
*/
static struct MYSQL_RES *get_result(const char *query)
{
/*
* Use open connection given by mysql_ptr to query server. Returns a
* result pointer on succes and NULL on errors
*/
static struct MYSQL_RES *get_result(const char *query)
{
- if (real_query(query) < 0)
- return NULL;
+ mutex_lock(mysql_lock);
+ if (lockless_real_query(query) < 0)
+ goto out;
result = mysql_store_result(mysql_ptr);
if (!result)
PARA_ERROR_LOG("%s", "store_result error\n");
result = mysql_store_result(mysql_ptr);
if (!result)
PARA_ERROR_LOG("%s", "store_result error\n");
+out:
+ mutex_unlock(mysql_lock);
goto out;
}
if (vrfy_mode) {
goto out;
}
if (vrfy_mode) {
- send_va_buffer(fd, "found %i invalid entr%s\n", num_rows,
+ send_va_buffer(fd, "found %lli invalid entr%s\n", num_rows,
num_rows == 1? "y" : "ies");
ret = print_results(fd, result, top, left, num_rows - 1, right);
goto out;
num_rows == 1? "y" : "ies");
ret = print_results(fd, result, top, left, num_rows - 1, right);
goto out;
static void shutdown_connection(void)
{
static void shutdown_connection(void)
{
if (mysql_ptr) {
PARA_NOTICE_LOG("%s", "shutting down mysql connection\n");
mysql_close(mysql_ptr);
mysql_ptr = NULL;
}
if (mysql_ptr) {
PARA_NOTICE_LOG("%s", "shutting down mysql connection\n");
mysql_close(mysql_ptr);
mysql_ptr = NULL;
}
+ if (mysql_lock) {
+ ret = mutex_destroy(mysql_lock);
+ if (ret < 0)
+ PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
+ }
db->get_audio_file_list = server_get_audio_file_list;
db->update_audio_file = update_audio_file_server_handler;
db->shutdown = shutdown_connection;
db->get_audio_file_list = server_get_audio_file_list;
db->update_audio_file = update_audio_file_server_handler;
db->shutdown = shutdown_connection;
+ ret = mutex_new();
+ if (ret < 0)
+ return ret;
+ mysql_lock = ret;
ret = init_mysql_server();
if (ret < 0)
PARA_WARNING_LOG("%s\n", PARA_STRERROR(-ret));
ret = init_mysql_server();
if (ret < 0)
PARA_WARNING_LOG("%s\n", PARA_STRERROR(-ret));