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
@ -776,9 +776,9 @@ extern void get_defaults_files(int argc, char **argv,
|
||||
char **defaults, char **extra_defaults);
|
||||
extern int load_defaults(const char *conf_file, const char **groups,
|
||||
int *argc, char ***argv);
|
||||
extern int process_default_option_files(const char *conf_file,
|
||||
Process_option_func func,
|
||||
void *func_ctx);
|
||||
extern int my_search_option_files(const char *conf_file, int *argc,
|
||||
char ***argv, uint *args_used,
|
||||
Process_option_func func, void *func_ctx);
|
||||
extern void free_defaults(char **argv);
|
||||
extern void print_defaults(const char *conf_file, const char **groups);
|
||||
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.
|
||||
|
||||
SYNOPSIS
|
||||
search_files()
|
||||
my_search_option_files()
|
||||
conf_file Basename for configuration file to search for.
|
||||
If this is a path, then only this file is read.
|
||||
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
|
||||
*/
|
||||
|
||||
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,
|
||||
void *func_ctx)
|
||||
{
|
||||
const char **dirs, *forced_default_file;
|
||||
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 */
|
||||
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.
|
||||
|
||||
@ -363,7 +329,7 @@ int load_defaults(const char *conf_file, const char **groups,
|
||||
ctx.args= &args;
|
||||
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);
|
||||
/*
|
||||
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_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" \
|
||||
-DDEFAULT_PASSWORD_FILE_NAME="$(sysconfdir)/mysqlmanager.passwd" \
|
||||
-DDEFAULT_MYSQLD_PATH="$(bindir)/mysqld$(EXEEXT)" \
|
||||
-DDEFAULT_USER="root" \
|
||||
-DDEFAULT_PASSWORD="" \
|
||||
-DDEFAULT_MONITORING_INTERVAL="5" \
|
||||
-DDEFAULT_MYSQLD_PATH="$(libexecdir)/mysqld$(EXEEXT)" \
|
||||
-DDEFAULT_MONITORING_INTERVAL="20" \
|
||||
-DDEFAULT_PORT="2273" \
|
||||
-DPROTOCOL_VERSION=@PROTOCOL_VERSION@
|
||||
|
||||
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
|
||||
# All symlinked files are grouped in libnet.a
|
||||
@ -59,7 +58,7 @@ client_settings.h: Makefile
|
||||
rm -f $(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 \
|
||||
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 \
|
||||
buffer.h buffer.cc parse.cc parse.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 \
|
||||
libnet.a \
|
||||
|
@ -40,7 +40,7 @@
|
||||
|
||||
RETURN
|
||||
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)
|
||||
@ -71,7 +71,7 @@ int Buffer::append(uint position, const char *string, uint len_arg)
|
||||
|
||||
RETURN
|
||||
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)
|
||||
@ -81,17 +81,29 @@ int Buffer::reserve(uint position, uint len_arg)
|
||||
|
||||
if (position + len_arg >= buffer_size)
|
||||
{
|
||||
buffer= (char *) realloc(buffer,
|
||||
buffer= (char *) my_realloc(buffer,
|
||||
min(MAX_BUFFER_SIZE,
|
||||
max((uint) (buffer_size*1.5),
|
||||
position + len_arg)));
|
||||
if (buffer == NULL)
|
||||
position + len_arg)), MYF(0));
|
||||
if (!(buffer))
|
||||
goto err;
|
||||
buffer_size= (uint) (buffer_size*1.5);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
error= 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 */
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma interface
|
||||
@ -36,11 +37,17 @@ private:
|
||||
/* maximum buffer size is 16Mb */
|
||||
enum { MAX_BUFFER_SIZE= 16777216 };
|
||||
size_t buffer_size;
|
||||
/* Error flag. Triggered if we get an error of some kind */
|
||||
int error;
|
||||
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()
|
||||
@ -50,6 +57,8 @@ public:
|
||||
|
||||
public:
|
||||
char *buffer;
|
||||
int get_size();
|
||||
int is_error();
|
||||
int append(uint position, const char *string, 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;
|
||||
|
||||
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;
|
||||
if (instance->is_running())
|
||||
{
|
||||
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
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -200,7 +201,7 @@ err:
|
||||
|
||||
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))
|
||||
return ER_OUT_OF_RESOURCES;
|
||||
@ -256,52 +257,31 @@ int Show_instance_options::do_command(struct st_net *net,
|
||||
{
|
||||
Instance *instance;
|
||||
|
||||
if ((instance= instance_map->
|
||||
find(instance_name, strlen(instance_name))) == NULL)
|
||||
if (!(instance= instance_map->find(instance_name, strlen(instance_name))))
|
||||
goto err;
|
||||
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))
|
||||
goto err;
|
||||
if (instance->options.mysqld_path != NULL)
|
||||
if ((instance->options.mysqld_path))
|
||||
{
|
||||
position= 0;
|
||||
store_to_string(&send_buff, (char *) "mysqld-path", &position);
|
||||
store_to_string(&send_buff,
|
||||
(char *) instance->options.mysqld_path,
|
||||
&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;
|
||||
}
|
||||
|
||||
if (instance->options.is_guarded != NULL)
|
||||
if ((instance->options.nonguarded))
|
||||
{
|
||||
position= 0;
|
||||
store_to_string(&send_buff, (char *) "guarded", &position);
|
||||
store_to_string(&send_buff, (char *) "nonguarded", &position);
|
||||
store_to_string(&send_buff, "", &position);
|
||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
||||
goto err;
|
||||
}
|
||||
|
||||
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))
|
||||
if (send_buff.is_error() ||
|
||||
my_net_write(net, send_buff.buffer, (uint) position))
|
||||
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);
|
||||
/* join name and the value into the same option again */
|
||||
*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;
|
||||
}
|
||||
}
|
||||
@ -335,7 +316,7 @@ err:
|
||||
|
||||
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))
|
||||
return ER_OUT_OF_RESOURCES;
|
||||
@ -369,10 +350,10 @@ int Start_instance::execute(struct st_net *net, ulong connection_id)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (err_code= instance->start())
|
||||
if ((err_code= instance->start()))
|
||||
return err_code;
|
||||
|
||||
if (instance->options.is_guarded != NULL)
|
||||
if (!(instance->options.nonguarded))
|
||||
instance_map->guardian->guard(instance);
|
||||
|
||||
net_send_ok(net, connection_id);
|
||||
@ -403,7 +384,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (instance->options.is_guarded != NULL)
|
||||
if (!(instance->options.nonguarded))
|
||||
instance_map->guardian->
|
||||
stop_guard(instance);
|
||||
if ((err_code= instance->stop()))
|
||||
|
@ -21,9 +21,15 @@
|
||||
|
||||
#include "guardian.h"
|
||||
#include "instance_map.h"
|
||||
#include "instance.h"
|
||||
#include "mysql_manager_error.h"
|
||||
#include "log.h"
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
|
||||
|
||||
|
||||
|
||||
C_MODE_START
|
||||
|
||||
@ -42,13 +48,13 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg,
|
||||
uint monitoring_interval_arg) :
|
||||
Guardian_thread_args(thread_registry_arg, instance_map_arg,
|
||||
monitoring_interval_arg),
|
||||
thread_info(pthread_self())
|
||||
thread_info(pthread_self()), guarded_instances(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);
|
||||
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 */
|
||||
pthread_mutex_lock(&LOCK_guardian);
|
||||
free_root(&alloc, MYF(0));
|
||||
thread_registry.unregister_thread(&thread_info);
|
||||
pthread_mutex_unlock(&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()
|
||||
{
|
||||
Instance *instance;
|
||||
LIST *loop;
|
||||
LIST *node;
|
||||
struct timespec timeout;
|
||||
|
||||
thread_registry.register_thread(&thread_info);
|
||||
|
||||
my_thread_init();
|
||||
|
||||
while (!thread_registry.is_shutdown())
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_guardian);
|
||||
loop= guarded_instances;
|
||||
while (loop != NULL)
|
||||
|
||||
/* loop, until all instances were shut down at the end */
|
||||
while (!(shutdown_requested && (guarded_instances == NULL)))
|
||||
{
|
||||
instance= (Instance *) loop->data;
|
||||
/* instance-> start already checks whether instance is running */
|
||||
if (instance->start() != ER_INSTANCE_ALREADY_STARTED)
|
||||
log_info("guardian attempted to restart instance %s",
|
||||
instance->options.instance_name);
|
||||
loop= loop->next;
|
||||
node= guarded_instances;
|
||||
|
||||
while (node != NULL)
|
||||
{
|
||||
struct timespec timeout;
|
||||
|
||||
GUARD_NODE *current_node= (GUARD_NODE *) node->data;
|
||||
instance= ((GUARD_NODE *) node->data)->instance;
|
||||
process_instance(instance, current_node, &guarded_instances, node);
|
||||
|
||||
node= node->next;
|
||||
}
|
||||
move_to_list(&starting_instances, &guarded_instances);
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
sleep(monitoring_interval);
|
||||
timeout.tv_sec= time(NULL) + monitoring_interval;
|
||||
timeout.tv_nsec= 0;
|
||||
|
||||
/* 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();
|
||||
}
|
||||
|
||||
|
||||
int Guardian_thread::start()
|
||||
int Guardian_thread::is_stopped()
|
||||
{
|
||||
Instance *instance;
|
||||
Instance_map::Iterator iterator(instance_map);
|
||||
|
||||
instance_map->lock();
|
||||
while ((instance= iterator.next()))
|
||||
{
|
||||
if ((instance->options.is_guarded != NULL) && (instance->is_running()))
|
||||
if (guard(instance))
|
||||
return 1;
|
||||
}
|
||||
instance_map->unlock();
|
||||
|
||||
return 0;
|
||||
int var;
|
||||
pthread_mutex_lock(&LOCK_guardian);
|
||||
var= stopped;
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
return var;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
guard()
|
||||
instance the instance to be guarded
|
||||
Guardian_thread::init()
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
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.
|
||||
NOTE: One should always lock guardian before calling this routine.
|
||||
|
||||
RETURN
|
||||
0 - ok
|
||||
1 - error occured
|
||||
*/
|
||||
|
||||
|
||||
int Guardian_thread::guard(Instance *instance)
|
||||
int Guardian_thread::init()
|
||||
{
|
||||
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)
|
||||
if (!(instance->options.nonguarded))
|
||||
if (guard(instance, TRUE)) /* do not lock guardian */
|
||||
{
|
||||
tmp= rest(*from);
|
||||
*to= list_add(*to, *from);
|
||||
*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;
|
||||
GUARD_NODE *content;
|
||||
|
||||
node= (LIST *) alloc_root(&alloc, sizeof(LIST));
|
||||
if (node == NULL)
|
||||
content= (GUARD_NODE *) alloc_root(&alloc, sizeof(GUARD_NODE));
|
||||
|
||||
if ((!(node)) || (!(content)))
|
||||
return 1;
|
||||
/* 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;
|
||||
|
||||
if (nolock)
|
||||
guarded_instances= list_add(guarded_instances, node);
|
||||
else
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_guardian);
|
||||
*list= list_add(*list, node);
|
||||
guarded_instances= list_add(guarded_instances, node);
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
TODO: perhaps it would make sense to create a pool of the LIST elements
|
||||
elements and give them upon request. Now we are loosing a bit of memory when
|
||||
TODO: perhaps it would make sense to create a pool of the LIST nodeents
|
||||
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
|
||||
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
|
||||
instance_map's MEM_ROOT.
|
||||
*/
|
||||
if ((Instance *) node->data == instance)
|
||||
if (((GUARD_NODE *) node->data)->instance == instance)
|
||||
{
|
||||
guarded_instances= list_delete(guarded_instances, node);
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
@ -212,3 +362,73 @@ int Guardian_thread::stop_guard(Instance *instance)
|
||||
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_sys.h>
|
||||
#include <my_list.h>
|
||||
#include "thread_registry.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma interface
|
||||
#endif
|
||||
|
||||
class Instance;
|
||||
class Instance_map;
|
||||
|
||||
#include "thread_registry.h"
|
||||
#include "instance.h"
|
||||
class Thread_registry;
|
||||
struct GUARD_NODE;
|
||||
|
||||
C_MODE_START
|
||||
|
||||
@ -35,12 +36,11 @@ pthread_handler_decl(guardian, arg);
|
||||
|
||||
C_MODE_END
|
||||
|
||||
|
||||
struct Guardian_thread_args
|
||||
{
|
||||
Thread_registry &thread_registry;
|
||||
Instance_map *instance_map;
|
||||
uint monitoring_interval;
|
||||
int monitoring_interval;
|
||||
|
||||
Guardian_thread_args(Thread_registry &thread_registry_arg,
|
||||
Instance_map *instance_map_arg,
|
||||
@ -60,27 +60,67 @@ struct Guardian_thread_args
|
||||
class Guardian_thread: public Guardian_thread_args
|
||||
{
|
||||
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,
|
||||
Instance_map *instance_map_arg,
|
||||
uint monitoring_interval_arg);
|
||||
~Guardian_thread();
|
||||
/* Main funtion of the thread */
|
||||
void run();
|
||||
/* Initialize or refresh the list of guarded instances */
|
||||
int init();
|
||||
int start();
|
||||
int guard(Instance *instance);
|
||||
/* Request guardian shutdown. Stop instances if needed */
|
||||
void request_shutdown(bool stop_instances);
|
||||
/* Start instance protection */
|
||||
int guard(Instance *instance, bool nolock= FALSE);
|
||||
/* Stop instance protection */
|
||||
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:
|
||||
int add_instance_to_list(Instance *instance, LIST **list);
|
||||
void move_to_list(LIST **from, LIST **to);
|
||||
/* Prepares Guardian shutdown. Stops instances is needed */
|
||||
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:
|
||||
pthread_mutex_t LOCK_guardian;
|
||||
Thread_info thread_info;
|
||||
LIST *guarded_instances;
|
||||
LIST *starting_instances;
|
||||
MEM_ROOT alloc;
|
||||
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 */
|
||||
|
@ -21,11 +21,34 @@
|
||||
#include "instance.h"
|
||||
#include "mysql_manager_error.h"
|
||||
#include "log.h"
|
||||
#include "instance_map.h"
|
||||
#include "priv.h"
|
||||
|
||||
#include <my_sys.h>
|
||||
#include <signal.h>
|
||||
#include <m_string.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.
|
||||
|
||||
@ -43,85 +66,175 @@ int Instance::start()
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
/* clear crash flag */
|
||||
pthread_mutex_lock(&LOCK_instance);
|
||||
crashed= 0;
|
||||
pthread_mutex_unlock(&LOCK_instance);
|
||||
|
||||
|
||||
if (!is_running())
|
||||
{
|
||||
log_info("trying to start instance %s", options.instance_name);
|
||||
switch (pid= fork()) {
|
||||
case 0:
|
||||
if (fork()) /* zombie protection */
|
||||
exit(0); /* parent goes bye-bye */
|
||||
else
|
||||
if ((pid= options.get_pid()) != 0) /* check the pidfile */
|
||||
if (options.unlink_pidfile()) /* remove stalled pidfile */
|
||||
log_error("cannot remove pidfile for instance %i, this might be \
|
||||
since IM lacks permmissions or hasn't found the pidifle",
|
||||
options.instance_name);
|
||||
|
||||
/*
|
||||
No need to monitor this thread in the Thread_registry, as all
|
||||
instances are to be stopped during shutdown.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
execv(options.mysqld_path, options.argv);
|
||||
exit(1);
|
||||
}
|
||||
case -1:
|
||||
log_error("Instance::start(): pthread_create(proxy) failed");
|
||||
return ER_CANNOT_START_INSTANCE;
|
||||
default:
|
||||
waitpid(pid, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the instance is started already */
|
||||
return ER_INSTANCE_ALREADY_STARTED;
|
||||
}
|
||||
|
||||
int Instance::cleanup()
|
||||
|
||||
void Instance::fork_and_monitor()
|
||||
{
|
||||
pid_t pid;
|
||||
log_info("starting instance %s", options.instance_name);
|
||||
switch (pid= fork()) {
|
||||
case 0:
|
||||
execv(options.mysqld_path, options.argv);
|
||||
/* exec never returns */
|
||||
exit(1);
|
||||
case -1:
|
||||
log_info("cannot fork() to start instance %s", options.instance_name);
|
||||
return;
|
||||
default:
|
||||
/*
|
||||
We cannot close connection in destructor, as mysql_close needs alarm
|
||||
services which are definitely unavailaible at the time of destructor
|
||||
call.
|
||||
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 (is_connected)
|
||||
mysql_close(&mysql);
|
||||
return 0;
|
||||
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()
|
||||
{
|
||||
pthread_cond_destroy(&COND_instance_stopped);
|
||||
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()
|
||||
{
|
||||
MYSQL mysql;
|
||||
uint port= 0;
|
||||
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)
|
||||
port= atoi(strchr(options.mysqld_port, '=') + 1);
|
||||
port= options.mysqld_port_val;
|
||||
|
||||
if (options.mysqld_socket)
|
||||
socket= strchr(options.mysqld_socket, '=') + 1;
|
||||
|
||||
pthread_mutex_lock(&LOCK_instance);
|
||||
if (!is_connected)
|
||||
{
|
||||
|
||||
mysql_init(&mysql);
|
||||
if (mysql_real_connect(&mysql, LOCAL_HOST, options.mysqld_user,
|
||||
options.mysqld_password,
|
||||
/* 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.reconnect= 1;
|
||||
is_connected= TRUE;
|
||||
/*
|
||||
We have successfully connected to the server using fake
|
||||
username/password. Write a warning to the logfile.
|
||||
*/
|
||||
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 TRUE;
|
||||
return_val= TRUE; /* server is alive */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!strncmp(access_denied_message, mysql_error(&mysql),
|
||||
sizeof(access_denied_message)-1))
|
||||
{
|
||||
return_val= TRUE;
|
||||
}
|
||||
else
|
||||
return_val= FALSE;
|
||||
}
|
||||
|
||||
mysql_close(&mysql);
|
||||
pthread_mutex_unlock(&LOCK_instance);
|
||||
return FALSE;
|
||||
}
|
||||
else if (!mysql_ping(&mysql))
|
||||
{
|
||||
pthread_mutex_unlock(&LOCK_instance);
|
||||
return TRUE;
|
||||
}
|
||||
pthread_mutex_unlock(&LOCK_instance);
|
||||
return FALSE;
|
||||
|
||||
return return_val;
|
||||
}
|
||||
|
||||
|
||||
@ -139,22 +252,67 @@ bool Instance::is_running()
|
||||
|
||||
int Instance::stop()
|
||||
{
|
||||
if (is_running())
|
||||
{
|
||||
if (mysql_shutdown(&mysql, SHUTDOWN_DEFAULT))
|
||||
pid_t pid;
|
||||
struct timespec timeout;
|
||||
uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY;
|
||||
|
||||
if (options.shutdown_delay_val)
|
||||
waitchild= options.shutdown_delay_val;
|
||||
|
||||
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;
|
||||
|
||||
mysql_close(&mysql);
|
||||
is_connected= FALSE;
|
||||
return 0;
|
||||
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;
|
||||
err:
|
||||
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.
|
||||
Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY.
|
||||
@ -162,7 +320,14 @@ err:
|
||||
|
||||
int Instance::init(const char *name_arg)
|
||||
{
|
||||
pthread_mutex_init(&LOCK_instance, 0);
|
||||
|
||||
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
|
||||
#endif
|
||||
|
||||
class Instance_map;
|
||||
|
||||
class Instance
|
||||
{
|
||||
public:
|
||||
Instance(): is_connected(FALSE)
|
||||
{}
|
||||
Instance();
|
||||
|
||||
~Instance();
|
||||
|
||||
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();
|
||||
int start();
|
||||
int stop();
|
||||
int cleanup();
|
||||
/* send a signal to the instance */
|
||||
void kill_instance(int signo);
|
||||
int is_crashed();
|
||||
void fork_and_monitor();
|
||||
|
||||
public:
|
||||
enum { DEFAULT_SHUTDOWN_DELAY= 35 };
|
||||
Instance_options options;
|
||||
|
||||
/* connection to the instance */
|
||||
MYSQL mysql;
|
||||
|
||||
private:
|
||||
/*
|
||||
Mutex protecting the instance. Currently we use it to avoid the
|
||||
double start of the instance. This happens when the instance is starting
|
||||
and we issue the start command once more.
|
||||
*/
|
||||
int crashed;
|
||||
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 */
|
||||
|
@ -82,7 +82,8 @@ static int process_option(void * ctx, const char *group, const char *option)
|
||||
|
||||
map = (Instance_map*) ctx;
|
||||
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)
|
||||
{
|
||||
@ -110,13 +111,9 @@ C_MODE_END
|
||||
|
||||
|
||||
Instance_map::Instance_map(const char *default_mysqld_path_arg,
|
||||
const char *default_admin_user_arg,
|
||||
const char *default_admin_password_arg)
|
||||
const char *first_option_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);
|
||||
}
|
||||
|
||||
@ -154,12 +151,15 @@ int Instance_map::flush_instances()
|
||||
{
|
||||
int rc;
|
||||
|
||||
guardian->lock();
|
||||
pthread_mutex_lock(&LOCK_instance_map);
|
||||
hash_free(&hash);
|
||||
hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
|
||||
get_instance_key, delete_instance, 0);
|
||||
pthread_mutex_unlock(&LOCK_instance_map);
|
||||
rc= load();
|
||||
guardian->init();
|
||||
guardian->unlock();
|
||||
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;
|
||||
uint i= 0;
|
||||
|
||||
|
||||
if (hash.records == 0) /* no instances found */
|
||||
{
|
||||
if ((instance= new Instance) == 0)
|
||||
goto err;
|
||||
|
||||
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);
|
||||
instance->options.complete_initialization(mysqld_path, user, password);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Instance_map::cleanup()
|
||||
{
|
||||
Instance *instance;
|
||||
uint i= 0;
|
||||
|
||||
while (i < hash.records)
|
||||
{
|
||||
instance= (Instance *) hash_element(&hash, i);
|
||||
if (instance->cleanup())
|
||||
return 1;
|
||||
if (instance->complete_initialization(this, mysqld_path))
|
||||
goto err;
|
||||
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;
|
||||
err:
|
||||
return 1;
|
||||
err_instance:
|
||||
delete instance;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -230,13 +226,30 @@ Instance_map::find(uint instance_number)
|
||||
|
||||
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:
|
||||
/* returns a pointer to the instance or NULL, if there is no such instance */
|
||||
Instance *find(const char *name, uint name_len);
|
||||
Instance *find(uint instance_number);
|
||||
|
||||
int flush_instances();
|
||||
int cleanup();
|
||||
int lock();
|
||||
int unlock();
|
||||
int init();
|
||||
|
||||
Instance_map(const char *default_mysqld_path_arg,
|
||||
const char *default_admin_user_arg,
|
||||
const char *default_admin_password_arg);
|
||||
const char *first_option_arg);
|
||||
~Instance_map();
|
||||
|
||||
/* loads options from config files */
|
||||
@ -75,16 +72,14 @@ public:
|
||||
/* adds instance to internal hash */
|
||||
int add_instance(Instance *instance);
|
||||
/* inits instances argv's after all options have been loaded */
|
||||
void complete_initialization();
|
||||
int complete_initialization();
|
||||
|
||||
public:
|
||||
const char *mysqld_path;
|
||||
/* user an password to shutdown MySQL */
|
||||
const char *user;
|
||||
const char *password;
|
||||
Guardian_thread *guardian;
|
||||
|
||||
private:
|
||||
const char *first_option;
|
||||
enum { START_HASH_SIZE = 16 };
|
||||
pthread_mutex_t LOCK_instance_map;
|
||||
HASH hash;
|
||||
|
@ -19,43 +19,170 @@
|
||||
#endif
|
||||
|
||||
#include "instance_options.h"
|
||||
#include "parse_output.h"
|
||||
#include "buffer.h"
|
||||
#include <my_sys.h>
|
||||
#include <mysql.h>
|
||||
#include <signal.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 */
|
||||
if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1
|
||||
+ MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*))))
|
||||
int position= 0;
|
||||
int rc= 1;
|
||||
char verbose_option[]= " --no-defaults --verbose --help";
|
||||
|
||||
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)))
|
||||
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))
|
||||
goto err;
|
||||
|
||||
/* the following options are not for argv */
|
||||
if (mysqld_user == NULL)
|
||||
{
|
||||
if (!(mysqld_user= strdup_root(&alloc, default_user)))
|
||||
if (add_to_argv(tmp))
|
||||
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,
|
||||
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},
|
||||
{"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD},
|
||||
{"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE},
|
||||
{"--admin-user=", 13, &mysqld_user, SAVE_VALUE},
|
||||
{"--admin-password=", 17, &mysqld_password, SAVE_VALUE},
|
||||
{"--guarded", 9, &is_guarded, SAVE_WHOLE},
|
||||
{"--nonguarded", 9, &nonguarded, SAVE_WHOLE},
|
||||
{"--shutdown_delay", 9, &shutdown_delay, SAVE_VALUE},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
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;
|
||||
|
||||
err:
|
||||
@ -142,12 +271,24 @@ int Instance_options::add_to_argv(const char* option)
|
||||
{
|
||||
DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS);
|
||||
|
||||
if (option != NULL)
|
||||
if ((option))
|
||||
argv[filled_default_options++]= (char *) option;
|
||||
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.
|
||||
Return value: 0 - ok. 1 - unable to allocate memory.
|
||||
|
@ -37,22 +37,31 @@ class Instance_options
|
||||
{
|
||||
public:
|
||||
Instance_options() :
|
||||
mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0),
|
||||
mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), mysqld_user(0),
|
||||
mysqld_password(0), is_guarded(0), filled_default_options(0)
|
||||
mysqld_socket(0), mysqld_datadir(0),
|
||||
mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0),
|
||||
mysqld_port_val(0), mysqld_path(0), nonguarded(0), shutdown_delay(0),
|
||||
shutdown_delay_val(0), filled_default_options(0)
|
||||
{}
|
||||
~Instance_options();
|
||||
/* fills in argv */
|
||||
int complete_initialization(const char *default_path,
|
||||
const char *default_user,
|
||||
const char *default_password);
|
||||
int complete_initialization(const char *default_path, int only_instance);
|
||||
|
||||
int add_option(const char* option);
|
||||
int init(const char *instance_name_arg);
|
||||
pid_t get_pid();
|
||||
int get_pid_filename(char *result);
|
||||
int unlink_pidfile();
|
||||
void print_argv();
|
||||
|
||||
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 };
|
||||
char pid_file_with_path[MAX_PATH_LEN];
|
||||
char **argv;
|
||||
/* We need the some options, so we store them as a separate pointers */
|
||||
const char *mysqld_socket;
|
||||
@ -60,15 +69,19 @@ public:
|
||||
const char *mysqld_bind_address;
|
||||
const char *mysqld_pid_file;
|
||||
const char *mysqld_port;
|
||||
uint instance_name_len;
|
||||
uint mysqld_port_val;
|
||||
const char *instance_name;
|
||||
uint instance_name_len;
|
||||
const char *mysqld_path;
|
||||
const char *mysqld_user;
|
||||
const char *mysqld_password;
|
||||
const char *is_guarded;
|
||||
const char *nonguarded;
|
||||
const char *shutdown_delay;
|
||||
uint shutdown_delay_val;
|
||||
/* this value is computed and cashed here */
|
||||
DYNAMIC_ARRAY options_array;
|
||||
private:
|
||||
int add_to_argv(const char *option);
|
||||
int get_default_option(char *result, size_t result_len,
|
||||
const char *option_name);
|
||||
private:
|
||||
uint filled_default_options;
|
||||
MEM_ROOT alloc;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "instance_map.h"
|
||||
#include "log.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)
|
||||
,thread_info(pthread_self())
|
||||
{
|
||||
thread_registry.register_thread(&thread_info);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
int flags;
|
||||
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 */
|
||||
|
||||
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",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
|
||||
struct sockaddr_in ip_socket_address;
|
||||
@ -104,7 +115,7 @@ void Listener_thread::run()
|
||||
}
|
||||
else
|
||||
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_addr.s_addr = im_bind_addr;
|
||||
@ -119,16 +130,14 @@ void Listener_thread::run()
|
||||
{
|
||||
log_error("Listener_thread::run(): bind(ip socket) failed, '%s'",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (listen(ip_socket, LISTEN_BACK_LOG_SIZE))
|
||||
{
|
||||
log_error("Listener_thread::run(): listen(ip socket) failed, %s",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
/* set the socket nonblocking */
|
||||
flags= fcntl(ip_socket, F_GETFL, 0);
|
||||
@ -140,13 +149,12 @@ void Listener_thread::run()
|
||||
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)
|
||||
{
|
||||
log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
|
||||
struct sockaddr_un unix_socket_address;
|
||||
@ -169,8 +177,7 @@ void Listener_thread::run()
|
||||
log_error("Listener_thread::run(): bind(unix socket) failed, "
|
||||
"socket file name is '%s', error '%s'",
|
||||
unix_socket_address.sun_path, strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
umask(old_mask);
|
||||
|
||||
@ -178,8 +185,7 @@ void Listener_thread::run()
|
||||
{
|
||||
log_error("Listener_thread::run(): listen(unix socket) failed, %s",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* set the socket nonblocking */
|
||||
@ -205,7 +211,15 @@ void Listener_thread::run()
|
||||
while (thread_registry.is_shutdown() == false)
|
||||
{
|
||||
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);
|
||||
|
||||
|
||||
if (rc == -1 && errno != EINTR)
|
||||
log_error("Listener_thread::run(): select() failed, %s",
|
||||
strerror(errno));
|
||||
@ -256,6 +270,16 @@ void Listener_thread::run()
|
||||
close(unix_socket);
|
||||
close(ip_socket);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,12 +16,7 @@
|
||||
|
||||
#include "manager.h"
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <m_string.h>
|
||||
#include <signal.h>
|
||||
#include <thr_alarm.h>
|
||||
|
||||
#include "priv.h"
|
||||
#include "thread_registry.h"
|
||||
#include "listener.h"
|
||||
#include "instance_map.h"
|
||||
@ -30,6 +25,14 @@
|
||||
#include "log.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)
|
||||
{
|
||||
if (FILE *pid_file= my_fopen(pid_file_name,
|
||||
@ -65,9 +68,7 @@ void manager(const Options &options)
|
||||
*/
|
||||
|
||||
User_map user_map;
|
||||
Instance_map instance_map(options.default_mysqld_path,
|
||||
options.default_admin_user,
|
||||
options.default_admin_password);
|
||||
Instance_map instance_map(options.default_mysqld_path, options.first_option);
|
||||
Guardian_thread guardian_thread(thread_registry,
|
||||
&instance_map,
|
||||
options.monitoring_interval);
|
||||
@ -75,10 +76,22 @@ void manager(const Options &options)
|
||||
Listener_thread_args listener_args(thread_registry, options, user_map,
|
||||
instance_map);
|
||||
|
||||
manager_pid= getpid();
|
||||
instance_map.guardian= &guardian_thread;
|
||||
|
||||
if (instance_map.init() || user_map.init() || instance_map.load() ||
|
||||
user_map.load(options.password_file_name))
|
||||
if (instance_map.init() || user_map.init())
|
||||
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;
|
||||
|
||||
/* write pid file */
|
||||
@ -126,6 +139,12 @@ void manager(const Options &options)
|
||||
pthread_attr_t guardian_thd_attr;
|
||||
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_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED);
|
||||
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.
|
||||
*/
|
||||
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
|
||||
alarm structures initialization as we have to use net_* functions while
|
||||
making the list. And they in their turn need alarms for timeout suppport.
|
||||
After the list of guarded instances have been initialized,
|
||||
Guardian should start them.
|
||||
*/
|
||||
guardian_thread.start();
|
||||
pthread_cond_signal(&guardian_thread.COND_guardian);
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
while (!shutdown_complete)
|
||||
{
|
||||
sigwait(&mask, &signo);
|
||||
int status= 0;
|
||||
|
||||
if ((status= my_sigwait(&mask, &signo)) != 0)
|
||||
{
|
||||
log_error("sigwait() failed");
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (signo)
|
||||
{
|
||||
case THR_SERVER_ALARM:
|
||||
process_alarm(signo);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -181,9 +224,6 @@ err:
|
||||
/* delete the pid file */
|
||||
my_delete(options.pid_file_name, MYF(0));
|
||||
|
||||
/* close permanent connections to the running instances */
|
||||
instance_map.cleanup();
|
||||
|
||||
/* free alarm structures */
|
||||
end_thr_alarm(1);
|
||||
/* 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:
|
||||
return "Bad instance name. Check that the instance with such a name exists";
|
||||
case ER_INSTANCE_IS_NOT_STARTED:
|
||||
return "Cannot stop instance. Perhaps the instance is not started or you"
|
||||
" have specified wrong username/password in the config file";
|
||||
return "Cannot stop instance. Perhaps the instance is not started, or was started"
|
||||
"manually, so IM cannot find the pidfile.";
|
||||
case ER_INSTANCE_ALREADY_STARTED:
|
||||
return "The instance is already started";
|
||||
case ER_CANNOT_START_INSTANCE:
|
||||
|
@ -82,7 +82,6 @@ private:
|
||||
private:
|
||||
/* Names are conventionally the same as in mysqld */
|
||||
int check_connection();
|
||||
int check_user(const char *user, const char *password);
|
||||
int do_command();
|
||||
int dispatch_command(enum enum_server_command command,
|
||||
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()
|
||||
{
|
||||
char *packet;
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include <my_sys.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -54,6 +56,8 @@
|
||||
static void init_environment(char *progname);
|
||||
static void daemonize(const char *log_file_name);
|
||||
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]);
|
||||
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)
|
||||
{
|
||||
/* forks, and returns only in child */
|
||||
@ -77,11 +94,84 @@ int main(int argc, char *argv[])
|
||||
angel(options);
|
||||
}
|
||||
manager(options);
|
||||
options.cleanup();
|
||||
my_end(0);
|
||||
return 0;
|
||||
err:
|
||||
my_end(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************* 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
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <my_getopt.h>
|
||||
#include <m_string.h>
|
||||
#include <mysql_com.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::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
|
||||
const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH);
|
||||
const char *Options::default_admin_user= QUOTE(DEFAULT_USER);
|
||||
const char *Options::default_admin_password= QUOTE(DEFAULT_PASSWORD);
|
||||
const char *Options::first_option= 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::port_number= DEFAULT_PORT;
|
||||
/* just to declare */
|
||||
char **Options::saved_argv;
|
||||
|
||||
/*
|
||||
List of options, accepted by the instance manager.
|
||||
@ -54,9 +58,6 @@ enum options {
|
||||
OPT_MYSQLD_PATH,
|
||||
OPT_RUN_AS_SERVICE,
|
||||
OPT_USER,
|
||||
OPT_PASSWORD,
|
||||
OPT_DEFAULT_ADMIN_USER,
|
||||
OPT_DEFAULT_ADMIN_PASSWORD,
|
||||
OPT_MONITORING_INTERVAL,
|
||||
OPT_PORT,
|
||||
OPT_BIND_ADDRESS
|
||||
@ -79,13 +80,16 @@ static struct my_option my_long_options[] =
|
||||
(gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name,
|
||||
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.",
|
||||
(gptr *) &Options::bind_address, (gptr *) &Options::bind_address,
|
||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||
|
||||
{ "port", OPT_PORT, "Port number to use for connections",
|
||||
(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"
|
||||
" and passwords here.",
|
||||
@ -98,28 +102,22 @@ static struct my_option my_long_options[] =
|
||||
(gptr *) &Options::default_mysqld_path, (gptr *) &Options::default_mysqld_path,
|
||||
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"
|
||||
" in seconds.",
|
||||
(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,
|
||||
"Daemonize and start angel process.", (gptr *) &Options::run_as_service,
|
||||
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,
|
||||
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
||||
|
||||
@ -150,6 +148,38 @@ static void usage()
|
||||
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
|
||||
|
||||
static my_bool
|
||||
@ -161,7 +191,9 @@ get_one_option(int optid,
|
||||
case 'V':
|
||||
version();
|
||||
exit(0);
|
||||
case 'I':
|
||||
case 'P':
|
||||
passwd();
|
||||
exit(0);
|
||||
case '?':
|
||||
usage();
|
||||
exit(0);
|
||||
@ -180,12 +212,28 @@ C_MODE_END
|
||||
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 */
|
||||
load_defaults("my", default_groups, &argc, &argv);
|
||||
|
||||
if (int rc= handle_options(&argc, &argv, my_long_options, get_one_option))
|
||||
exit(rc);
|
||||
if ((rc= handle_options(&argc, &argv, my_long_options, get_one_option)) != 0)
|
||||
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 *password_file_name;
|
||||
static const char *default_mysqld_path;
|
||||
static const char *default_admin_user;
|
||||
static const char *default_admin_password;
|
||||
static const char *user;
|
||||
/* the option which should be passed to process_default_option_files */
|
||||
static const char *first_option;
|
||||
static uint monitoring_interval;
|
||||
static uint port_number;
|
||||
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
|
||||
|
@ -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
|
||||
TOK_NOT_FOUND
|
||||
|
@ -20,4 +20,34 @@
|
||||
|
||||
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 */
|
||||
|
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"
|
||||
|
||||
/* 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
|
||||
mysql_connection.cc relies on it
|
||||
|
@ -16,6 +16,19 @@
|
||||
along with this program; if not, write to the Free Software
|
||||
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 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, field->name, &position); /* column name */
|
||||
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;
|
||||
send_buff.buffer[position++]= 12;
|
||||
int2store(send_buff.buffer + position, 1); /* charsetnr */
|
||||
|
@ -197,6 +197,7 @@ void Thread_registry::deliver_shutdown()
|
||||
if (info->current_cond)
|
||||
pthread_cond_signal(info->current_cond);
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
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));
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), file))
|
||||
|
Loading…
x
Reference in New Issue
Block a user