Merge pchardin@bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/home/cps/mysql/devel/im-fix-review
This commit is contained in:
commit
dfba6f4e9b
@ -531,7 +531,7 @@ typedef uint32 ha_checksum;
|
|||||||
|
|
||||||
/* Define the type of function to be passed to process_default_option_files */
|
/* Define the type of function to be passed to process_default_option_files */
|
||||||
typedef int (*Process_option_func)(void *ctx, const char *group_name,
|
typedef int (*Process_option_func)(void *ctx, const char *group_name,
|
||||||
const char *option);
|
const char *option);
|
||||||
|
|
||||||
#include <my_alloc.h>
|
#include <my_alloc.h>
|
||||||
|
|
||||||
@ -776,9 +776,9 @@ extern void get_defaults_files(int argc, char **argv,
|
|||||||
char **defaults, char **extra_defaults);
|
char **defaults, char **extra_defaults);
|
||||||
extern int load_defaults(const char *conf_file, const char **groups,
|
extern int load_defaults(const char *conf_file, const char **groups,
|
||||||
int *argc, char ***argv);
|
int *argc, char ***argv);
|
||||||
extern int process_default_option_files(const char *conf_file,
|
extern int my_search_option_files(const char *conf_file, int *argc,
|
||||||
Process_option_func func,
|
char ***argv, uint *args_used,
|
||||||
void *func_ctx);
|
Process_option_func func, void *func_ctx);
|
||||||
extern void free_defaults(char **argv);
|
extern void free_defaults(char **argv);
|
||||||
extern void print_defaults(const char *conf_file, const char **groups);
|
extern void print_defaults(const char *conf_file, const char **groups);
|
||||||
extern my_bool my_compress(byte *, ulong *, ulong *);
|
extern my_bool my_compress(byte *, ulong *, ulong *);
|
||||||
|
@ -83,7 +83,7 @@ static char *remove_end_comment(char *ptr);
|
|||||||
Process config files in default directories.
|
Process config files in default directories.
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
search_files()
|
my_search_option_files()
|
||||||
conf_file Basename for configuration file to search for.
|
conf_file Basename for configuration file to search for.
|
||||||
If this is a path, then only this file is read.
|
If this is a path, then only this file is read.
|
||||||
argc Pointer to argc of original program
|
argc Pointer to argc of original program
|
||||||
@ -103,13 +103,13 @@ static char *remove_end_comment(char *ptr);
|
|||||||
1 given cinf_file doesn't exist
|
1 given cinf_file doesn't exist
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int search_files(const char *conf_file, int *argc, char ***argv,
|
int my_search_option_files(const char *conf_file, int *argc, char ***argv,
|
||||||
uint *args_used, Process_option_func func,
|
uint *args_used, Process_option_func func,
|
||||||
void *func_ctx)
|
void *func_ctx)
|
||||||
{
|
{
|
||||||
const char **dirs, *forced_default_file;
|
const char **dirs, *forced_default_file;
|
||||||
int error= 0;
|
int error= 0;
|
||||||
DBUG_ENTER("search_files");
|
DBUG_ENTER("my_search_option_files");
|
||||||
|
|
||||||
/* Check if we want to force the use a specific default file */
|
/* Check if we want to force the use a specific default file */
|
||||||
get_defaults_files(*argc, *argv,
|
get_defaults_files(*argc, *argv,
|
||||||
@ -180,40 +180,6 @@ err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Simplified version of search_files (no argv, argc to process).
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
process_default_option_files()
|
|
||||||
conf_file Basename for configuration file to search for.
|
|
||||||
If this is a path, then only this file is read.
|
|
||||||
func Pointer to the function to process options
|
|
||||||
func_ctx It's context. Usually it is the structure to
|
|
||||||
store additional options.
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
|
|
||||||
Often we want only to get options from default config files. In this case we
|
|
||||||
don't want to provide any argc and argv parameters. This function is a
|
|
||||||
simplified variant of search_files which allows us to forget about
|
|
||||||
argc, argv.
|
|
||||||
|
|
||||||
RETURN
|
|
||||||
0 ok
|
|
||||||
1 given cinf_file doesn't exist
|
|
||||||
*/
|
|
||||||
|
|
||||||
int process_default_option_files(const char *conf_file,
|
|
||||||
Process_option_func func, void *func_ctx)
|
|
||||||
{
|
|
||||||
int argc= 1;
|
|
||||||
/* this is a dummy variable for search_files() */
|
|
||||||
uint args_used;
|
|
||||||
|
|
||||||
return search_files(conf_file, &argc, NULL, &args_used, func, func_ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The option handler for load_defaults.
|
The option handler for load_defaults.
|
||||||
|
|
||||||
@ -363,7 +329,7 @@ int load_defaults(const char *conf_file, const char **groups,
|
|||||||
ctx.args= &args;
|
ctx.args= &args;
|
||||||
ctx.group= &group;
|
ctx.group= &group;
|
||||||
|
|
||||||
error= search_files(conf_file, argc, argv, &args_used,
|
error= my_search_option_files(conf_file, argc, argv, &args_used,
|
||||||
handle_default_option, (void *) &ctx);
|
handle_default_option, (void *) &ctx);
|
||||||
/*
|
/*
|
||||||
Here error contains <> 0 only if we have a fully specified conf_file
|
Here error contains <> 0 only if we have a fully specified conf_file
|
||||||
|
@ -30,14 +30,13 @@ liboptions_a_CXXFLAGS= $(CXXFLAGS) \
|
|||||||
-DDEFAULT_LOG_FILE_NAME="$(localstatedir)/mysqlmanager.log" \
|
-DDEFAULT_LOG_FILE_NAME="$(localstatedir)/mysqlmanager.log" \
|
||||||
-DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" \
|
-DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" \
|
||||||
-DDEFAULT_PASSWORD_FILE_NAME="$(sysconfdir)/mysqlmanager.passwd" \
|
-DDEFAULT_PASSWORD_FILE_NAME="$(sysconfdir)/mysqlmanager.passwd" \
|
||||||
-DDEFAULT_MYSQLD_PATH="$(bindir)/mysqld$(EXEEXT)" \
|
-DDEFAULT_MYSQLD_PATH="$(libexecdir)/mysqld$(EXEEXT)" \
|
||||||
-DDEFAULT_USER="root" \
|
-DDEFAULT_MONITORING_INTERVAL="20" \
|
||||||
-DDEFAULT_PASSWORD="" \
|
|
||||||
-DDEFAULT_MONITORING_INTERVAL="5" \
|
|
||||||
-DDEFAULT_PORT="2273" \
|
-DDEFAULT_PORT="2273" \
|
||||||
-DPROTOCOL_VERSION=@PROTOCOL_VERSION@
|
-DPROTOCOL_VERSION=@PROTOCOL_VERSION@
|
||||||
|
|
||||||
liboptions_a_SOURCES= options.h options.cc priv.h priv.cc
|
liboptions_a_SOURCES= options.h options.cc priv.h priv.cc
|
||||||
|
liboptions_a_LIBADD= $(top_builddir)/libmysql/get_password.$(OBJEXT)
|
||||||
|
|
||||||
# MySQL sometimes uses symlinks to reuse code
|
# MySQL sometimes uses symlinks to reuse code
|
||||||
# All symlinked files are grouped in libnet.a
|
# All symlinked files are grouped in libnet.a
|
||||||
@ -59,7 +58,7 @@ client_settings.h: Makefile
|
|||||||
rm -f $(srcdir)/client_settings.h
|
rm -f $(srcdir)/client_settings.h
|
||||||
@LN_CP_F@ $(top_srcdir)/sql/client_settings.h $(srcdir)/client_settings.h
|
@LN_CP_F@ $(top_srcdir)/sql/client_settings.h $(srcdir)/client_settings.h
|
||||||
|
|
||||||
bin_PROGRAMS= mysqlmanager
|
libexec_PROGRAMS= mysqlmanager
|
||||||
|
|
||||||
mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
|
mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
|
||||||
manager.h manager.cc log.h log.cc \
|
manager.h manager.cc log.h log.cc \
|
||||||
@ -75,7 +74,8 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
|
|||||||
instance_options.h instance_options.cc \
|
instance_options.h instance_options.cc \
|
||||||
buffer.h buffer.cc parse.cc parse.h \
|
buffer.h buffer.cc parse.cc parse.h \
|
||||||
guardian.cc guardian.h \
|
guardian.cc guardian.h \
|
||||||
mysql_manager_error.h client_func.c
|
parse_output.cc parse_output.h \
|
||||||
|
mysql_manager_error.h
|
||||||
|
|
||||||
mysqlmanager_LDADD= liboptions.a \
|
mysqlmanager_LDADD= liboptions.a \
|
||||||
libnet.a \
|
libnet.a \
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
0 - ok
|
0 - ok
|
||||||
1 - The buffer came to 16Mb barrier
|
1 - got an error in reserve()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int Buffer::append(uint position, const char *string, uint len_arg)
|
int Buffer::append(uint position, const char *string, uint len_arg)
|
||||||
@ -71,7 +71,7 @@ int Buffer::append(uint position, const char *string, uint len_arg)
|
|||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
0 - ok
|
0 - ok
|
||||||
1 - The buffer came to 16Mb barrier
|
1 - realloc error or we have come to the 16Mb barrier
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int Buffer::reserve(uint position, uint len_arg)
|
int Buffer::reserve(uint position, uint len_arg)
|
||||||
@ -79,19 +79,31 @@ int Buffer::reserve(uint position, uint len_arg)
|
|||||||
if (position + len_arg >= MAX_BUFFER_SIZE)
|
if (position + len_arg >= MAX_BUFFER_SIZE)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (position + len_arg>= buffer_size)
|
if (position + len_arg >= buffer_size)
|
||||||
{
|
{
|
||||||
buffer= (char *) realloc(buffer,
|
buffer= (char *) my_realloc(buffer,
|
||||||
min(MAX_BUFFER_SIZE,
|
min(MAX_BUFFER_SIZE,
|
||||||
max((uint) (buffer_size*1.5),
|
max((uint) (buffer_size*1.5),
|
||||||
position + len_arg)));
|
position + len_arg)), MYF(0));
|
||||||
if (buffer == NULL)
|
if (!(buffer))
|
||||||
goto err;
|
goto err;
|
||||||
buffer_size= (uint) (buffer_size*1.5);
|
buffer_size= (uint) (buffer_size*1.5);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
error= 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Buffer::get_size()
|
||||||
|
{
|
||||||
|
return buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Buffer::is_error()
|
||||||
|
{
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
#include <my_global.h>
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#pragma interface
|
#pragma interface
|
||||||
@ -36,11 +37,17 @@ private:
|
|||||||
/* maximum buffer size is 16Mb */
|
/* maximum buffer size is 16Mb */
|
||||||
enum { MAX_BUFFER_SIZE= 16777216 };
|
enum { MAX_BUFFER_SIZE= 16777216 };
|
||||||
size_t buffer_size;
|
size_t buffer_size;
|
||||||
|
/* Error flag. Triggered if we get an error of some kind */
|
||||||
|
int error;
|
||||||
public:
|
public:
|
||||||
Buffer()
|
Buffer(size_t buffer_size_arg= BUFFER_INITIAL_SIZE)
|
||||||
|
:buffer_size(buffer_size_arg), error(0)
|
||||||
{
|
{
|
||||||
buffer=(char *) malloc(BUFFER_INITIAL_SIZE);
|
/*
|
||||||
buffer_size= BUFFER_INITIAL_SIZE;
|
As append() will invokes realloc() anyway, it's ok if malloc returns 0
|
||||||
|
*/
|
||||||
|
if (!(buffer= (char*) my_malloc(buffer_size, MYF(0))))
|
||||||
|
buffer_size= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
~Buffer()
|
~Buffer()
|
||||||
@ -50,6 +57,8 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
char *buffer;
|
char *buffer;
|
||||||
|
int get_size();
|
||||||
|
int is_error();
|
||||||
int append(uint position, const char *string, uint len_arg);
|
int append(uint position, const char *string, uint len_arg);
|
||||||
int reserve(uint position, uint len_arg);
|
int reserve(uint position, uint len_arg);
|
||||||
};
|
};
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
#include <my_global.h>
|
|
||||||
#include <my_sys.h>
|
|
||||||
#include <mysql.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
Currently we cannot use libmysqlclient directly becouse of the linking
|
|
||||||
issues. Here we provide needed libmysqlclient functions.
|
|
||||||
TODO: to think how to use libmysqlclient code instead of copy&paste.
|
|
||||||
The other possible solution is to use simple_command directly.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const char * STDCALL
|
|
||||||
mysql_get_server_info(MYSQL *mysql)
|
|
||||||
{
|
|
||||||
return((char*) mysql->server_version);
|
|
||||||
}
|
|
||||||
|
|
||||||
int STDCALL
|
|
||||||
mysql_ping(MYSQL *mysql)
|
|
||||||
{
|
|
||||||
DBUG_ENTER("mysql_ping");
|
|
||||||
DBUG_RETURN(simple_command(mysql,COM_PING,0,0,0));
|
|
||||||
}
|
|
||||||
|
|
||||||
int STDCALL
|
|
||||||
mysql_shutdown(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level)
|
|
||||||
{
|
|
||||||
uchar level[1];
|
|
||||||
DBUG_ENTER("mysql_shutdown");
|
|
||||||
level[0]= (uchar) shutdown_level;
|
|
||||||
DBUG_RETURN(simple_command(mysql, COM_SHUTDOWN, (char *)level, 1, 0));
|
|
||||||
}
|
|
@ -170,12 +170,12 @@ int Show_instance_status::do_command(struct st_net *net,
|
|||||||
Instance *instance;
|
Instance *instance;
|
||||||
|
|
||||||
store_to_string(&send_buff, (char *) instance_name, &position);
|
store_to_string(&send_buff, (char *) instance_name, &position);
|
||||||
if ((instance= instance_map->find(instance_name, strlen(instance_name))) == NULL)
|
if (!(instance= instance_map->find(instance_name, strlen(instance_name))))
|
||||||
goto err;
|
goto err;
|
||||||
if (instance->is_running())
|
if (instance->is_running())
|
||||||
{
|
{
|
||||||
store_to_string(&send_buff, (char *) "online", &position);
|
store_to_string(&send_buff, (char *) "online", &position);
|
||||||
store_to_string(&send_buff, mysql_get_server_info(&(instance->mysql)), &position);
|
store_to_string(&send_buff, "unknown", &position);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -184,7 +184,8 @@ int Show_instance_status::do_command(struct st_net *net,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
if (send_buff.is_error() ||
|
||||||
|
my_net_write(net, send_buff.buffer, (uint) position))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +201,7 @@ err:
|
|||||||
|
|
||||||
int Show_instance_status::execute(struct st_net *net, ulong connection_id)
|
int Show_instance_status::execute(struct st_net *net, ulong connection_id)
|
||||||
{
|
{
|
||||||
if (instance_name != NULL)
|
if ((instance_name))
|
||||||
{
|
{
|
||||||
if (do_command(net, instance_name))
|
if (do_command(net, instance_name))
|
||||||
return ER_OUT_OF_RESOURCES;
|
return ER_OUT_OF_RESOURCES;
|
||||||
@ -256,52 +257,31 @@ int Show_instance_options::do_command(struct st_net *net,
|
|||||||
{
|
{
|
||||||
Instance *instance;
|
Instance *instance;
|
||||||
|
|
||||||
if ((instance= instance_map->
|
if (!(instance= instance_map->find(instance_name, strlen(instance_name))))
|
||||||
find(instance_name, strlen(instance_name))) == NULL)
|
|
||||||
goto err;
|
goto err;
|
||||||
store_to_string(&send_buff, (char *) "instance_name", &position);
|
store_to_string(&send_buff, (char *) "instance_name", &position);
|
||||||
store_to_string(&send_buff, (char *) instance_name, &position);
|
store_to_string(&send_buff, (char *) instance_name, &position);
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
if (my_net_write(net, send_buff.buffer, (uint) position))
|
||||||
goto err;
|
goto err;
|
||||||
if (instance->options.mysqld_path != NULL)
|
if ((instance->options.mysqld_path))
|
||||||
{
|
{
|
||||||
position= 0;
|
position= 0;
|
||||||
store_to_string(&send_buff, (char *) "mysqld-path", &position);
|
store_to_string(&send_buff, (char *) "mysqld-path", &position);
|
||||||
store_to_string(&send_buff,
|
store_to_string(&send_buff,
|
||||||
(char *) instance->options.mysqld_path,
|
(char *) instance->options.mysqld_path,
|
||||||
&position);
|
&position);
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
if (send_buff.is_error() ||
|
||||||
|
my_net_write(net, send_buff.buffer, (uint) position))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance->options.is_guarded != NULL)
|
if ((instance->options.nonguarded))
|
||||||
{
|
{
|
||||||
position= 0;
|
position= 0;
|
||||||
store_to_string(&send_buff, (char *) "guarded", &position);
|
store_to_string(&send_buff, (char *) "nonguarded", &position);
|
||||||
store_to_string(&send_buff, "", &position);
|
store_to_string(&send_buff, "", &position);
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
if (send_buff.is_error() ||
|
||||||
goto err;
|
my_net_write(net, send_buff.buffer, (uint) position))
|
||||||
}
|
|
||||||
|
|
||||||
if (instance->options.mysqld_user != NULL)
|
|
||||||
{
|
|
||||||
position= 0;
|
|
||||||
store_to_string(&send_buff, (char *) "admin-user", &position);
|
|
||||||
store_to_string(&send_buff,
|
|
||||||
(char *) instance->options.mysqld_user,
|
|
||||||
&position);
|
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (instance->options.mysqld_password != NULL)
|
|
||||||
{
|
|
||||||
position= 0;
|
|
||||||
store_to_string(&send_buff, (char *) "admin-password", &position);
|
|
||||||
store_to_string(&send_buff,
|
|
||||||
(char *) instance->options.mysqld_password,
|
|
||||||
&position);
|
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +298,8 @@ int Show_instance_options::do_command(struct st_net *net,
|
|||||||
store_to_string(&send_buff, option_value + 1, &position);
|
store_to_string(&send_buff, option_value + 1, &position);
|
||||||
/* join name and the value into the same option again */
|
/* join name and the value into the same option again */
|
||||||
*option_value= '=';
|
*option_value= '=';
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
if (send_buff.is_error() ||
|
||||||
|
my_net_write(net, send_buff.buffer, (uint) position))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,7 +316,7 @@ err:
|
|||||||
|
|
||||||
int Show_instance_options::execute(struct st_net *net, ulong connection_id)
|
int Show_instance_options::execute(struct st_net *net, ulong connection_id)
|
||||||
{
|
{
|
||||||
if (instance_name != NULL)
|
if ((instance_name))
|
||||||
{
|
{
|
||||||
if (do_command(net, instance_name))
|
if (do_command(net, instance_name))
|
||||||
return ER_OUT_OF_RESOURCES;
|
return ER_OUT_OF_RESOURCES;
|
||||||
@ -369,10 +350,10 @@ int Start_instance::execute(struct st_net *net, ulong connection_id)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (err_code= instance->start())
|
if ((err_code= instance->start()))
|
||||||
return err_code;
|
return err_code;
|
||||||
|
|
||||||
if (instance->options.is_guarded != NULL)
|
if (!(instance->options.nonguarded))
|
||||||
instance_map->guardian->guard(instance);
|
instance_map->guardian->guard(instance);
|
||||||
|
|
||||||
net_send_ok(net, connection_id);
|
net_send_ok(net, connection_id);
|
||||||
@ -403,7 +384,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (instance->options.is_guarded != NULL)
|
if (!(instance->options.nonguarded))
|
||||||
instance_map->guardian->
|
instance_map->guardian->
|
||||||
stop_guard(instance);
|
stop_guard(instance);
|
||||||
if ((err_code= instance->stop()))
|
if ((err_code= instance->stop()))
|
||||||
|
@ -21,9 +21,15 @@
|
|||||||
|
|
||||||
#include "guardian.h"
|
#include "guardian.h"
|
||||||
#include "instance_map.h"
|
#include "instance_map.h"
|
||||||
|
#include "instance.h"
|
||||||
#include "mysql_manager_error.h"
|
#include "mysql_manager_error.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
C_MODE_START
|
C_MODE_START
|
||||||
|
|
||||||
@ -42,13 +48,13 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg,
|
|||||||
uint monitoring_interval_arg) :
|
uint monitoring_interval_arg) :
|
||||||
Guardian_thread_args(thread_registry_arg, instance_map_arg,
|
Guardian_thread_args(thread_registry_arg, instance_map_arg,
|
||||||
monitoring_interval_arg),
|
monitoring_interval_arg),
|
||||||
thread_info(pthread_self())
|
thread_info(pthread_self()), guarded_instances(0)
|
||||||
{
|
{
|
||||||
pthread_mutex_init(&LOCK_guardian, 0);
|
pthread_mutex_init(&LOCK_guardian, 0);
|
||||||
thread_registry.register_thread(&thread_info);
|
pthread_cond_init(&COND_guardian, 0);
|
||||||
|
shutdown_requested= FALSE;
|
||||||
|
stopped= FALSE;
|
||||||
init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
|
init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
|
||||||
guarded_instances= NULL;
|
|
||||||
starting_instances= NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -57,9 +63,114 @@ Guardian_thread::~Guardian_thread()
|
|||||||
/* delay guardian destruction to the moment when no one needs it */
|
/* delay guardian destruction to the moment when no one needs it */
|
||||||
pthread_mutex_lock(&LOCK_guardian);
|
pthread_mutex_lock(&LOCK_guardian);
|
||||||
free_root(&alloc, MYF(0));
|
free_root(&alloc, MYF(0));
|
||||||
thread_registry.unregister_thread(&thread_info);
|
|
||||||
pthread_mutex_unlock(&LOCK_guardian);
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
pthread_mutex_destroy(&LOCK_guardian);
|
pthread_mutex_destroy(&LOCK_guardian);
|
||||||
|
pthread_cond_destroy(&COND_guardian);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Guardian_thread::request_shutdown(bool stop_instances_arg)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&LOCK_guardian);
|
||||||
|
/* stop instances or just clean up Guardian repository */
|
||||||
|
stop_instances(stop_instances_arg);
|
||||||
|
shutdown_requested= TRUE;
|
||||||
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Guardian_thread::process_instance(Instance *instance,
|
||||||
|
GUARD_NODE *current_node,
|
||||||
|
LIST **guarded_instances,
|
||||||
|
LIST *node)
|
||||||
|
{
|
||||||
|
uint waitchild= (uint) Instance::DEFAULT_SHUTDOWN_DELAY;
|
||||||
|
/* The amount of times, Guardian attempts to restart an instance */
|
||||||
|
int restart_retry= 100;
|
||||||
|
time_t current_time= time(NULL);
|
||||||
|
|
||||||
|
if (current_node->state == STOPPING)
|
||||||
|
{
|
||||||
|
/* this brach is executed during shutdown */
|
||||||
|
if (instance->options.shutdown_delay_val)
|
||||||
|
waitchild= instance->options.shutdown_delay_val;
|
||||||
|
|
||||||
|
/* this returns true if and only if an instance was stopped for sure */
|
||||||
|
if (instance->is_crashed())
|
||||||
|
*guarded_instances= list_delete(*guarded_instances, node);
|
||||||
|
else if ( (uint) (current_time - current_node->last_checked) > waitchild)
|
||||||
|
{
|
||||||
|
instance->kill_instance(SIGKILL);
|
||||||
|
/*
|
||||||
|
Later we do node= node->next. This is ok, as we are only removing
|
||||||
|
the node from the list. The pointer to the next one is still valid.
|
||||||
|
*/
|
||||||
|
*guarded_instances= list_delete(*guarded_instances, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance->is_running())
|
||||||
|
{
|
||||||
|
/* clear status fields */
|
||||||
|
current_node->restart_counter= 0;
|
||||||
|
current_node->crash_moment= 0;
|
||||||
|
current_node->state= STARTED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (current_node->state)
|
||||||
|
{
|
||||||
|
case NOT_STARTED:
|
||||||
|
instance->start();
|
||||||
|
current_node->last_checked= current_time;
|
||||||
|
log_info("guardian: starting instance %s",
|
||||||
|
instance->options.instance_name);
|
||||||
|
current_node->state= STARTING;
|
||||||
|
break;
|
||||||
|
case STARTED: /* fallthrough */
|
||||||
|
case STARTING: /* let the instance start or crash */
|
||||||
|
if (instance->is_crashed())
|
||||||
|
{
|
||||||
|
current_node->crash_moment= current_time;
|
||||||
|
current_node->last_checked= current_time;
|
||||||
|
current_node->state= JUST_CRASHED;
|
||||||
|
/* fallthrough -- restart an instance immediately */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
case JUST_CRASHED:
|
||||||
|
if (current_time - current_node->crash_moment <= 2)
|
||||||
|
{
|
||||||
|
instance->start();
|
||||||
|
log_info("guardian: starting instance %s",
|
||||||
|
instance->options.instance_name);
|
||||||
|
}
|
||||||
|
else current_node->state= CRASHED;
|
||||||
|
break;
|
||||||
|
case CRASHED: /* just regular restarts */
|
||||||
|
if (current_time - current_node->last_checked >
|
||||||
|
monitoring_interval)
|
||||||
|
{
|
||||||
|
if ((current_node->restart_counter < restart_retry))
|
||||||
|
{
|
||||||
|
instance->start();
|
||||||
|
current_node->last_checked= current_time;
|
||||||
|
current_node->restart_counter++;
|
||||||
|
log_info("guardian: restarting instance %s",
|
||||||
|
instance->options.instance_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
current_node->state= CRASHED_AND_ABANDONED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CRASHED_AND_ABANDONED:
|
||||||
|
break; /* do nothing */
|
||||||
|
default:
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -78,109 +189,148 @@ Guardian_thread::~Guardian_thread()
|
|||||||
void Guardian_thread::run()
|
void Guardian_thread::run()
|
||||||
{
|
{
|
||||||
Instance *instance;
|
Instance *instance;
|
||||||
LIST *loop;
|
LIST *node;
|
||||||
|
struct timespec timeout;
|
||||||
|
|
||||||
|
thread_registry.register_thread(&thread_info);
|
||||||
|
|
||||||
my_thread_init();
|
my_thread_init();
|
||||||
|
pthread_mutex_lock(&LOCK_guardian);
|
||||||
|
|
||||||
while (!thread_registry.is_shutdown())
|
/* loop, until all instances were shut down at the end */
|
||||||
|
while (!(shutdown_requested && (guarded_instances == NULL)))
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&LOCK_guardian);
|
node= guarded_instances;
|
||||||
loop= guarded_instances;
|
|
||||||
while (loop != NULL)
|
while (node != NULL)
|
||||||
{
|
{
|
||||||
instance= (Instance *) loop->data;
|
struct timespec timeout;
|
||||||
/* instance-> start already checks whether instance is running */
|
|
||||||
if (instance->start() != ER_INSTANCE_ALREADY_STARTED)
|
GUARD_NODE *current_node= (GUARD_NODE *) node->data;
|
||||||
log_info("guardian attempted to restart instance %s",
|
instance= ((GUARD_NODE *) node->data)->instance;
|
||||||
instance->options.instance_name);
|
process_instance(instance, current_node, &guarded_instances, node);
|
||||||
loop= loop->next;
|
|
||||||
|
node= node->next;
|
||||||
}
|
}
|
||||||
move_to_list(&starting_instances, &guarded_instances);
|
timeout.tv_sec= time(NULL) + monitoring_interval;
|
||||||
pthread_mutex_unlock(&LOCK_guardian);
|
timeout.tv_nsec= 0;
|
||||||
sleep(monitoring_interval);
|
|
||||||
|
/* check the loop predicate before sleeping */
|
||||||
|
if (!(shutdown_requested && (!(guarded_instances))))
|
||||||
|
pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopped= TRUE;
|
||||||
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
|
/* now, when the Guardian is stopped we can stop the IM */
|
||||||
|
thread_registry.unregister_thread(&thread_info);
|
||||||
|
thread_registry.request_shutdown();
|
||||||
my_thread_end();
|
my_thread_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Guardian_thread::start()
|
int Guardian_thread::is_stopped()
|
||||||
{
|
{
|
||||||
Instance *instance;
|
int var;
|
||||||
Instance_map::Iterator iterator(instance_map);
|
pthread_mutex_lock(&LOCK_guardian);
|
||||||
|
var= stopped;
|
||||||
instance_map->lock();
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
while ((instance= iterator.next()))
|
return var;
|
||||||
{
|
|
||||||
if ((instance->options.is_guarded != NULL) && (instance->is_running()))
|
|
||||||
if (guard(instance))
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
instance_map->unlock();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start instance guarding
|
Initialize the list of guarded instances: loop through the Instance_map and
|
||||||
|
add all of the instances, which don't have 'nonguarded' option specified.
|
||||||
|
|
||||||
SYNOPSYS
|
SYNOPSYS
|
||||||
guard()
|
Guardian_thread::init()
|
||||||
instance the instance to be guarded
|
|
||||||
|
|
||||||
DESCRIPTION
|
NOTE: One should always lock guardian before calling this routine.
|
||||||
|
|
||||||
The instance is added to the list of starting instances. Then after one guardian
|
|
||||||
loop it is moved to the guarded instances list. Usually guard() is called after we
|
|
||||||
start an instance, so we need to give some time to the instance to start.
|
|
||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
0 - ok
|
0 - ok
|
||||||
1 - error occured
|
1 - error occured
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
int Guardian_thread::init()
|
||||||
int Guardian_thread::guard(Instance *instance)
|
|
||||||
{
|
{
|
||||||
return add_instance_to_list(instance, &starting_instances);
|
Instance *instance;
|
||||||
}
|
Instance_map::Iterator iterator(instance_map);
|
||||||
|
|
||||||
|
instance_map->lock();
|
||||||
|
/* clear the list of guarded instances */
|
||||||
|
free_root(&alloc, MYF(0));
|
||||||
|
init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
|
||||||
|
guarded_instances= NULL;
|
||||||
|
|
||||||
void Guardian_thread::move_to_list(LIST **from, LIST **to)
|
while ((instance= iterator.next()))
|
||||||
{
|
|
||||||
LIST *tmp;
|
|
||||||
|
|
||||||
while (*from)
|
|
||||||
{
|
{
|
||||||
tmp= rest(*from);
|
if (!(instance->options.nonguarded))
|
||||||
*to= list_add(*to, *from);
|
if (guard(instance, TRUE)) /* do not lock guardian */
|
||||||
*from= tmp;
|
{
|
||||||
|
instance_map->unlock();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance_map->unlock();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list)
|
/*
|
||||||
|
Add instance to the Guardian list
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
guard()
|
||||||
|
instance the instance to be guarded
|
||||||
|
nolock whether we prefer do not lock Guardian here,
|
||||||
|
but use external locking instead
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
|
||||||
|
The instance is added to the guarded instances list. Usually guard() is
|
||||||
|
called after we start an instance.
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
0 - ok
|
||||||
|
1 - error occured
|
||||||
|
*/
|
||||||
|
|
||||||
|
int Guardian_thread::guard(Instance *instance, bool nolock)
|
||||||
{
|
{
|
||||||
LIST *node;
|
LIST *node;
|
||||||
|
GUARD_NODE *content;
|
||||||
|
|
||||||
node= (LIST *) alloc_root(&alloc, sizeof(LIST));
|
node= (LIST *) alloc_root(&alloc, sizeof(LIST));
|
||||||
if (node == NULL)
|
content= (GUARD_NODE *) alloc_root(&alloc, sizeof(GUARD_NODE));
|
||||||
|
|
||||||
|
if ((!(node)) || (!(content)))
|
||||||
return 1;
|
return 1;
|
||||||
/* we store the pointers to instances from the instance_map's MEM_ROOT */
|
/* we store the pointers to instances from the instance_map's MEM_ROOT */
|
||||||
node->data= (void *) instance;
|
content->instance= instance;
|
||||||
|
content->restart_counter= 0;
|
||||||
|
content->crash_moment= 0;
|
||||||
|
content->state= NOT_STARTED;
|
||||||
|
node->data= (void *) content;
|
||||||
|
|
||||||
pthread_mutex_lock(&LOCK_guardian);
|
if (nolock)
|
||||||
*list= list_add(*list, node);
|
guarded_instances= list_add(guarded_instances, node);
|
||||||
pthread_mutex_unlock(&LOCK_guardian);
|
else
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&LOCK_guardian);
|
||||||
|
guarded_instances= list_add(guarded_instances, node);
|
||||||
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: perhaps it would make sense to create a pool of the LIST elements
|
TODO: perhaps it would make sense to create a pool of the LIST nodeents
|
||||||
elements and give them upon request. Now we are loosing a bit of memory when
|
and give them upon request. Now we are loosing a bit of memory when
|
||||||
guarded instance was stopped and then restarted (since we cannot free just
|
guarded instance was stopped and then restarted (since we cannot free just
|
||||||
a piece of the MEM_ROOT).
|
a piece of the MEM_ROOT).
|
||||||
*/
|
*/
|
||||||
@ -198,7 +348,7 @@ int Guardian_thread::stop_guard(Instance *instance)
|
|||||||
We compare only pointers, as we always use pointers from the
|
We compare only pointers, as we always use pointers from the
|
||||||
instance_map's MEM_ROOT.
|
instance_map's MEM_ROOT.
|
||||||
*/
|
*/
|
||||||
if ((Instance *) node->data == instance)
|
if (((GUARD_NODE *) node->data)->instance == instance)
|
||||||
{
|
{
|
||||||
guarded_instances= list_delete(guarded_instances, node);
|
guarded_instances= list_delete(guarded_instances, node);
|
||||||
pthread_mutex_unlock(&LOCK_guardian);
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
@ -212,3 +362,73 @@ int Guardian_thread::stop_guard(Instance *instance)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start Guardian shutdown. Attempt to start instances if requested.
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
stop_instances()
|
||||||
|
stop_instances_arg whether we should stop instances at shutdown
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
|
||||||
|
Loops through the guarded_instances list and prepares them for shutdown.
|
||||||
|
If stop_instances was requested, we need to issue a stop command and change
|
||||||
|
the state accordingly. Otherwise we could simply delete an entry.
|
||||||
|
NOTE: Guardian should be locked by the calling function
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
0 - ok
|
||||||
|
1 - error occured
|
||||||
|
*/
|
||||||
|
|
||||||
|
int Guardian_thread::stop_instances(bool stop_instances_arg)
|
||||||
|
{
|
||||||
|
LIST *node;
|
||||||
|
node= guarded_instances;
|
||||||
|
while (node != NULL)
|
||||||
|
{
|
||||||
|
if (!stop_instances_arg)
|
||||||
|
{
|
||||||
|
/* just forget about an instance */
|
||||||
|
guarded_instances= list_delete(guarded_instances, node);
|
||||||
|
/*
|
||||||
|
This should still work fine, as we have only removed the
|
||||||
|
node from the list. The pointer to the next one is still valid
|
||||||
|
*/
|
||||||
|
node= node->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUARD_NODE *current_node= (GUARD_NODE *) node->data;
|
||||||
|
/*
|
||||||
|
If instance is running or was running (and now probably hanging),
|
||||||
|
request stop.
|
||||||
|
*/
|
||||||
|
if (current_node->instance->is_running() ||
|
||||||
|
(current_node->state == STARTED))
|
||||||
|
{
|
||||||
|
current_node->state= STOPPING;
|
||||||
|
current_node->last_checked= time(NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* otherwise remove it from the list */
|
||||||
|
guarded_instances= list_delete(guarded_instances, node);
|
||||||
|
/* But try to kill it anyway. Just in case */
|
||||||
|
current_node->instance->kill_instance(SIGTERM);
|
||||||
|
node= node->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Guardian_thread::lock()
|
||||||
|
{
|
||||||
|
return pthread_mutex_lock(&LOCK_guardian);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Guardian_thread::unlock()
|
||||||
|
{
|
||||||
|
return pthread_mutex_unlock(&LOCK_guardian);
|
||||||
|
}
|
||||||
|
@ -19,15 +19,16 @@
|
|||||||
#include <my_global.h>
|
#include <my_global.h>
|
||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
#include <my_list.h>
|
#include <my_list.h>
|
||||||
|
#include "thread_registry.h"
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#pragma interface
|
#pragma interface
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class Instance;
|
||||||
class Instance_map;
|
class Instance_map;
|
||||||
|
class Thread_registry;
|
||||||
#include "thread_registry.h"
|
struct GUARD_NODE;
|
||||||
#include "instance.h"
|
|
||||||
|
|
||||||
C_MODE_START
|
C_MODE_START
|
||||||
|
|
||||||
@ -35,12 +36,11 @@ pthread_handler_decl(guardian, arg);
|
|||||||
|
|
||||||
C_MODE_END
|
C_MODE_END
|
||||||
|
|
||||||
|
|
||||||
struct Guardian_thread_args
|
struct Guardian_thread_args
|
||||||
{
|
{
|
||||||
Thread_registry &thread_registry;
|
Thread_registry &thread_registry;
|
||||||
Instance_map *instance_map;
|
Instance_map *instance_map;
|
||||||
uint monitoring_interval;
|
int monitoring_interval;
|
||||||
|
|
||||||
Guardian_thread_args(Thread_registry &thread_registry_arg,
|
Guardian_thread_args(Thread_registry &thread_registry_arg,
|
||||||
Instance_map *instance_map_arg,
|
Instance_map *instance_map_arg,
|
||||||
@ -60,27 +60,67 @@ struct Guardian_thread_args
|
|||||||
class Guardian_thread: public Guardian_thread_args
|
class Guardian_thread: public Guardian_thread_args
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/* states of an instance */
|
||||||
|
enum INSTANCE_STATE { NOT_STARTED= 1, STARTING, STARTED, JUST_CRASHED,
|
||||||
|
CRASHED, CRASHED_AND_ABANDONED, STOPPING };
|
||||||
|
|
||||||
|
/*
|
||||||
|
The Guardian list node structure. Guardian utilizes it to store
|
||||||
|
guarded instances plus some additional info.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct GUARD_NODE
|
||||||
|
{
|
||||||
|
Instance *instance;
|
||||||
|
/* state of an instance (i.e. STARTED, CRASHED, etc.) */
|
||||||
|
INSTANCE_STATE state;
|
||||||
|
/* the amount of attemts to restart instance (cleaned up at success) */
|
||||||
|
int restart_counter;
|
||||||
|
/* triggered at a crash */
|
||||||
|
time_t crash_moment;
|
||||||
|
/* General time field. Used to provide timeouts (at shutdown and restart) */
|
||||||
|
time_t last_checked;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Guardian_thread(Thread_registry &thread_registry_arg,
|
Guardian_thread(Thread_registry &thread_registry_arg,
|
||||||
Instance_map *instance_map_arg,
|
Instance_map *instance_map_arg,
|
||||||
uint monitoring_interval_arg);
|
uint monitoring_interval_arg);
|
||||||
~Guardian_thread();
|
~Guardian_thread();
|
||||||
|
/* Main funtion of the thread */
|
||||||
void run();
|
void run();
|
||||||
|
/* Initialize or refresh the list of guarded instances */
|
||||||
int init();
|
int init();
|
||||||
int start();
|
/* Request guardian shutdown. Stop instances if needed */
|
||||||
int guard(Instance *instance);
|
void request_shutdown(bool stop_instances);
|
||||||
|
/* Start instance protection */
|
||||||
|
int guard(Instance *instance, bool nolock= FALSE);
|
||||||
|
/* Stop instance protection */
|
||||||
int stop_guard(Instance *instance);
|
int stop_guard(Instance *instance);
|
||||||
|
/* Returns true if guardian thread is stopped */
|
||||||
|
int is_stopped();
|
||||||
|
int lock();
|
||||||
|
int unlock();
|
||||||
|
|
||||||
|
public:
|
||||||
|
pthread_cond_t COND_guardian;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int add_instance_to_list(Instance *instance, LIST **list);
|
/* Prepares Guardian shutdown. Stops instances is needed */
|
||||||
void move_to_list(LIST **from, LIST **to);
|
int stop_instances(bool stop_instances_arg);
|
||||||
|
/* check instance state and act accordingly */
|
||||||
|
void process_instance(Instance *instance, GUARD_NODE *current_node,
|
||||||
|
LIST **guarded_instances, LIST *elem);
|
||||||
|
int stopped;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pthread_mutex_t LOCK_guardian;
|
pthread_mutex_t LOCK_guardian;
|
||||||
Thread_info thread_info;
|
Thread_info thread_info;
|
||||||
LIST *guarded_instances;
|
LIST *guarded_instances;
|
||||||
LIST *starting_instances;
|
|
||||||
MEM_ROOT alloc;
|
MEM_ROOT alloc;
|
||||||
enum { MEM_ROOT_BLOCK_SIZE= 512 };
|
enum { MEM_ROOT_BLOCK_SIZE= 512 };
|
||||||
|
/* this variable is set to TRUE when we want to stop Guardian thread */
|
||||||
|
bool shutdown_requested;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */
|
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */
|
||||||
|
@ -21,11 +21,34 @@
|
|||||||
#include "instance.h"
|
#include "instance.h"
|
||||||
#include "mysql_manager_error.h"
|
#include "mysql_manager_error.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "instance_map.h"
|
||||||
|
#include "priv.h"
|
||||||
|
|
||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <m_string.h>
|
#include <m_string.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
|
||||||
|
C_MODE_START
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proxy thread is a simple way to avoid all pitfalls of the threads
|
||||||
|
implementation in the OS (e.g. LinuxThreads). With such a thread we
|
||||||
|
don't have to process SIGCHLD, which is a tricky business if we want
|
||||||
|
to do it in a portable way.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pthread_handler_decl(proxy, arg)
|
||||||
|
{
|
||||||
|
Instance *instance= (Instance *) arg;
|
||||||
|
instance->fork_and_monitor();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
C_MODE_END
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The method starts an instance.
|
The method starts an instance.
|
||||||
|
|
||||||
@ -43,85 +66,175 @@ int Instance::start()
|
|||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
|
/* clear crash flag */
|
||||||
|
pthread_mutex_lock(&LOCK_instance);
|
||||||
|
crashed= 0;
|
||||||
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
|
|
||||||
|
|
||||||
if (!is_running())
|
if (!is_running())
|
||||||
{
|
{
|
||||||
log_info("trying to start instance %s", options.instance_name);
|
if ((pid= options.get_pid()) != 0) /* check the pidfile */
|
||||||
switch (pid= fork()) {
|
if (options.unlink_pidfile()) /* remove stalled pidfile */
|
||||||
case 0:
|
log_error("cannot remove pidfile for instance %i, this might be \
|
||||||
if (fork()) /* zombie protection */
|
since IM lacks permmissions or hasn't found the pidifle",
|
||||||
exit(0); /* parent goes bye-bye */
|
options.instance_name);
|
||||||
else
|
|
||||||
{
|
/*
|
||||||
execv(options.mysqld_path, options.argv);
|
No need to monitor this thread in the Thread_registry, as all
|
||||||
exit(1);
|
instances are to be stopped during shutdown.
|
||||||
}
|
*/
|
||||||
case -1:
|
pthread_t proxy_thd_id;
|
||||||
|
pthread_attr_t proxy_thd_attr;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
pthread_attr_init(&proxy_thd_attr);
|
||||||
|
pthread_attr_setdetachstate(&proxy_thd_attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
rc= pthread_create(&proxy_thd_id, &proxy_thd_attr, proxy,
|
||||||
|
this);
|
||||||
|
pthread_attr_destroy(&proxy_thd_attr);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
log_error("Instance::start(): pthread_create(proxy) failed");
|
||||||
return ER_CANNOT_START_INSTANCE;
|
return ER_CANNOT_START_INSTANCE;
|
||||||
default:
|
|
||||||
waitpid(pid, NULL, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* the instance is started already */
|
/* the instance is started already */
|
||||||
return ER_INSTANCE_ALREADY_STARTED;
|
return ER_INSTANCE_ALREADY_STARTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Instance::cleanup()
|
|
||||||
|
void Instance::fork_and_monitor()
|
||||||
{
|
{
|
||||||
/*
|
pid_t pid;
|
||||||
We cannot close connection in destructor, as mysql_close needs alarm
|
log_info("starting instance %s", options.instance_name);
|
||||||
services which are definitely unavailaible at the time of destructor
|
switch (pid= fork()) {
|
||||||
call.
|
case 0:
|
||||||
*/
|
execv(options.mysqld_path, options.argv);
|
||||||
if (is_connected)
|
/* exec never returns */
|
||||||
mysql_close(&mysql);
|
exit(1);
|
||||||
return 0;
|
case -1:
|
||||||
|
log_info("cannot fork() to start instance %s", options.instance_name);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
Here we wait for the child created. This process differs for systems
|
||||||
|
running LinuxThreads and POSIX Threads compliant systems. This is because
|
||||||
|
according to POSIX we could wait() for a child in any thread of the
|
||||||
|
process. While LinuxThreads require that wait() is called by the thread,
|
||||||
|
which created the child.
|
||||||
|
On the other hand we could not expect mysqld to return the pid, we
|
||||||
|
got in from fork(), to wait4() fucntion when running on LinuxThreads.
|
||||||
|
This is because MySQL shutdown thread is not the one, which was created
|
||||||
|
by our fork() call.
|
||||||
|
So basically we have two options: whether the wait() call returns only in
|
||||||
|
the creator thread, but we cannot use waitpid() since we have no idea
|
||||||
|
which pid we should wait for (in fact it should be the pid of shutdown
|
||||||
|
thread, but we don't know this one). Or we could use waitpid(), but
|
||||||
|
couldn't use wait(), because it could return in any wait() in the program.
|
||||||
|
*/
|
||||||
|
if (linuxthreads)
|
||||||
|
wait(NULL); /* LinuxThreads were detected */
|
||||||
|
else
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
/* set instance state to crashed */
|
||||||
|
pthread_mutex_lock(&LOCK_instance);
|
||||||
|
crashed= 1;
|
||||||
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Wake connection threads waiting for an instance to stop. This
|
||||||
|
is needed if a user issued command to stop an instance via
|
||||||
|
mysql connection. This is not the case if Guardian stop the thread.
|
||||||
|
*/
|
||||||
|
pthread_cond_signal(&COND_instance_stopped);
|
||||||
|
/* wake guardian */
|
||||||
|
pthread_cond_signal(&instance_map->guardian->COND_guardian);
|
||||||
|
/* thread exits */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* we should never end up here */
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Instance::Instance(): crashed(0)
|
||||||
|
{
|
||||||
|
pthread_mutex_init(&LOCK_instance, 0);
|
||||||
|
pthread_cond_init(&COND_instance_stopped, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Instance::~Instance()
|
Instance::~Instance()
|
||||||
{
|
{
|
||||||
|
pthread_cond_destroy(&COND_instance_stopped);
|
||||||
pthread_mutex_destroy(&LOCK_instance);
|
pthread_mutex_destroy(&LOCK_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Instance::is_crashed()
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
pthread_mutex_lock(&LOCK_instance);
|
||||||
|
val= crashed;
|
||||||
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Instance::is_running()
|
bool Instance::is_running()
|
||||||
{
|
{
|
||||||
|
MYSQL mysql;
|
||||||
uint port= 0;
|
uint port= 0;
|
||||||
const char *socket= NULL;
|
const char *socket= NULL;
|
||||||
|
const char *password= "321rarepassword213";
|
||||||
|
const char *username= "645rareusername945";
|
||||||
|
const char *access_denied_message= "Access denied for user";
|
||||||
|
bool return_val;
|
||||||
|
|
||||||
if (options.mysqld_port)
|
if (options.mysqld_port)
|
||||||
port= atoi(strchr(options.mysqld_port, '=') + 1);
|
port= options.mysqld_port_val;
|
||||||
|
|
||||||
if (options.mysqld_socket)
|
if (options.mysqld_socket)
|
||||||
socket= strchr(options.mysqld_socket, '=') + 1;
|
socket= strchr(options.mysqld_socket, '=') + 1;
|
||||||
|
|
||||||
pthread_mutex_lock(&LOCK_instance);
|
pthread_mutex_lock(&LOCK_instance);
|
||||||
if (!is_connected)
|
|
||||||
|
mysql_init(&mysql);
|
||||||
|
/* try to connect to a server with a fake username/password pair */
|
||||||
|
if (mysql_real_connect(&mysql, LOCAL_HOST, username,
|
||||||
|
password,
|
||||||
|
NullS, port,
|
||||||
|
socket, 0))
|
||||||
{
|
{
|
||||||
mysql_init(&mysql);
|
/*
|
||||||
if (mysql_real_connect(&mysql, LOCAL_HOST, options.mysqld_user,
|
We have successfully connected to the server using fake
|
||||||
options.mysqld_password,
|
username/password. Write a warning to the logfile.
|
||||||
NullS, port,
|
*/
|
||||||
socket, 0))
|
log_info("The Instance Manager was able to log into you server \
|
||||||
|
with faked compiled-in password while checking server status. \
|
||||||
|
Looks like something is wrong.");
|
||||||
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
|
return_val= TRUE; /* server is alive */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!strncmp(access_denied_message, mysql_error(&mysql),
|
||||||
|
sizeof(access_denied_message)-1))
|
||||||
{
|
{
|
||||||
mysql.reconnect= 1;
|
return_val= TRUE;
|
||||||
is_connected= TRUE;
|
|
||||||
pthread_mutex_unlock(&LOCK_instance);
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
mysql_close(&mysql);
|
else
|
||||||
pthread_mutex_unlock(&LOCK_instance);
|
return_val= FALSE;
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
else if (!mysql_ping(&mysql))
|
|
||||||
{
|
|
||||||
pthread_mutex_unlock(&LOCK_instance);
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mysql_close(&mysql);
|
||||||
pthread_mutex_unlock(&LOCK_instance);
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
return FALSE;
|
|
||||||
|
return return_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -139,22 +252,67 @@ bool Instance::is_running()
|
|||||||
|
|
||||||
int Instance::stop()
|
int Instance::stop()
|
||||||
{
|
{
|
||||||
if (is_running())
|
pid_t pid;
|
||||||
{
|
struct timespec timeout;
|
||||||
if (mysql_shutdown(&mysql, SHUTDOWN_DEFAULT))
|
uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY;
|
||||||
goto err;
|
|
||||||
|
|
||||||
mysql_close(&mysql);
|
if (options.shutdown_delay_val)
|
||||||
is_connected= FALSE;
|
waitchild= options.shutdown_delay_val;
|
||||||
return 0;
|
|
||||||
|
kill_instance(SIGTERM);
|
||||||
|
/* sleep on condition to wait for SIGCHLD */
|
||||||
|
|
||||||
|
timeout.tv_sec= time(NULL) + waitchild;
|
||||||
|
timeout.tv_nsec= 0;
|
||||||
|
if (pthread_mutex_lock(&LOCK_instance))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
while (options.get_pid() != 0) /* while server isn't stopped */
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status= pthread_cond_timedwait(&COND_instance_stopped,
|
||||||
|
&LOCK_instance,
|
||||||
|
&timeout);
|
||||||
|
if (status == ETIMEDOUT)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
|
|
||||||
|
kill_instance(SIGKILL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
return ER_INSTANCE_IS_NOT_STARTED;
|
return ER_INSTANCE_IS_NOT_STARTED;
|
||||||
err:
|
err:
|
||||||
return ER_STOP_INSTANCE;
|
return ER_STOP_INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Instance::kill_instance(int signum)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
/* if there are no pid, everything seems to be fine */
|
||||||
|
if ((pid= options.get_pid()) != 0) /* get pid from pidfile */
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
If we cannot kill mysqld, then it has propably crashed.
|
||||||
|
Let us try to remove staled pidfile and return successfully
|
||||||
|
as mysqld is probably stopped.
|
||||||
|
*/
|
||||||
|
if (!kill(pid, signum))
|
||||||
|
options.unlink_pidfile();
|
||||||
|
else
|
||||||
|
if (signum == SIGKILL) /* really killed instance with SIGKILL */
|
||||||
|
log_error("The instance %s is being stopped forsibly. Normally \
|
||||||
|
it should not happed. Probably the instance has been \
|
||||||
|
hanging. You should also check your IM setup",
|
||||||
|
options.instance_name);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We execute this function to initialize instance parameters.
|
We execute this function to initialize instance parameters.
|
||||||
Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY.
|
Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY.
|
||||||
@ -162,7 +320,14 @@ err:
|
|||||||
|
|
||||||
int Instance::init(const char *name_arg)
|
int Instance::init(const char *name_arg)
|
||||||
{
|
{
|
||||||
pthread_mutex_init(&LOCK_instance, 0);
|
|
||||||
|
|
||||||
return options.init(name_arg);
|
return options.init(name_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Instance::complete_initialization(Instance_map *instance_map_arg,
|
||||||
|
const char *mysqld_path,
|
||||||
|
int only_instance)
|
||||||
|
{
|
||||||
|
instance_map= instance_map_arg;
|
||||||
|
return options.complete_initialization(mysqld_path, only_instance);
|
||||||
|
}
|
||||||
|
@ -25,36 +25,44 @@
|
|||||||
#pragma interface
|
#pragma interface
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class Instance_map;
|
||||||
|
|
||||||
class Instance
|
class Instance
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Instance(): is_connected(FALSE)
|
Instance();
|
||||||
{}
|
|
||||||
~Instance();
|
~Instance();
|
||||||
|
|
||||||
int init(const char *name);
|
int init(const char *name);
|
||||||
|
int complete_initialization(Instance_map *instance_map_arg,
|
||||||
|
const char *mysqld_path, int only_instance= 0);
|
||||||
|
|
||||||
/* check if the instance is running and set up mysql connection if yes */
|
|
||||||
bool is_running();
|
bool is_running();
|
||||||
int start();
|
int start();
|
||||||
int stop();
|
int stop();
|
||||||
int cleanup();
|
/* send a signal to the instance */
|
||||||
|
void kill_instance(int signo);
|
||||||
|
int is_crashed();
|
||||||
|
void fork_and_monitor();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum { DEFAULT_SHUTDOWN_DELAY= 35 };
|
||||||
Instance_options options;
|
Instance_options options;
|
||||||
|
|
||||||
/* connection to the instance */
|
|
||||||
MYSQL mysql;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
Mutex protecting the instance. Currently we use it to avoid the
|
Mutex protecting the instance. Currently we use it to avoid the
|
||||||
double start of the instance. This happens when the instance is starting
|
double start of the instance. This happens when the instance is starting
|
||||||
and we issue the start command once more.
|
and we issue the start command once more.
|
||||||
*/
|
*/
|
||||||
|
int crashed;
|
||||||
pthread_mutex_t LOCK_instance;
|
pthread_mutex_t LOCK_instance;
|
||||||
/* Here we store the state of the following connection */
|
/*
|
||||||
bool is_connected;
|
This condition variable is used to wake threads waiting for instance to
|
||||||
|
stop in Instance::stop()
|
||||||
|
*/
|
||||||
|
pthread_cond_t COND_instance_stopped;
|
||||||
|
Instance_map *instance_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */
|
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */
|
||||||
|
@ -74,7 +74,7 @@ static void delete_instance(void *u)
|
|||||||
1 - error occured
|
1 - error occured
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int process_option(void * ctx, const char *group, const char *option)
|
static int process_option(void *ctx, const char *group, const char *option)
|
||||||
{
|
{
|
||||||
Instance_map *map= NULL;
|
Instance_map *map= NULL;
|
||||||
Instance *instance= NULL;
|
Instance *instance= NULL;
|
||||||
@ -82,7 +82,8 @@ static int process_option(void * ctx, const char *group, const char *option)
|
|||||||
|
|
||||||
map = (Instance_map*) ctx;
|
map = (Instance_map*) ctx;
|
||||||
if (strncmp(group, prefix, sizeof prefix) == 0 &&
|
if (strncmp(group, prefix, sizeof prefix) == 0 &&
|
||||||
(my_isdigit(default_charset_info, group[sizeof prefix])))
|
((my_isdigit(default_charset_info, group[sizeof prefix]))
|
||||||
|
|| group[sizeof(prefix)] == '\0'))
|
||||||
{
|
{
|
||||||
if ((instance= map->find(group, strlen(group))) == NULL)
|
if ((instance= map->find(group, strlen(group))) == NULL)
|
||||||
{
|
{
|
||||||
@ -110,13 +111,9 @@ C_MODE_END
|
|||||||
|
|
||||||
|
|
||||||
Instance_map::Instance_map(const char *default_mysqld_path_arg,
|
Instance_map::Instance_map(const char *default_mysqld_path_arg,
|
||||||
const char *default_admin_user_arg,
|
const char *first_option_arg):
|
||||||
const char *default_admin_password_arg)
|
mysqld_path(default_mysqld_path_arg), first_option(first_option_arg)
|
||||||
{
|
{
|
||||||
mysqld_path= default_mysqld_path_arg;
|
|
||||||
user= default_admin_user_arg;
|
|
||||||
password= default_admin_password_arg;
|
|
||||||
|
|
||||||
pthread_mutex_init(&LOCK_instance_map, 0);
|
pthread_mutex_init(&LOCK_instance_map, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,12 +151,15 @@ int Instance_map::flush_instances()
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
guardian->lock();
|
||||||
pthread_mutex_lock(&LOCK_instance_map);
|
pthread_mutex_lock(&LOCK_instance_map);
|
||||||
hash_free(&hash);
|
hash_free(&hash);
|
||||||
hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
|
hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
|
||||||
get_instance_key, delete_instance, 0);
|
get_instance_key, delete_instance, 0);
|
||||||
pthread_mutex_unlock(&LOCK_instance_map);
|
pthread_mutex_unlock(&LOCK_instance_map);
|
||||||
rc= load();
|
rc= load();
|
||||||
|
guardian->init();
|
||||||
|
guardian->unlock();
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,48 +181,44 @@ Instance_map::find(const char *name, uint name_len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Instance_map::complete_initialization()
|
int Instance_map::complete_initialization()
|
||||||
{
|
{
|
||||||
Instance *instance;
|
Instance *instance;
|
||||||
uint i= 0;
|
uint i= 0;
|
||||||
|
|
||||||
while (i < hash.records)
|
|
||||||
|
if (hash.records == 0) /* no instances found */
|
||||||
{
|
{
|
||||||
instance= (Instance *) hash_element(&hash, i);
|
if ((instance= new Instance) == 0)
|
||||||
instance->options.complete_initialization(mysqld_path, user, password);
|
goto err;
|
||||||
i++;
|
|
||||||
|
if (instance->init("mysqld") || add_instance(instance))
|
||||||
|
goto err_instance;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
After an instance have been added to the instance_map,
|
||||||
|
hash_free should handle it's deletion => goto err, not
|
||||||
|
err_instance.
|
||||||
|
*/
|
||||||
|
if (instance->complete_initialization(this, mysqld_path, 1))
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
while (i < hash.records)
|
||||||
|
{
|
||||||
|
instance= (Instance *) hash_element(&hash, i);
|
||||||
|
if (instance->complete_initialization(this, mysqld_path))
|
||||||
|
goto err;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
int Instance_map::cleanup()
|
err:
|
||||||
{
|
return 1;
|
||||||
Instance *instance;
|
err_instance:
|
||||||
uint i= 0;
|
delete instance;
|
||||||
|
return 1;
|
||||||
while (i < hash.records)
|
|
||||||
{
|
|
||||||
instance= (Instance *) hash_element(&hash, i);
|
|
||||||
if (instance->cleanup())
|
|
||||||
return 1;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Instance *
|
|
||||||
Instance_map::find(uint instance_number)
|
|
||||||
{
|
|
||||||
Instance *instance;
|
|
||||||
char name[80];
|
|
||||||
|
|
||||||
sprintf(name, "mysqld%i", instance_number);
|
|
||||||
pthread_mutex_lock(&LOCK_instance_map);
|
|
||||||
instance= (Instance *) hash_search(&hash, (byte *) name, strlen(name));
|
|
||||||
pthread_mutex_unlock(&LOCK_instance_map);
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -230,13 +226,30 @@ Instance_map::find(uint instance_number)
|
|||||||
|
|
||||||
int Instance_map::load()
|
int Instance_map::load()
|
||||||
{
|
{
|
||||||
int error;
|
int argc= 1;
|
||||||
|
/* this is a dummy variable for search_option_files() */
|
||||||
|
uint args_used= 0;
|
||||||
|
const char *argv_options[3];
|
||||||
|
char **argv= (char **) &argv_options;
|
||||||
|
|
||||||
error= process_default_option_files("my", process_option, (void *) this);
|
|
||||||
|
|
||||||
complete_initialization();
|
/* the name of the program may be orbitrary here in fact */
|
||||||
|
argv_options[0]= "mysqlmanager";
|
||||||
|
if (first_option != NULL)
|
||||||
|
{
|
||||||
|
argc= 2;
|
||||||
|
argv_options[1]= first_option;
|
||||||
|
argv_options[2]= '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
argv_options[1]= '\0';
|
||||||
|
|
||||||
return error;
|
if (my_search_option_files("my", &argc, (char ***) &argv, &args_used,
|
||||||
|
process_option, (void *) this) ||
|
||||||
|
complete_initialization())
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,17 +57,14 @@ public:
|
|||||||
public:
|
public:
|
||||||
/* returns a pointer to the instance or NULL, if there is no such instance */
|
/* returns a pointer to the instance or NULL, if there is no such instance */
|
||||||
Instance *find(const char *name, uint name_len);
|
Instance *find(const char *name, uint name_len);
|
||||||
Instance *find(uint instance_number);
|
|
||||||
|
|
||||||
int flush_instances();
|
int flush_instances();
|
||||||
int cleanup();
|
|
||||||
int lock();
|
int lock();
|
||||||
int unlock();
|
int unlock();
|
||||||
int init();
|
int init();
|
||||||
|
|
||||||
Instance_map(const char *default_mysqld_path_arg,
|
Instance_map(const char *default_mysqld_path_arg,
|
||||||
const char *default_admin_user_arg,
|
const char *first_option_arg);
|
||||||
const char *default_admin_password_arg);
|
|
||||||
~Instance_map();
|
~Instance_map();
|
||||||
|
|
||||||
/* loads options from config files */
|
/* loads options from config files */
|
||||||
@ -75,16 +72,14 @@ public:
|
|||||||
/* adds instance to internal hash */
|
/* adds instance to internal hash */
|
||||||
int add_instance(Instance *instance);
|
int add_instance(Instance *instance);
|
||||||
/* inits instances argv's after all options have been loaded */
|
/* inits instances argv's after all options have been loaded */
|
||||||
void complete_initialization();
|
int complete_initialization();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const char *mysqld_path;
|
const char *mysqld_path;
|
||||||
/* user an password to shutdown MySQL */
|
|
||||||
const char *user;
|
|
||||||
const char *password;
|
|
||||||
Guardian_thread *guardian;
|
Guardian_thread *guardian;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
const char *first_option;
|
||||||
enum { START_HASH_SIZE = 16 };
|
enum { START_HASH_SIZE = 16 };
|
||||||
pthread_mutex_t LOCK_instance_map;
|
pthread_mutex_t LOCK_instance_map;
|
||||||
HASH hash;
|
HASH hash;
|
||||||
|
@ -19,43 +19,170 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "instance_options.h"
|
#include "instance_options.h"
|
||||||
|
#include "parse_output.h"
|
||||||
|
#include "buffer.h"
|
||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
#include <mysql.h>
|
#include <mysql.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <m_string.h>
|
#include <m_string.h>
|
||||||
|
|
||||||
int Instance_options::complete_initialization(const char *default_path,
|
|
||||||
const char *default_user,
|
/*
|
||||||
const char *default_password)
|
Get compiled-in value of default_option
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
get_default_option()
|
||||||
|
result buffer to put found value
|
||||||
|
result_len buffer size
|
||||||
|
oprion_name the name of the option, prefixed with "--"
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
|
||||||
|
Get compile-in value of requested option from server
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
0 - ok
|
||||||
|
1 - error occured
|
||||||
|
*/
|
||||||
|
|
||||||
|
int Instance_options::get_default_option(char *result, size_t result_len,
|
||||||
|
const char *option_name)
|
||||||
{
|
{
|
||||||
/* we need to reserve space for the final zero + possible default options */
|
int position= 0;
|
||||||
if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1
|
int rc= 1;
|
||||||
+ MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*))))
|
char verbose_option[]= " --no-defaults --verbose --help";
|
||||||
goto err;
|
|
||||||
|
Buffer cmd(strlen(mysqld_path)+sizeof(verbose_option)+1);
|
||||||
|
if (cmd.get_size()) /* malloc succeeded */
|
||||||
|
{
|
||||||
|
cmd.append(position, mysqld_path, strlen(mysqld_path));
|
||||||
|
position+= strlen(mysqld_path);
|
||||||
|
cmd.append(position, verbose_option, sizeof(verbose_option) - 1);
|
||||||
|
position+= sizeof(verbose_option) - 1;
|
||||||
|
cmd.append(position, "\0", 1);
|
||||||
|
|
||||||
|
if (cmd.is_error())
|
||||||
|
goto err;
|
||||||
|
/* get the value from "mysqld --help --verbose" */
|
||||||
|
rc= parse_output_and_get_value(cmd.buffer, option_name + 2,
|
||||||
|
result, result_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
err:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (mysqld_path == NULL)
|
int Instance_options::get_pid_filename(char *result)
|
||||||
|
{
|
||||||
|
const char *pid_file= mysqld_pid_file;
|
||||||
|
char datadir[MAX_PATH_LEN];
|
||||||
|
|
||||||
|
if (!(mysqld_datadir))
|
||||||
|
{
|
||||||
|
/* we might get an error here if we have wrong path to the mysqld binary */
|
||||||
|
if (get_default_option(datadir, sizeof(datadir), "--datadir"))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1,
|
||||||
|
"/", NullS);
|
||||||
|
|
||||||
|
DBUG_ASSERT(mysqld_pid_file);
|
||||||
|
pid_file= strchr(pid_file, '=') + 1;
|
||||||
|
|
||||||
|
/* get the full path to the pidfile */
|
||||||
|
my_load_path(result, pid_file, datadir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Instance_options::unlink_pidfile()
|
||||||
|
{
|
||||||
|
return unlink(pid_file_with_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pid_t Instance_options::get_pid()
|
||||||
|
{
|
||||||
|
FILE *pid_file_stream;
|
||||||
|
|
||||||
|
/* get the pid */
|
||||||
|
if ((pid_file_stream= my_fopen(pid_file_with_path,
|
||||||
|
O_RDONLY | O_BINARY, MYF(0))) != NULL)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
fscanf(pid_file_stream, "%i", &pid);
|
||||||
|
my_fclose(pid_file_stream, MYF(0));
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Instance_options::complete_initialization(const char *default_path,
|
||||||
|
int only_instance)
|
||||||
|
{
|
||||||
|
const char *tmp;
|
||||||
|
|
||||||
|
if (!(mysqld_path))
|
||||||
{
|
{
|
||||||
if (!(mysqld_path= strdup_root(&alloc, default_path)))
|
if (!(mysqld_path= strdup_root(&alloc, default_path)))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this option must be first in the argv */
|
if (mysqld_port)
|
||||||
|
mysqld_port_val= atoi(strchr(mysqld_port, '=') + 1);
|
||||||
|
|
||||||
|
if (shutdown_delay)
|
||||||
|
shutdown_delay_val= atoi(shutdown_delay);
|
||||||
|
|
||||||
|
if (!(tmp= strdup_root(&alloc, "--no-defaults")))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (!(mysqld_pid_file))
|
||||||
|
{
|
||||||
|
char pidfilename[MAX_PATH_LEN];
|
||||||
|
char hostname[MAX_PATH_LEN];
|
||||||
|
|
||||||
|
/*
|
||||||
|
If we created only one istance [mysqld], because no config. files were
|
||||||
|
found, we would like to model mysqld pid file values.
|
||||||
|
*/
|
||||||
|
if (!gethostname(hostname, sizeof(hostname) - 1))
|
||||||
|
(only_instance == 0) ?
|
||||||
|
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name, "-",
|
||||||
|
hostname, ".pid", NullS):
|
||||||
|
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname,
|
||||||
|
".pid", NullS);
|
||||||
|
|
||||||
|
else
|
||||||
|
(only_instance == 0) ?
|
||||||
|
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name,
|
||||||
|
".pid", NullS):
|
||||||
|
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", "mysql",
|
||||||
|
".pid", NullS);
|
||||||
|
|
||||||
|
add_option(pidfilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_pid_filename(pid_file_with_path))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
/* we need to reserve space for the final zero + possible default options */
|
||||||
|
if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1
|
||||||
|
+ MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*))))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
/* the path must be first in the argv */
|
||||||
if (add_to_argv(mysqld_path))
|
if (add_to_argv(mysqld_path))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* the following options are not for argv */
|
if (add_to_argv(tmp))
|
||||||
if (mysqld_user == NULL)
|
goto err;
|
||||||
{
|
|
||||||
if (!(mysqld_user= strdup_root(&alloc, default_user)))
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mysqld_password == NULL)
|
|
||||||
{
|
|
||||||
if (!(mysqld_password= strdup_root(&alloc, default_password)))
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy((gptr) (argv + filled_default_options), options_array.buffer,
|
memcpy((gptr) (argv + filled_default_options), options_array.buffer,
|
||||||
options_array.elements*sizeof(char*));
|
options_array.elements*sizeof(char*));
|
||||||
@ -102,9 +229,8 @@ int Instance_options::add_option(const char* option)
|
|||||||
{"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD},
|
{"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD},
|
||||||
{"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD},
|
{"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD},
|
||||||
{"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE},
|
{"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE},
|
||||||
{"--admin-user=", 13, &mysqld_user, SAVE_VALUE},
|
{"--nonguarded", 9, &nonguarded, SAVE_WHOLE},
|
||||||
{"--admin-password=", 17, &mysqld_password, SAVE_VALUE},
|
{"--shutdown_delay", 9, &shutdown_delay, SAVE_VALUE},
|
||||||
{"--guarded", 9, &is_guarded, SAVE_WHOLE},
|
|
||||||
{NULL, 0, NULL, 0}
|
{NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
struct selected_options_st *selected_options;
|
struct selected_options_st *selected_options;
|
||||||
@ -131,6 +257,9 @@ int Instance_options::add_option(const char* option)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if we haven't returned earlier we should just save the option */
|
||||||
|
insert_dynamic(&options_array,(gptr) &tmp);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
@ -142,12 +271,24 @@ int Instance_options::add_to_argv(const char* option)
|
|||||||
{
|
{
|
||||||
DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS);
|
DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS);
|
||||||
|
|
||||||
if (option != NULL)
|
if ((option))
|
||||||
argv[filled_default_options++]= (char *) option;
|
argv[filled_default_options++]= (char *) option;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* function for debug purposes */
|
||||||
|
void Instance_options::print_argv()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
printf("printing out an instance %s argv:\n", instance_name);
|
||||||
|
for (i=0; argv[i] != NULL; i++)
|
||||||
|
{
|
||||||
|
printf("argv: %s\n", argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We execute this function to initialize some options.
|
We execute this function to initialize some options.
|
||||||
Return value: 0 - ok. 1 - unable to allocate memory.
|
Return value: 0 - ok. 1 - unable to allocate memory.
|
||||||
|
@ -37,22 +37,31 @@ class Instance_options
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Instance_options() :
|
Instance_options() :
|
||||||
mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0),
|
mysqld_socket(0), mysqld_datadir(0),
|
||||||
mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), mysqld_user(0),
|
mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0),
|
||||||
mysqld_password(0), is_guarded(0), filled_default_options(0)
|
mysqld_port_val(0), mysqld_path(0), nonguarded(0), shutdown_delay(0),
|
||||||
|
shutdown_delay_val(0), filled_default_options(0)
|
||||||
{}
|
{}
|
||||||
~Instance_options();
|
~Instance_options();
|
||||||
/* fills in argv */
|
/* fills in argv */
|
||||||
int complete_initialization(const char *default_path,
|
int complete_initialization(const char *default_path, int only_instance);
|
||||||
const char *default_user,
|
|
||||||
const char *default_password);
|
|
||||||
|
|
||||||
int add_option(const char* option);
|
int add_option(const char* option);
|
||||||
int init(const char *instance_name_arg);
|
int init(const char *instance_name_arg);
|
||||||
|
pid_t get_pid();
|
||||||
|
int get_pid_filename(char *result);
|
||||||
|
int unlink_pidfile();
|
||||||
|
void print_argv();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 1 };
|
/*
|
||||||
|
We need this value to be greater or equal then FN_REFLEN found in
|
||||||
|
my_global.h to use my_load_path()
|
||||||
|
*/
|
||||||
|
enum { MAX_PATH_LEN= 512 };
|
||||||
|
enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 2 };
|
||||||
enum { MEM_ROOT_BLOCK_SIZE= 512 };
|
enum { MEM_ROOT_BLOCK_SIZE= 512 };
|
||||||
|
char pid_file_with_path[MAX_PATH_LEN];
|
||||||
char **argv;
|
char **argv;
|
||||||
/* We need the some options, so we store them as a separate pointers */
|
/* We need the some options, so we store them as a separate pointers */
|
||||||
const char *mysqld_socket;
|
const char *mysqld_socket;
|
||||||
@ -60,15 +69,19 @@ public:
|
|||||||
const char *mysqld_bind_address;
|
const char *mysqld_bind_address;
|
||||||
const char *mysqld_pid_file;
|
const char *mysqld_pid_file;
|
||||||
const char *mysqld_port;
|
const char *mysqld_port;
|
||||||
uint instance_name_len;
|
uint mysqld_port_val;
|
||||||
const char *instance_name;
|
const char *instance_name;
|
||||||
|
uint instance_name_len;
|
||||||
const char *mysqld_path;
|
const char *mysqld_path;
|
||||||
const char *mysqld_user;
|
const char *nonguarded;
|
||||||
const char *mysqld_password;
|
const char *shutdown_delay;
|
||||||
const char *is_guarded;
|
uint shutdown_delay_val;
|
||||||
|
/* this value is computed and cashed here */
|
||||||
DYNAMIC_ARRAY options_array;
|
DYNAMIC_ARRAY options_array;
|
||||||
private:
|
private:
|
||||||
int add_to_argv(const char *option);
|
int add_to_argv(const char *option);
|
||||||
|
int get_default_option(char *result, size_t result_len,
|
||||||
|
const char *option_name);
|
||||||
private:
|
private:
|
||||||
uint filled_default_options;
|
uint filled_default_options;
|
||||||
MEM_ROOT alloc;
|
MEM_ROOT alloc;
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "instance_map.h"
|
#include "instance_map.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mysql_connection.h"
|
#include "mysql_connection.h"
|
||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -57,13 +58,11 @@ Listener_thread::Listener_thread(const Listener_thread_args &args) :
|
|||||||
,total_connection_count(0)
|
,total_connection_count(0)
|
||||||
,thread_info(pthread_self())
|
,thread_info(pthread_self())
|
||||||
{
|
{
|
||||||
thread_registry.register_thread(&thread_info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Listener_thread::~Listener_thread()
|
Listener_thread::~Listener_thread()
|
||||||
{
|
{
|
||||||
thread_registry.unregister_thread(&thread_info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -82,6 +81,19 @@ void Listener_thread::run()
|
|||||||
enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size
|
enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size
|
||||||
int flags;
|
int flags;
|
||||||
int arg= 1; /* value to be set by setsockopt */
|
int arg= 1; /* value to be set by setsockopt */
|
||||||
|
int unix_socket;
|
||||||
|
uint im_port;
|
||||||
|
/* we use this var to check whether we are running on LinuxThreads */
|
||||||
|
pid_t thread_pid;
|
||||||
|
|
||||||
|
thread_pid= getpid();
|
||||||
|
/* set global variable */
|
||||||
|
linuxthreads= (thread_pid != manager_pid);
|
||||||
|
|
||||||
|
thread_registry.register_thread(&thread_info);
|
||||||
|
|
||||||
|
my_thread_init();
|
||||||
|
|
||||||
/* I. prepare 'listen' sockets */
|
/* I. prepare 'listen' sockets */
|
||||||
|
|
||||||
int ip_socket= socket(AF_INET, SOCK_STREAM, 0);
|
int ip_socket= socket(AF_INET, SOCK_STREAM, 0);
|
||||||
@ -89,8 +101,7 @@ void Listener_thread::run()
|
|||||||
{
|
{
|
||||||
log_error("Listener_thead::run(): socket(AF_INET) failed, %s",
|
log_error("Listener_thead::run(): socket(AF_INET) failed, %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in ip_socket_address;
|
struct sockaddr_in ip_socket_address;
|
||||||
@ -104,7 +115,7 @@ void Listener_thread::run()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
im_bind_addr= htonl(INADDR_ANY);
|
im_bind_addr= htonl(INADDR_ANY);
|
||||||
uint im_port= options.port_number;
|
im_port= options.port_number;
|
||||||
|
|
||||||
ip_socket_address.sin_family= AF_INET;
|
ip_socket_address.sin_family= AF_INET;
|
||||||
ip_socket_address.sin_addr.s_addr = im_bind_addr;
|
ip_socket_address.sin_addr.s_addr = im_bind_addr;
|
||||||
@ -119,16 +130,14 @@ void Listener_thread::run()
|
|||||||
{
|
{
|
||||||
log_error("Listener_thread::run(): bind(ip socket) failed, '%s'",
|
log_error("Listener_thread::run(): bind(ip socket) failed, '%s'",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listen(ip_socket, LISTEN_BACK_LOG_SIZE))
|
if (listen(ip_socket, LISTEN_BACK_LOG_SIZE))
|
||||||
{
|
{
|
||||||
log_error("Listener_thread::run(): listen(ip socket) failed, %s",
|
log_error("Listener_thread::run(): listen(ip socket) failed, %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
/* set the socket nonblocking */
|
/* set the socket nonblocking */
|
||||||
flags= fcntl(ip_socket, F_GETFL, 0);
|
flags= fcntl(ip_socket, F_GETFL, 0);
|
||||||
@ -140,13 +149,12 @@ void Listener_thread::run()
|
|||||||
log_info("accepting connections on ip socket");
|
log_info("accepting connections on ip socket");
|
||||||
|
|
||||||
/*--------------------------------------------------------------*/
|
/*--------------------------------------------------------------*/
|
||||||
int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0);
|
unix_socket= socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
if (unix_socket == INVALID_SOCKET)
|
if (unix_socket == INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s",
|
log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_un unix_socket_address;
|
struct sockaddr_un unix_socket_address;
|
||||||
@ -169,8 +177,7 @@ void Listener_thread::run()
|
|||||||
log_error("Listener_thread::run(): bind(unix socket) failed, "
|
log_error("Listener_thread::run(): bind(unix socket) failed, "
|
||||||
"socket file name is '%s', error '%s'",
|
"socket file name is '%s', error '%s'",
|
||||||
unix_socket_address.sun_path, strerror(errno));
|
unix_socket_address.sun_path, strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
umask(old_mask);
|
umask(old_mask);
|
||||||
|
|
||||||
@ -178,8 +185,7 @@ void Listener_thread::run()
|
|||||||
{
|
{
|
||||||
log_error("Listener_thread::run(): listen(unix socket) failed, %s",
|
log_error("Listener_thread::run(): listen(unix socket) failed, %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set the socket nonblocking */
|
/* set the socket nonblocking */
|
||||||
@ -205,7 +211,15 @@ void Listener_thread::run()
|
|||||||
while (thread_registry.is_shutdown() == false)
|
while (thread_registry.is_shutdown() == false)
|
||||||
{
|
{
|
||||||
fd_set read_fds_arg= read_fds;
|
fd_set read_fds_arg= read_fds;
|
||||||
|
|
||||||
|
/*
|
||||||
|
When using valgrind 2.0 this syscall doesn't get kicked off by a
|
||||||
|
signal during shutdown. This results in failing assert
|
||||||
|
(Thread_registry::~Thread_registry). Valgrind 2.2 works fine.
|
||||||
|
*/
|
||||||
int rc= select(n, &read_fds_arg, 0, 0, 0);
|
int rc= select(n, &read_fds_arg, 0, 0, 0);
|
||||||
|
|
||||||
|
|
||||||
if (rc == -1 && errno != EINTR)
|
if (rc == -1 && errno != EINTR)
|
||||||
log_error("Listener_thread::run(): select() failed, %s",
|
log_error("Listener_thread::run(): select() failed, %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
@ -256,6 +270,16 @@ void Listener_thread::run()
|
|||||||
close(unix_socket);
|
close(unix_socket);
|
||||||
close(ip_socket);
|
close(ip_socket);
|
||||||
unlink(unix_socket_address.sun_path);
|
unlink(unix_socket_address.sun_path);
|
||||||
|
|
||||||
|
thread_registry.unregister_thread(&thread_info);
|
||||||
|
my_thread_end();
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
thread_registry.unregister_thread(&thread_info);
|
||||||
|
thread_registry.request_shutdown();
|
||||||
|
my_thread_end();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@
|
|||||||
SYNOPSYS
|
SYNOPSYS
|
||||||
log()
|
log()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline void log(FILE *file, const char *format, va_list args)
|
static inline void log(FILE *file, const char *format, va_list args)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
log() should be thread-safe; it implies that we either call fprintf()
|
log() should be thread-safe; it implies that we either call fprintf()
|
||||||
|
@ -17,14 +17,14 @@
|
|||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Logging facilities.
|
Logging facilities.
|
||||||
|
|
||||||
Two logging streams are supported: error log and info log. Additionally
|
Two logging streams are supported: error log and info log. Additionally
|
||||||
libdbug may be used for debug information output.
|
libdbug may be used for debug information output.
|
||||||
ANSI C buffered I/O is used to perform logging.
|
ANSI C buffered I/O is used to perform logging.
|
||||||
Logging is performed via stdout/stder, so one can reopen them to point to
|
Logging is performed via stdout/stder, so one can reopen them to point to
|
||||||
ordinary files. To initialize loggin environment log_init() must be called.
|
ordinary files. To initialize loggin environment log_init() must be called.
|
||||||
|
|
||||||
Rationale:
|
Rationale:
|
||||||
- no MYSQL_LOG as it has BIN mode, and not easy to fetch from sql_class.h
|
- no MYSQL_LOG as it has BIN mode, and not easy to fetch from sql_class.h
|
||||||
- no constructors/desctructors to make logging available all the time
|
- no constructors/desctructors to make logging available all the time
|
||||||
|
@ -16,12 +16,7 @@
|
|||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
#include <my_global.h>
|
#include "priv.h"
|
||||||
#include <my_sys.h>
|
|
||||||
#include <m_string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <thr_alarm.h>
|
|
||||||
|
|
||||||
#include "thread_registry.h"
|
#include "thread_registry.h"
|
||||||
#include "listener.h"
|
#include "listener.h"
|
||||||
#include "instance_map.h"
|
#include "instance_map.h"
|
||||||
@ -30,6 +25,14 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "guardian.h"
|
#include "guardian.h"
|
||||||
|
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <m_string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <thr_alarm.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
|
||||||
static int create_pid_file(const char *pid_file_name)
|
static int create_pid_file(const char *pid_file_name)
|
||||||
{
|
{
|
||||||
if (FILE *pid_file= my_fopen(pid_file_name,
|
if (FILE *pid_file= my_fopen(pid_file_name,
|
||||||
@ -65,9 +68,7 @@ void manager(const Options &options)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
User_map user_map;
|
User_map user_map;
|
||||||
Instance_map instance_map(options.default_mysqld_path,
|
Instance_map instance_map(options.default_mysqld_path, options.first_option);
|
||||||
options.default_admin_user,
|
|
||||||
options.default_admin_password);
|
|
||||||
Guardian_thread guardian_thread(thread_registry,
|
Guardian_thread guardian_thread(thread_registry,
|
||||||
&instance_map,
|
&instance_map,
|
||||||
options.monitoring_interval);
|
options.monitoring_interval);
|
||||||
@ -75,10 +76,22 @@ void manager(const Options &options)
|
|||||||
Listener_thread_args listener_args(thread_registry, options, user_map,
|
Listener_thread_args listener_args(thread_registry, options, user_map,
|
||||||
instance_map);
|
instance_map);
|
||||||
|
|
||||||
|
manager_pid= getpid();
|
||||||
instance_map.guardian= &guardian_thread;
|
instance_map.guardian= &guardian_thread;
|
||||||
|
|
||||||
if (instance_map.init() || user_map.init() || instance_map.load() ||
|
if (instance_map.init() || user_map.init())
|
||||||
user_map.load(options.password_file_name))
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
if (instance_map.load())
|
||||||
|
{
|
||||||
|
log_error("Cannot init instances repository. This might be caused by "
|
||||||
|
"the wrong config file options. For instance, missing mysqld "
|
||||||
|
"binary. Aborting.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_map.load(options.password_file_name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* write pid file */
|
/* write pid file */
|
||||||
@ -126,6 +139,12 @@ void manager(const Options &options)
|
|||||||
pthread_attr_t guardian_thd_attr;
|
pthread_attr_t guardian_thd_attr;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE: Guardian should be shutdown first. Only then all other threads
|
||||||
|
need to be stopped. This should be done, as guardian is responsible for
|
||||||
|
shutting down the instances, and this is a long operation.
|
||||||
|
*/
|
||||||
|
|
||||||
pthread_attr_init(&guardian_thd_attr);
|
pthread_attr_init(&guardian_thd_attr);
|
||||||
pthread_attr_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED);
|
pthread_attr_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED);
|
||||||
rc= pthread_create(&guardian_thd_id, &guardian_thd_attr, guardian,
|
rc= pthread_create(&guardian_thd_id, &guardian_thd_attr, guardian,
|
||||||
@ -153,26 +172,50 @@ void manager(const Options &options)
|
|||||||
more then 10 alarms at the same time.
|
more then 10 alarms at the same time.
|
||||||
*/
|
*/
|
||||||
init_thr_alarm(10);
|
init_thr_alarm(10);
|
||||||
|
/* init list of guarded instances */
|
||||||
|
guardian_thread.lock();
|
||||||
|
|
||||||
|
guardian_thread.init();
|
||||||
|
|
||||||
|
guardian_thread.unlock();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Now we can init the list of guarded instances. We have to do it after
|
After the list of guarded instances have been initialized,
|
||||||
alarm structures initialization as we have to use net_* functions while
|
Guardian should start them.
|
||||||
making the list. And they in their turn need alarms for timeout suppport.
|
|
||||||
*/
|
*/
|
||||||
guardian_thread.start();
|
pthread_cond_signal(&guardian_thread.COND_guardian);
|
||||||
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
while (!shutdown_complete)
|
while (!shutdown_complete)
|
||||||
{
|
{
|
||||||
sigwait(&mask, &signo);
|
int status= 0;
|
||||||
|
|
||||||
|
if ((status= my_sigwait(&mask, &signo)) != 0)
|
||||||
|
{
|
||||||
|
log_error("sigwait() failed");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
switch (signo)
|
switch (signo)
|
||||||
{
|
{
|
||||||
case THR_SERVER_ALARM:
|
case THR_SERVER_ALARM:
|
||||||
process_alarm(signo);
|
process_alarm(signo);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
thread_registry.deliver_shutdown();
|
{
|
||||||
shutdown_complete= TRUE;
|
if (!guardian_thread.is_stopped())
|
||||||
|
{
|
||||||
|
bool stop_instances= true;
|
||||||
|
guardian_thread.request_shutdown(stop_instances);
|
||||||
|
pthread_cond_signal(&guardian_thread.COND_guardian);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
thread_registry.deliver_shutdown();
|
||||||
|
shutdown_complete= TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,9 +224,6 @@ err:
|
|||||||
/* delete the pid file */
|
/* delete the pid file */
|
||||||
my_delete(options.pid_file_name, MYF(0));
|
my_delete(options.pid_file_name, MYF(0));
|
||||||
|
|
||||||
/* close permanent connections to the running instances */
|
|
||||||
instance_map.cleanup();
|
|
||||||
|
|
||||||
/* free alarm structures */
|
/* free alarm structures */
|
||||||
end_thr_alarm(1);
|
end_thr_alarm(1);
|
||||||
/* don't pthread_exit to kill all threads who did not shut down in time */
|
/* don't pthread_exit to kill all threads who did not shut down in time */
|
||||||
|
@ -45,8 +45,8 @@ static const char *mysqld_error_message(unsigned sql_errno)
|
|||||||
case ER_BAD_INSTANCE_NAME:
|
case ER_BAD_INSTANCE_NAME:
|
||||||
return "Bad instance name. Check that the instance with such a name exists";
|
return "Bad instance name. Check that the instance with such a name exists";
|
||||||
case ER_INSTANCE_IS_NOT_STARTED:
|
case ER_INSTANCE_IS_NOT_STARTED:
|
||||||
return "Cannot stop instance. Perhaps the instance is not started or you"
|
return "Cannot stop instance. Perhaps the instance is not started, or was started"
|
||||||
" have specified wrong username/password in the config file";
|
"manually, so IM cannot find the pidfile.";
|
||||||
case ER_INSTANCE_ALREADY_STARTED:
|
case ER_INSTANCE_ALREADY_STARTED:
|
||||||
return "The instance is already started";
|
return "The instance is already started";
|
||||||
case ER_CANNOT_START_INSTANCE:
|
case ER_CANNOT_START_INSTANCE:
|
||||||
|
@ -82,7 +82,6 @@ private:
|
|||||||
private:
|
private:
|
||||||
/* Names are conventionally the same as in mysqld */
|
/* Names are conventionally the same as in mysqld */
|
||||||
int check_connection();
|
int check_connection();
|
||||||
int check_user(const char *user, const char *password);
|
|
||||||
int do_command();
|
int do_command();
|
||||||
int dispatch_command(enum enum_server_command command,
|
int dispatch_command(enum enum_server_command command,
|
||||||
const char *text, uint len);
|
const char *text, uint len);
|
||||||
@ -287,12 +286,6 @@ int Mysql_connection_thread::check_connection()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Mysql_connection_thread::check_user(const char *user, const char *password)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Mysql_connection_thread::do_command()
|
int Mysql_connection_thread::do_command()
|
||||||
{
|
{
|
||||||
char *packet;
|
char *packet;
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -54,6 +56,8 @@
|
|||||||
static void init_environment(char *progname);
|
static void init_environment(char *progname);
|
||||||
static void daemonize(const char *log_file_name);
|
static void daemonize(const char *log_file_name);
|
||||||
static void angel(const Options &options);
|
static void angel(const Options &options);
|
||||||
|
static struct passwd *check_user(const char *user);
|
||||||
|
static int set_user(const char *user, struct passwd *user_info);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -68,7 +72,20 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
init_environment(argv[0]);
|
init_environment(argv[0]);
|
||||||
Options options;
|
Options options;
|
||||||
options.load(argc, argv);
|
struct passwd *user_info;
|
||||||
|
|
||||||
|
if (options.load(argc, argv))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if ((user_info= check_user(options.user)))
|
||||||
|
{
|
||||||
|
if (set_user(options.user, user_info))
|
||||||
|
{
|
||||||
|
options.cleanup();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.run_as_service)
|
if (options.run_as_service)
|
||||||
{
|
{
|
||||||
/* forks, and returns only in child */
|
/* forks, and returns only in child */
|
||||||
@ -77,11 +94,84 @@ int main(int argc, char *argv[])
|
|||||||
angel(options);
|
angel(options);
|
||||||
}
|
}
|
||||||
manager(options);
|
manager(options);
|
||||||
|
options.cleanup();
|
||||||
|
my_end(0);
|
||||||
return 0;
|
return 0;
|
||||||
|
err:
|
||||||
|
my_end(0);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************* Auxilary functions implementation **********************/
|
/******************* Auxilary functions implementation **********************/
|
||||||
|
|
||||||
|
/* Change to run as another user if started with --user */
|
||||||
|
|
||||||
|
static struct passwd *check_user(const char *user)
|
||||||
|
{
|
||||||
|
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
|
||||||
|
struct passwd *user_info;
|
||||||
|
uid_t user_id= geteuid();
|
||||||
|
|
||||||
|
/* Don't bother if we aren't superuser */
|
||||||
|
if (user_id)
|
||||||
|
{
|
||||||
|
if (user)
|
||||||
|
{
|
||||||
|
/* Don't give a warning, if real user is same as given with --user */
|
||||||
|
user_info= getpwnam(user);
|
||||||
|
if ((!user_info || user_id != user_info->pw_uid))
|
||||||
|
log_info("One can only use the --user switch if running as root\n");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
log_info("You are running mysqlmanager as root! This might introduce security problems. It is safer to use --user option istead.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!strcmp(user, "root"))
|
||||||
|
return NULL; /* Avoid problem with dynamic libraries */
|
||||||
|
if (!(user_info= getpwnam(user)))
|
||||||
|
{
|
||||||
|
/* Allow a numeric uid to be used */
|
||||||
|
const char *pos;
|
||||||
|
for (pos= user; my_isdigit(default_charset_info, *pos); pos++) ;
|
||||||
|
if (*pos) /* Not numeric id */
|
||||||
|
goto err;
|
||||||
|
if (!(user_info= getpwuid(atoi(user))))
|
||||||
|
goto err;
|
||||||
|
else
|
||||||
|
return user_info;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return user_info;
|
||||||
|
|
||||||
|
err:
|
||||||
|
log_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n", user);
|
||||||
|
#endif
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_user(const char *user, struct passwd *user_info)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(user_info);
|
||||||
|
#ifdef HAVE_INITGROUPS
|
||||||
|
initgroups((char*) user,user_info->pw_gid);
|
||||||
|
#endif
|
||||||
|
if (setgid(user_info->pw_gid) == -1)
|
||||||
|
{
|
||||||
|
log_error("setgid() failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (setuid(user_info->pw_uid) == -1)
|
||||||
|
{
|
||||||
|
log_error("setuid() failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Init environment, common for daemon and non-daemon
|
Init environment, common for daemon and non-daemon
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include <my_global.h>
|
#include <my_global.h>
|
||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
#include <my_getopt.h>
|
#include <my_getopt.h>
|
||||||
|
#include <m_string.h>
|
||||||
|
#include <mysql_com.h>
|
||||||
|
|
||||||
#include "priv.h"
|
#include "priv.h"
|
||||||
|
|
||||||
@ -35,11 +37,13 @@ const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME);
|
|||||||
const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME);
|
const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME);
|
||||||
const char *Options::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
|
const char *Options::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
|
||||||
const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH);
|
const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH);
|
||||||
const char *Options::default_admin_user= QUOTE(DEFAULT_USER);
|
const char *Options::first_option= 0; /* No default value */
|
||||||
const char *Options::default_admin_password= QUOTE(DEFAULT_PASSWORD);
|
const char *Options::bind_address= 0; /* No default value */
|
||||||
const char *Options::bind_address= 0; /* No default value */
|
const char *Options::user= 0; /* No default value */
|
||||||
uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL;
|
uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL;
|
||||||
uint Options::port_number= DEFAULT_PORT;
|
uint Options::port_number= DEFAULT_PORT;
|
||||||
|
/* just to declare */
|
||||||
|
char **Options::saved_argv;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
List of options, accepted by the instance manager.
|
List of options, accepted by the instance manager.
|
||||||
@ -54,9 +58,6 @@ enum options {
|
|||||||
OPT_MYSQLD_PATH,
|
OPT_MYSQLD_PATH,
|
||||||
OPT_RUN_AS_SERVICE,
|
OPT_RUN_AS_SERVICE,
|
||||||
OPT_USER,
|
OPT_USER,
|
||||||
OPT_PASSWORD,
|
|
||||||
OPT_DEFAULT_ADMIN_USER,
|
|
||||||
OPT_DEFAULT_ADMIN_PASSWORD,
|
|
||||||
OPT_MONITORING_INTERVAL,
|
OPT_MONITORING_INTERVAL,
|
||||||
OPT_PORT,
|
OPT_PORT,
|
||||||
OPT_BIND_ADDRESS
|
OPT_BIND_ADDRESS
|
||||||
@ -79,13 +80,16 @@ static struct my_option my_long_options[] =
|
|||||||
(gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name,
|
(gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name,
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
|
{ "passwd", 'P', "Prepare entry for passwd file and exit.", 0, 0, 0,
|
||||||
|
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "bind-address", OPT_BIND_ADDRESS, "Bind address to use for connection.",
|
{ "bind-address", OPT_BIND_ADDRESS, "Bind address to use for connection.",
|
||||||
(gptr *) &Options::bind_address, (gptr *) &Options::bind_address,
|
(gptr *) &Options::bind_address, (gptr *) &Options::bind_address,
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "port", OPT_PORT, "Port number to use for connections",
|
{ "port", OPT_PORT, "Port number to use for connections",
|
||||||
(gptr *) &Options::port_number, (gptr *) &Options::port_number,
|
(gptr *) &Options::port_number, (gptr *) &Options::port_number,
|
||||||
0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_UINT, REQUIRED_ARG, DEFAULT_PORT, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "password-file", OPT_PASSWORD_FILE, "Look for Instane Manager users"
|
{ "password-file", OPT_PASSWORD_FILE, "Look for Instane Manager users"
|
||||||
" and passwords here.",
|
" and passwords here.",
|
||||||
@ -98,28 +102,22 @@ static struct my_option my_long_options[] =
|
|||||||
(gptr *) &Options::default_mysqld_path, (gptr *) &Options::default_mysqld_path,
|
(gptr *) &Options::default_mysqld_path, (gptr *) &Options::default_mysqld_path,
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "default-admin-user", OPT_DEFAULT_ADMIN_USER, "Username to shutdown MySQL"
|
|
||||||
" instances.",
|
|
||||||
(gptr *) &Options::default_admin_user,
|
|
||||||
(gptr *) &Options::default_admin_user,
|
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
|
||||||
|
|
||||||
{ "default-admin-password", OPT_DEFAULT_ADMIN_PASSWORD, "Password to"
|
|
||||||
"shutdown MySQL instances.",
|
|
||||||
(gptr *) &Options::default_admin_password,
|
|
||||||
(gptr *) &Options::default_admin_password,
|
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
|
||||||
|
|
||||||
{ "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor instances"
|
{ "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor instances"
|
||||||
" in seconds.",
|
" in seconds.",
|
||||||
(gptr *) &Options::monitoring_interval,
|
(gptr *) &Options::monitoring_interval,
|
||||||
(gptr *) &Options::monitoring_interval,
|
(gptr *) &Options::monitoring_interval,
|
||||||
0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_UINT, REQUIRED_ARG, DEFAULT_MONITORING_INTERVAL,
|
||||||
|
0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "run-as-service", OPT_RUN_AS_SERVICE,
|
{ "run-as-service", OPT_RUN_AS_SERVICE,
|
||||||
"Daemonize and start angel process.", (gptr *) &Options::run_as_service,
|
"Daemonize and start angel process.", (gptr *) &Options::run_as_service,
|
||||||
0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
|
0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
|
||||||
|
|
||||||
|
{ "user", OPT_USER, "Username to start mysqlmanager",
|
||||||
|
(gptr *) &Options::user,
|
||||||
|
(gptr *) &Options::user,
|
||||||
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "version", 'V', "Output version information and exit.", 0, 0, 0,
|
{ "version", 'V', "Output version information and exit.", 0, 0, 0,
|
||||||
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
@ -150,6 +148,38 @@ static void usage()
|
|||||||
my_print_variables(my_long_options);
|
my_print_variables(my_long_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void passwd()
|
||||||
|
{
|
||||||
|
char user[1024], *p;
|
||||||
|
const char *pw1, *pw2;
|
||||||
|
char pw1msg[]= "Enter password: ";
|
||||||
|
char pw2msg[]= "Re-type password: ";
|
||||||
|
char crypted_pw[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1];
|
||||||
|
|
||||||
|
fprintf(stderr, "Creating record for new user.\n");
|
||||||
|
fprintf(stderr, "Enter user name: ");
|
||||||
|
if (! fgets(user, sizeof(user), stdin))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Unable to read user.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((p= strchr(user, '\n'))) *p= 0;
|
||||||
|
|
||||||
|
pw1= get_tty_password(pw1msg);
|
||||||
|
pw2= get_tty_password(pw2msg);
|
||||||
|
|
||||||
|
if (strcmp(pw1, pw2))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Sorry, passwords do not match.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
make_scrambled_password(crypted_pw, pw1);
|
||||||
|
printf("%s:%s\n", user, crypted_pw);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
C_MODE_START
|
C_MODE_START
|
||||||
|
|
||||||
static my_bool
|
static my_bool
|
||||||
@ -161,7 +191,9 @@ get_one_option(int optid,
|
|||||||
case 'V':
|
case 'V':
|
||||||
version();
|
version();
|
||||||
exit(0);
|
exit(0);
|
||||||
case 'I':
|
case 'P':
|
||||||
|
passwd();
|
||||||
|
exit(0);
|
||||||
case '?':
|
case '?':
|
||||||
usage();
|
usage();
|
||||||
exit(0);
|
exit(0);
|
||||||
@ -180,12 +212,28 @@ C_MODE_END
|
|||||||
May not return.
|
May not return.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void Options::load(int argc, char **argv)
|
int Options::load(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (argc >= 2)
|
||||||
|
{
|
||||||
|
if (is_prefix(argv[1],"--defaults-file=") ||
|
||||||
|
is_prefix(argv[1],"--defaults-extra-file="))
|
||||||
|
Options::first_option= argv[1];
|
||||||
|
}
|
||||||
|
|
||||||
/* config-file options are prepended to command-line ones */
|
/* config-file options are prepended to command-line ones */
|
||||||
load_defaults("my", default_groups, &argc, &argv);
|
load_defaults("my", default_groups, &argc, &argv);
|
||||||
|
|
||||||
if (int rc= handle_options(&argc, &argv, my_long_options, get_one_option))
|
if ((rc= handle_options(&argc, &argv, my_long_options, get_one_option)) != 0)
|
||||||
exit(rc);
|
return rc;
|
||||||
|
Options::saved_argv= argv;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Options::cleanup()
|
||||||
|
{
|
||||||
|
/* free_defaults returns nothing */
|
||||||
|
free_defaults(Options::saved_argv);
|
||||||
|
}
|
||||||
|
@ -34,13 +34,17 @@ struct Options
|
|||||||
static const char *socket_file_name;
|
static const char *socket_file_name;
|
||||||
static const char *password_file_name;
|
static const char *password_file_name;
|
||||||
static const char *default_mysqld_path;
|
static const char *default_mysqld_path;
|
||||||
static const char *default_admin_user;
|
static const char *user;
|
||||||
static const char *default_admin_password;
|
/* the option which should be passed to process_default_option_files */
|
||||||
|
static const char *first_option;
|
||||||
static uint monitoring_interval;
|
static uint monitoring_interval;
|
||||||
static uint port_number;
|
static uint port_number;
|
||||||
static const char *bind_address;
|
static const char *bind_address;
|
||||||
|
|
||||||
static void load(int argc, char **argv);
|
static char **saved_argv;
|
||||||
|
|
||||||
|
static int load(int argc, char **argv);
|
||||||
|
void cleanup();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H
|
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H
|
||||||
|
@ -49,29 +49,6 @@ static struct tokens_st tokens[]= {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
tries to find next word in the text
|
|
||||||
if found, returns the beginning and puts word length to word_len argument.
|
|
||||||
if not found returns pointer to first non-space or to '\0', word_len == 0
|
|
||||||
*/
|
|
||||||
|
|
||||||
inline void get_word(const char **text, uint *word_len)
|
|
||||||
{
|
|
||||||
const char *word_end;
|
|
||||||
|
|
||||||
/* skip space */
|
|
||||||
while (my_isspace(default_charset_info, **text))
|
|
||||||
++(*text);
|
|
||||||
|
|
||||||
word_end= *text;
|
|
||||||
|
|
||||||
while (my_isalnum(default_charset_info, *word_end))
|
|
||||||
++word_end;
|
|
||||||
|
|
||||||
*word_len= word_end - *text;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Returns token no if word corresponds to some token, otherwise returns
|
Returns token no if word corresponds to some token, otherwise returns
|
||||||
TOK_NOT_FOUND
|
TOK_NOT_FOUND
|
||||||
|
@ -20,4 +20,34 @@
|
|||||||
|
|
||||||
Command *parse_command(Command_factory *factory, const char *text);
|
Command *parse_command(Command_factory *factory, const char *text);
|
||||||
|
|
||||||
|
/* define kinds of the word seek method */
|
||||||
|
enum { ALPHANUM= 1, NONSPACE };
|
||||||
|
|
||||||
|
/*
|
||||||
|
tries to find next word in the text
|
||||||
|
if found, returns the beginning and puts word length to word_len argument.
|
||||||
|
if not found returns pointer to first non-space or to '\0', word_len == 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline void get_word(const char **text, uint *word_len,
|
||||||
|
int seek_method= ALPHANUM)
|
||||||
|
{
|
||||||
|
const char *word_end;
|
||||||
|
|
||||||
|
/* skip space */
|
||||||
|
while (my_isspace(default_charset_info, **text))
|
||||||
|
++(*text);
|
||||||
|
|
||||||
|
word_end= *text;
|
||||||
|
|
||||||
|
if (seek_method == ALPHANUM)
|
||||||
|
while (my_isalnum(default_charset_info, *word_end))
|
||||||
|
++word_end;
|
||||||
|
else
|
||||||
|
while (!my_isspace(default_charset_info, *word_end))
|
||||||
|
++word_end;
|
||||||
|
|
||||||
|
*word_len= word_end - *text;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H */
|
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H */
|
||||||
|
101
server-tools/instance-manager/parse_output.cc
Normal file
101
server-tools/instance-manager/parse_output.cc
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/* Copyright (C) 2004 MySQL AB
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Parse output of the given command
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
parse_output_and_get_value()
|
||||||
|
|
||||||
|
command the command to execue with popen.
|
||||||
|
word the word to look for (usually an option name)
|
||||||
|
result the buffer to store the next word (option value)
|
||||||
|
result_len self-explanatory
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
|
||||||
|
Parse output of the "command". Find the "word" and return the next one
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
0 - ok
|
||||||
|
1 - error occured
|
||||||
|
*/
|
||||||
|
|
||||||
|
int parse_output_and_get_value(const char *command, const char *word,
|
||||||
|
char *result, size_t result_len)
|
||||||
|
{
|
||||||
|
FILE *output;
|
||||||
|
uint wordlen;
|
||||||
|
/* should be enought to store the string from the output */
|
||||||
|
enum { MAX_LINE_LEN= 512 };
|
||||||
|
char linebuf[MAX_LINE_LEN];
|
||||||
|
|
||||||
|
wordlen= strlen(word);
|
||||||
|
|
||||||
|
if (!(output= popen(command, "r")))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
We want fully buffered stream. We also want system to
|
||||||
|
allocate appropriate buffer.
|
||||||
|
*/
|
||||||
|
setvbuf(output, NULL, _IOFBF, 0);
|
||||||
|
|
||||||
|
while (fgets(linebuf, sizeof(linebuf) - 1, output))
|
||||||
|
{
|
||||||
|
uint lineword_len= 0;
|
||||||
|
char *linep= linebuf;
|
||||||
|
|
||||||
|
linebuf[sizeof(linebuf) - 1]= '\0'; /* safety */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the word, which might contain non-alphanumeric characters. (Usually
|
||||||
|
these are '/', '-' and '.' in the path expressions and filenames)
|
||||||
|
*/
|
||||||
|
get_word((const char **) &linep, &lineword_len, NONSPACE);
|
||||||
|
if (!strncmp(word, linep, wordlen))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
If we have found the word, return the next one. This is usually
|
||||||
|
an option value.
|
||||||
|
*/
|
||||||
|
linep+= lineword_len; /* swallow the previous one */
|
||||||
|
get_word((const char **) &linep, &lineword_len, NONSPACE);
|
||||||
|
if (result_len <= lineword_len)
|
||||||
|
goto err;
|
||||||
|
strncpy(result, linep, lineword_len);
|
||||||
|
result[lineword_len]= '\0';
|
||||||
|
goto pclose;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose:
|
||||||
|
/* we are not interested in the termination status */
|
||||||
|
pclose(output);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
return 1;
|
||||||
|
}
|
19
server-tools/instance-manager/parse_output.h
Normal file
19
server-tools/instance-manager/parse_output.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* Copyright (C) 2004 MySQL AB
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
int parse_output_and_get_value(const char *command, const char *word,
|
||||||
|
char *result, size_t result_len);
|
||||||
|
|
@ -16,6 +16,15 @@
|
|||||||
|
|
||||||
#include "priv.h"
|
#include "priv.h"
|
||||||
|
|
||||||
|
/* the pid of the manager process (of the signal thread on the LinuxThreads) */
|
||||||
|
pid_t manager_pid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This flag is set if mysqlmanager has detected that it is running on the
|
||||||
|
system using LinuxThreads
|
||||||
|
*/
|
||||||
|
bool linuxthreads;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The following string must be less then 80 characters, as
|
The following string must be less then 80 characters, as
|
||||||
mysql_connection.cc relies on it
|
mysql_connection.cc relies on it
|
||||||
|
@ -16,6 +16,19 @@
|
|||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* the pid of the manager process (of the signal thread on the LinuxThreads) */
|
||||||
|
extern pid_t manager_pid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This flag is set if mysqlmanager has detected that it is running on the
|
||||||
|
system using LinuxThreads
|
||||||
|
*/
|
||||||
|
extern bool linuxthreads;
|
||||||
|
|
||||||
extern const char mysqlmanager_version[];
|
extern const char mysqlmanager_version[];
|
||||||
extern const int mysqlmanager_version_length;
|
extern const int mysqlmanager_version_length;
|
||||||
|
|
||||||
|
@ -154,7 +154,8 @@ int send_fields(struct st_net *net, LIST *fields)
|
|||||||
store_to_string(&send_buff, (char *) "", &position); /* table name alias */
|
store_to_string(&send_buff, (char *) "", &position); /* table name alias */
|
||||||
store_to_string(&send_buff, field->name, &position); /* column name */
|
store_to_string(&send_buff, field->name, &position); /* column name */
|
||||||
store_to_string(&send_buff, field->name, &position); /* column name alias */
|
store_to_string(&send_buff, field->name, &position); /* column name alias */
|
||||||
if (send_buff.reserve(position, 12))
|
send_buff.reserve(position, 12);
|
||||||
|
if (send_buff.is_error())
|
||||||
goto err;
|
goto err;
|
||||||
send_buff.buffer[position++]= 12;
|
send_buff.buffer[position++]= 12;
|
||||||
int2store(send_buff.buffer + position, 1); /* charsetnr */
|
int2store(send_buff.buffer + position, 1); /* charsetnr */
|
||||||
|
@ -197,6 +197,7 @@ void Thread_registry::deliver_shutdown()
|
|||||||
if (info->current_cond)
|
if (info->current_cond)
|
||||||
pthread_cond_signal(info->current_cond);
|
pthread_cond_signal(info->current_cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_registry);
|
pthread_mutex_unlock(&LOCK_thread_registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,185 +0,0 @@
|
|||||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#pragma implementation
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "thread_repository.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* Kick-off signal handler */
|
|
||||||
|
|
||||||
enum { THREAD_KICK_OFF_SIGNAL= SIGUSR2 };
|
|
||||||
|
|
||||||
static void handle_signal(int __attribute__((unused)) sig_no)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: think about moving signal information (now it's shutdown_in_progress)
|
|
||||||
to Thread_info. It will reduce contention and allow signal deliverence to
|
|
||||||
a particular thread, not to the whole worker crew
|
|
||||||
*/
|
|
||||||
|
|
||||||
Thread_repository::Thread_repository() :
|
|
||||||
shutdown_in_progress(false)
|
|
||||||
{
|
|
||||||
pthread_mutex_init(&LOCK_thread_repository, 0);
|
|
||||||
pthread_cond_init(&COND_thread_repository_is_empty, 0);
|
|
||||||
|
|
||||||
/* head is used by-value to simplify nodes inserting */
|
|
||||||
head.next= head.prev= &head;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Thread_repository::~Thread_repository()
|
|
||||||
{
|
|
||||||
/* Check that no one uses the repository. */
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
|
|
||||||
/* All threads must unregister */
|
|
||||||
DBUG_ASSERT(head.next == &head);
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
pthread_cond_destroy(&COND_thread_repository_is_empty);
|
|
||||||
pthread_mutex_destroy(&LOCK_thread_repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Set signal handler for kick-off thread, and insert a thread info to the
|
|
||||||
repository. New node is appended to the end of the list; head.prev always
|
|
||||||
points to the last node.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void Thread_repository::register_thread(Thread_info *info)
|
|
||||||
{
|
|
||||||
struct sigaction sa;
|
|
||||||
sa.sa_handler= handle_signal;
|
|
||||||
sa.sa_flags= 0;
|
|
||||||
sigemptyset(&sa.sa_mask);
|
|
||||||
sigaction(THREAD_KICK_OFF_SIGNAL, &sa, 0);
|
|
||||||
|
|
||||||
info->current_cond= 0;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
info->next= &head;
|
|
||||||
info->prev= head.prev;
|
|
||||||
head.prev->next= info;
|
|
||||||
head.prev= info;
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Unregister a thread from the repository and free Thread_info structure.
|
|
||||||
Every registered thread must unregister. Unregistering should be the last
|
|
||||||
thing a thread is doing, otherwise it could have no time to finalize.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void Thread_repository::unregister_thread(Thread_info *info)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
info->prev->next= info->next;
|
|
||||||
info->next->prev= info->prev;
|
|
||||||
if (head.next == &head)
|
|
||||||
pthread_cond_signal(&COND_thread_repository_is_empty);
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Check whether shutdown is in progress, and if yes, return immidiately.
|
|
||||||
Else set info->current_cond and call pthread_cond_wait. When
|
|
||||||
pthread_cond_wait returns, unregister current cond and check the shutdown
|
|
||||||
status again.
|
|
||||||
RETURN VALUE
|
|
||||||
return value from pthread_cond_wait
|
|
||||||
*/
|
|
||||||
|
|
||||||
int Thread_repository::cond_wait(Thread_info *info, pthread_cond_t *cond,
|
|
||||||
pthread_mutex_t *mutex, bool *is_shutdown)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
*is_shutdown= shutdown_in_progress;
|
|
||||||
if (*is_shutdown)
|
|
||||||
{
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
info->current_cond= cond;
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
/* sic: race condition here, cond can be signaled in deliver_shutdown */
|
|
||||||
int rc= pthread_cond_wait(cond, mutex);
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
info->current_cond= 0;
|
|
||||||
*is_shutdown= shutdown_in_progress;
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Deliver shutdown message to the workers crew.
|
|
||||||
As it's impossible to avoid all race conditions, we signal latecomers
|
|
||||||
again.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void Thread_repository::deliver_shutdown()
|
|
||||||
{
|
|
||||||
struct timespec shutdown_time;
|
|
||||||
set_timespec(shutdown_time, 1);
|
|
||||||
Thread_info *info;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
shutdown_in_progress= true;
|
|
||||||
for (info= head.next; info != &head; info= info->next)
|
|
||||||
{
|
|
||||||
pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL);
|
|
||||||
/*
|
|
||||||
sic: race condition here, the thread may not yet fall into
|
|
||||||
pthread_cond_wait.
|
|
||||||
*/
|
|
||||||
if (info->current_cond)
|
|
||||||
pthread_cond_signal(info->current_cond);
|
|
||||||
}
|
|
||||||
while (pthread_cond_timedwait(&COND_thread_repository_is_empty,
|
|
||||||
&LOCK_thread_repository,
|
|
||||||
&shutdown_time) != ETIMEDOUT &&
|
|
||||||
head.next != &head)
|
|
||||||
;
|
|
||||||
/*
|
|
||||||
If previous signals did not reach some threads, they must be sleeping
|
|
||||||
in pthread_cond_wait or a blocking syscall. Wake them up:
|
|
||||||
every thread shall check signal variables after each syscall/cond_wait,
|
|
||||||
so this time everybody should be informed (presumably each worker can
|
|
||||||
get CPU during shutdown_time.)
|
|
||||||
*/
|
|
||||||
for (info= head.next; info != &head; info= info->next)
|
|
||||||
{
|
|
||||||
pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL);
|
|
||||||
if (info->current_cond)
|
|
||||||
pthread_cond_signal(info->current_cond);
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
}
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
|||||||
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH
|
|
||||||
#define INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH
|
|
||||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
||||||
|
|
||||||
/*
|
|
||||||
A multi-threaded application shall nicely work with signals.
|
|
||||||
|
|
||||||
This means it shall, first of all, shut down nicely on ``quit'' signals:
|
|
||||||
stop all running threads, cleanup and exit.
|
|
||||||
|
|
||||||
Note, that a thread can't be shut down nicely if it doesn't want to be.
|
|
||||||
That's why to perform clean shutdown, all threads consituting a process
|
|
||||||
must observe certain rules. Here we use the rules, described in Butenhof
|
|
||||||
book 'Programming with POSIX threads', namely:
|
|
||||||
- all user signals are handled in 'signal thread' in synchronous manner
|
|
||||||
(by means of sigwait). To guarantee that the signal thread is the only who
|
|
||||||
can receive user signals, all threads block them, and signal thread is
|
|
||||||
the only who calls sigwait() with an apporpriate sigmask.
|
|
||||||
To propogate a signal to the workers the signal thread sets
|
|
||||||
a variable, corresponding to the signal. Additionally the signal thread
|
|
||||||
sends each worker an internal signal (by means of pthread_kill) to kick it
|
|
||||||
out from possible blocking syscall, and possibly pthread_cond_signal if
|
|
||||||
some thread is blocked in pthread_cond_[timed]wait.
|
|
||||||
- a worker handles only internal 'kick' signal (the handler does nothing).
|
|
||||||
In case when a syscall returns 'EINTR' the worker checks all
|
|
||||||
signal-related variables and behaves accordingly.
|
|
||||||
Also these variables shall be checked from time to time in long
|
|
||||||
CPU-bounded operations, and before/after pthread_cond_wait. (It's supposed
|
|
||||||
that a worker thread either waits in a syscall/conditional variable, or
|
|
||||||
computes something.)
|
|
||||||
- to guarantee signal deliverence, there should be some kind of feedback,
|
|
||||||
e. g. all workers shall account in the signal thread Thread Repository and
|
|
||||||
unregister from it on exit.
|
|
||||||
|
|
||||||
Configuration reload (on SIGHUP) and thread timeouts/alarms can be handled
|
|
||||||
in manner, similar to ``quit'' signals.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#pragma interface
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <my_global.h>
|
|
||||||
#include <my_pthread.h>
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Thread_info - repository entry for each worker thread
|
|
||||||
All entries comprise double-linked list like:
|
|
||||||
0 -- entry -- entry -- entry - 0
|
|
||||||
Double-linked list is used to unregister threads easy.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Thread_info
|
|
||||||
{
|
|
||||||
pthread_cond_t *current_cond;
|
|
||||||
Thread_info *prev, *next;
|
|
||||||
pthread_t thread_id;
|
|
||||||
Thread_info() {}
|
|
||||||
friend class Thread_repository;
|
|
||||||
public:
|
|
||||||
Thread_info(pthread_t thread_id_arg) : thread_id(thread_id_arg) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Thread_repository - contains handles for each worker thread to deliver
|
|
||||||
signal information to workers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Thread_repository
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Thread_repository();
|
|
||||||
~Thread_repository();
|
|
||||||
|
|
||||||
void register_thread(Thread_info *info);
|
|
||||||
void unregister_thread(Thread_info *info);
|
|
||||||
void deliver_shutdown();
|
|
||||||
inline bool is_shutdown();
|
|
||||||
int cond_wait(Thread_info *info, pthread_cond_t *cond,
|
|
||||||
pthread_mutex_t *mutex, bool *is_shutdown);
|
|
||||||
private:
|
|
||||||
Thread_info head;
|
|
||||||
bool shutdown_in_progress;
|
|
||||||
pthread_mutex_t LOCK_thread_repository;
|
|
||||||
pthread_cond_t COND_thread_repository_is_empty;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
inline bool Thread_repository::is_shutdown()
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
bool res= shutdown_in_progress;
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH
|
|
@ -69,7 +69,7 @@ int User::init(const char *line)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err:
|
||||||
log_error("error parsing user and password at line %d", line);
|
log_error("error parsing user and password at line %s", line);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,9 +128,10 @@ int User_map::load(const char *password_file_name)
|
|||||||
|
|
||||||
if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0)
|
if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0)
|
||||||
{
|
{
|
||||||
log_error("can't open password file %s: errno=%d, %s", password_file_name,
|
/* Probably the password file wasn't specified. Try to leave without it */
|
||||||
|
log_info("[WARNING] can't open password file %s: errno=%d, %s", password_file_name,
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), file))
|
while (fgets(line, sizeof(line), file))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user