summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <petr@mysql.com>2005-03-01 01:59:37 +0300
committerunknown <petr@mysql.com>2005-03-01 01:59:37 +0300
commitad1c1e070cb446d26ab579769473184e5a09f9df (patch)
treed0a6b7686af0d978bf1a03413ea6f6e6b843bd04
parente99c035ffaf8c01bf4d848ddf10219103fc6a314 (diff)
parentd4c2c74724b17d5f09f6f6bd110bb65258037722 (diff)
downloadmariadb-git-ad1c1e070cb446d26ab579769473184e5a09f9df.tar.gz
Merge pchardin@bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/home/cps/mysql/devel/im-fix-review mysys/default.c: Auto merged
-rw-r--r--include/my_sys.h8
-rw-r--r--mysys/default.c42
-rw-r--r--server-tools/instance-manager/Makefile.am12
-rw-r--r--server-tools/instance-manager/buffer.cc28
-rw-r--r--server-tools/instance-manager/buffer.h15
-rw-r--r--server-tools/instance-manager/client_func.c32
-rw-r--r--server-tools/instance-manager/commands.cc57
-rw-r--r--server-tools/instance-manager/guardian.cc334
-rw-r--r--server-tools/instance-manager/guardian.h60
-rw-r--r--server-tools/instance-manager/instance.cc265
-rw-r--r--server-tools/instance-manager/instance.h28
-rw-r--r--server-tools/instance-manager/instance_map.cc101
-rw-r--r--server-tools/instance-manager/instance_map.h11
-rw-r--r--server-tools/instance-manager/instance_options.cc187
-rw-r--r--server-tools/instance-manager/instance_options.h35
-rw-r--r--server-tools/instance-manager/listener.cc56
-rw-r--r--server-tools/instance-manager/log.cc4
-rw-r--r--server-tools/instance-manager/log.h4
-rw-r--r--server-tools/instance-manager/manager.cc82
-rw-r--r--server-tools/instance-manager/messages.cc4
-rw-r--r--server-tools/instance-manager/mysql_connection.cc7
-rw-r--r--server-tools/instance-manager/mysqlmanager.cc92
-rw-r--r--server-tools/instance-manager/options.cc96
-rw-r--r--server-tools/instance-manager/options.h10
-rw-r--r--server-tools/instance-manager/parse.cc23
-rw-r--r--server-tools/instance-manager/parse.h30
-rw-r--r--server-tools/instance-manager/parse_output.cc101
-rw-r--r--server-tools/instance-manager/parse_output.h19
-rw-r--r--server-tools/instance-manager/priv.cc9
-rw-r--r--server-tools/instance-manager/priv.h13
-rw-r--r--server-tools/instance-manager/protocol.cc3
-rw-r--r--server-tools/instance-manager/thread_registry.cc1
-rw-r--r--server-tools/instance-manager/thread_repository.cc185
-rw-r--r--server-tools/instance-manager/thread_repository.h113
-rw-r--r--server-tools/instance-manager/user_map.cc7
35 files changed, 1329 insertions, 745 deletions
diff --git a/include/my_sys.h b/include/my_sys.h
index 4618c25dbfb..7c69a626366 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -531,7 +531,7 @@ typedef uint32 ha_checksum;
/* Define the type of function to be passed to process_default_option_files */
typedef int (*Process_option_func)(void *ctx, const char *group_name,
- const char *option);
+ const char *option);
#include <my_alloc.h>
@@ -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 *);
diff --git a/mysys/default.c b/mysys/default.c
index 73dca3b6c2f..1c06feec634 100644
--- a/mysys/default.c
+++ b/mysys/default.c
@@ -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,
@@ -181,40 +181,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.
SYNOPSIS
@@ -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
diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am
index ebaccd194dd..d73ba95ea0a 100644
--- a/server-tools/instance-manager/Makefile.am
+++ b/server-tools/instance-manager/Makefile.am
@@ -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 \
diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc
index ca84adbfd10..b000a48d5ae 100644
--- a/server-tools/instance-manager/buffer.cc
+++ b/server-tools/instance-manager/buffer.cc
@@ -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)
@@ -79,19 +79,31 @@ int Buffer::reserve(uint position, uint len_arg)
if (position + len_arg >= MAX_BUFFER_SIZE)
goto err;
- if (position + len_arg>= buffer_size)
+ if (position + len_arg >= buffer_size)
{
- buffer= (char *) realloc(buffer,
- min(MAX_BUFFER_SIZE,
- max((uint) (buffer_size*1.5),
- position + len_arg)));
- if (buffer == NULL)
+ buffer= (char *) my_realloc(buffer,
+ min(MAX_BUFFER_SIZE,
+ max((uint) (buffer_size*1.5),
+ 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;
+}
diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h
index 66860bd67b5..a94047b11a5 100644
--- a/server-tools/instance-manager/buffer.h
+++ b/server-tools/instance-manager/buffer.h
@@ -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);
};
diff --git a/server-tools/instance-manager/client_func.c b/server-tools/instance-manager/client_func.c
deleted file mode 100644
index a7ff1d27a8f..00000000000
--- a/server-tools/instance-manager/client_func.c
+++ /dev/null
@@ -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));
-}
diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc
index dacace1af1e..9831ad3a891 100644
--- a/server-tools/instance-manager/commands.cc
+++ b/server-tools/instance-manager/commands.cc
@@ -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()))
diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc
index 7375453673b..4fbfaf2672d 100644
--- a/server-tools/instance-manager/guardian.cc
+++ b/server-tools/instance-manager/guardian.cc
@@ -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();
+ pthread_mutex_lock(&LOCK_guardian);
- while (!thread_registry.is_shutdown())
+ /* loop, until all instances were shut down at the end */
+ while (!(shutdown_requested && (guarded_instances == NULL)))
{
- pthread_mutex_lock(&LOCK_guardian);
- loop= guarded_instances;
- while (loop != NULL)
+ node= guarded_instances;
+
+ while (node != 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;
+ 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()
+{
+ int var;
+ pthread_mutex_lock(&LOCK_guardian);
+ var= stopped;
+ pthread_mutex_unlock(&LOCK_guardian);
+ return var;
+}
+
+
+/*
+ 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
+ Guardian_thread::init()
+
+ NOTE: One should always lock guardian before calling this routine.
+
+ RETURN
+ 0 - ok
+ 1 - error occured
+*/
+
+int Guardian_thread::init()
{
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;
+
while ((instance= iterator.next()))
{
- if ((instance->options.is_guarded != NULL) && (instance->is_running()))
- if (guard(instance))
+ if (!(instance->options.nonguarded))
+ if (guard(instance, TRUE)) /* do not lock guardian */
+ {
+ instance_map->unlock();
return 1;
+ }
}
- instance_map->unlock();
+ instance_map->unlock();
return 0;
}
/*
- Start instance guarding
+ 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 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.
+ 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)
-{
- return add_instance_to_list(instance, &starting_instances);
-}
-
-
-void Guardian_thread::move_to_list(LIST **from, LIST **to)
-{
- LIST *tmp;
-
- while (*from)
- {
- tmp= rest(*from);
- *to= list_add(*to, *from);
- *from= tmp;
- }
-}
-
-
-int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list)
+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;
-
- pthread_mutex_lock(&LOCK_guardian);
- *list= list_add(*list, node);
- pthread_mutex_unlock(&LOCK_guardian);
+ 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);
+ 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);
+}
diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h
index 0ae2161f1dc..e43b5d565d7 100644
--- a/server-tools/instance-manager/guardian.h
+++ b/server-tools/instance-manager/guardian.h
@@ -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 */
diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc
index 58bc5b85dd8..97282f3f800 100644
--- a/server-tools/instance-manager/instance.cc
+++ b/server-tools/instance-manager/instance.cc
@@ -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
- {
- execv(options.mysqld_path, options.argv);
- exit(1);
- }
- case -1:
+ 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)
+ {
+ 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()
{
- /*
- We cannot close connection in destructor, as mysql_close needs alarm
- services which are definitely unavailaible at the time of destructor
- call.
- */
- if (is_connected)
- mysql_close(&mysql);
- return 0;
+ 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:
+ /*
+ Here we wait for the child created. This process differs for systems
+ running LinuxThreads and POSIX Threads compliant systems. This is because
+ according to POSIX we could wait() for a child in any thread of the
+ process. While LinuxThreads require that wait() is called by the thread,
+ which created the child.
+ On the other hand we could not expect mysqld to return the pid, we
+ got in from fork(), to wait4() fucntion when running on LinuxThreads.
+ This is because MySQL shutdown thread is not the one, which was created
+ by our fork() call.
+ So basically we have two options: whether the wait() call returns only in
+ the creator thread, but we cannot use waitpid() since we have no idea
+ which pid we should wait for (in fact it should be the pid of shutdown
+ thread, but we don't know this one). Or we could use waitpid(), but
+ couldn't use wait(), because it could return in any wait() in the program.
+ */
+ if (linuxthreads)
+ wait(NULL); /* LinuxThreads were detected */
+ else
+ waitpid(pid, NULL, 0);
+ /* set instance state to crashed */
+ pthread_mutex_lock(&LOCK_instance);
+ crashed= 1;
+ pthread_mutex_unlock(&LOCK_instance);
+
+ /*
+ Wake connection threads waiting for an instance to stop. This
+ is needed if a user issued command to stop an instance via
+ mysql connection. This is not the case if Guardian stop the thread.
+ */
+ pthread_cond_signal(&COND_instance_stopped);
+ /* wake guardian */
+ pthread_cond_signal(&instance_map->guardian->COND_guardian);
+ /* thread exits */
+ return;
+ }
+ /* we should never end up here */
+ DBUG_ASSERT(0);
+}
+
+
+Instance::Instance(): crashed(0)
+{
+ pthread_mutex_init(&LOCK_instance, 0);
+ pthread_cond_init(&COND_instance_stopped, 0);
}
Instance::~Instance()
{
+ 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);
+ /* try to connect to a server with a fake username/password pair */
+ if (mysql_real_connect(&mysql, LOCAL_HOST, username,
+ password,
+ NullS, port,
+ socket, 0))
{
- mysql_init(&mysql);
- if (mysql_real_connect(&mysql, LOCAL_HOST, options.mysqld_user,
- options.mysqld_password,
- NullS, port,
- socket, 0))
- {
- mysql.reconnect= 1;
- is_connected= TRUE;
- pthread_mutex_unlock(&LOCK_instance);
- return TRUE;
- }
- mysql_close(&mysql);
+ /*
+ 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 FALSE;
+ return_val= TRUE; /* server is alive */
}
- else if (!mysql_ping(&mysql))
+ else
{
- pthread_mutex_unlock(&LOCK_instance);
- return TRUE;
+ 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;
+
+ return return_val;
}
@@ -139,22 +252,67 @@ bool Instance::is_running()
int Instance::stop()
{
- if (is_running())
+ 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;
+
+ while (options.get_pid() != 0) /* while server isn't stopped */
{
- if (mysql_shutdown(&mysql, SHUTDOWN_DEFAULT))
- goto err;
+ int status;
- mysql_close(&mysql);
- is_connected= FALSE;
- return 0;
+ 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);
+}
diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h
index 6733985116a..c141ff7b30d 100644
--- a/server-tools/instance-manager/instance.h
+++ b/server-tools/instance-manager/instance.h
@@ -25,36 +25,44 @@
#pragma interface
#endif
+class Instance_map;
+
class Instance
{
public:
- Instance(): is_connected(FALSE)
- {}
- ~Instance();
+ 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 */
diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc
index 9399d6e2563..d19d42662e5 100644
--- a/server-tools/instance-manager/instance_map.cc
+++ b/server-tools/instance-manager/instance_map.cc
@@ -74,7 +74,7 @@ static void delete_instance(void *u)
1 - error occured
*/
-static int process_option(void * ctx, const char *group, const char *option)
+static int process_option(void *ctx, const char *group, const char *option)
{
Instance_map *map= NULL;
Instance *instance= NULL;
@@ -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;
- 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)
+ if (hash.records == 0) /* no instances found */
{
- instance= (Instance *) hash_element(&hash, i);
- if (instance->cleanup())
- return 1;
- i++;
- }
+ if ((instance= new Instance) == 0)
+ goto err;
- return 0;
-}
+ if (instance->init("mysqld") || add_instance(instance))
+ goto err_instance;
-Instance *
-Instance_map::find(uint instance_number)
-{
- Instance *instance;
- char name[80];
+ /*
+ After an instance have been added to the instance_map,
+ hash_free should handle it's deletion => goto err, not
+ err_instance.
+ */
+ if (instance->complete_initialization(this, mysqld_path, 1))
+ goto err;
+ }
+ else
+ while (i < hash.records)
+ {
+ instance= (Instance *) hash_element(&hash, i);
+ if (instance->complete_initialization(this, mysqld_path))
+ goto err;
+ i++;
+ }
- 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;
+ return 0;
+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;
}
diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h
index 522785ce9b9..8d8ea33cf5d 100644
--- a/server-tools/instance-manager/instance_map.h
+++ b/server-tools/instance-manager/instance_map.h
@@ -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;
diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc
index fab69865c85..e1b81bc93b1 100644
--- a/server-tools/instance-manager/instance_options.cc
+++ b/server-tools/instance-manager/instance_options.cc
@@ -19,44 +19,171 @@
#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)
-{
- /* 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;
+/*
+ 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
+*/
- if (mysqld_path == NULL)
+int Instance_options::get_default_option(char *result, size_t result_len,
+ const char *option_name)
+{
+ 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 */
{
- if (!(mysqld_path= strdup_root(&alloc, default_path)))
+ 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);
}
- /* this option must be first in the argv */
- if (add_to_argv(mysqld_path))
- goto err;
+ return rc;
+err:
+ return 1;
+}
+
- /* the following options are not for argv */
- if (mysqld_user == NULL)
+int Instance_options::get_pid_filename(char *result)
+{
+ const char *pid_file= mysqld_pid_file;
+ char datadir[MAX_PATH_LEN];
+
+ if (!(mysqld_datadir))
{
- if (!(mysqld_user= strdup_root(&alloc, default_user)))
- goto err;
+ /* 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);
+}
+
- if (mysqld_password == NULL)
+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)
{
- if (!(mysqld_password= strdup_root(&alloc, default_password)))
+ 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;
}
+ 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;
+
+ if (add_to_argv(tmp))
+ goto err;
+
memcpy((gptr) (argv + filled_default_options), options_array.buffer,
options_array.elements*sizeof(char*));
argv[filled_default_options + options_array.elements]= 0;
@@ -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.
diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h
index 5bc46497d2a..06ad0156bc0 100644
--- a/server-tools/instance-manager/instance_options.h
+++ b/server-tools/instance-manager/instance_options.h
@@ -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;
diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc
index 15f57e7e595..946bab11a2e 100644
--- a/server-tools/instance-manager/listener.cc
+++ b/server-tools/instance-manager/listener.cc
@@ -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;
}
diff --git a/server-tools/instance-manager/log.cc b/server-tools/instance-manager/log.cc
index f89f5e425b8..dbcea3f9c3d 100644
--- a/server-tools/instance-manager/log.cc
+++ b/server-tools/instance-manager/log.cc
@@ -32,8 +32,8 @@
SYNOPSYS
log()
*/
-
-static inline void log(FILE *file, const char *format, va_list args)
+
+static inline void log(FILE *file, const char *format, va_list args)
{
/*
log() should be thread-safe; it implies that we either call fprintf()
diff --git a/server-tools/instance-manager/log.h b/server-tools/instance-manager/log.h
index a1441d5bd71..825d7515513 100644
--- a/server-tools/instance-manager/log.h
+++ b/server-tools/instance-manager/log.h
@@ -17,14 +17,14 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*
- Logging facilities.
+ Logging facilities.
Two logging streams are supported: error log and info log. Additionally
libdbug may be used for debug information output.
ANSI C buffered I/O is used to perform logging.
Logging is performed via stdout/stder, so one can reopen them to point to
ordinary files. To initialize loggin environment log_init() must be called.
-
+
Rationale:
- no MYSQL_LOG as it has BIN mode, and not easy to fetch from sql_class.h
- no constructors/desctructors to make logging available all the time
diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc
index 07d4f1ed33e..8629c5e9d9c 100644
--- a/server-tools/instance-manager/manager.cc
+++ b/server-tools/instance-manager/manager.cc
@@ -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:
- thread_registry.deliver_shutdown();
- shutdown_complete= TRUE;
+ {
+ if (!guardian_thread.is_stopped())
+ {
+ bool stop_instances= true;
+ guardian_thread.request_shutdown(stop_instances);
+ pthread_cond_signal(&guardian_thread.COND_guardian);
+ }
+ else
+ {
+ thread_registry.deliver_shutdown();
+ shutdown_complete= TRUE;
+ }
+ }
break;
}
}
@@ -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 */
diff --git a/server-tools/instance-manager/messages.cc b/server-tools/instance-manager/messages.cc
index cc07352f58a..e3c2423a47e 100644
--- a/server-tools/instance-manager/messages.cc
+++ b/server-tools/instance-manager/messages.cc
@@ -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:
diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc
index d3f58bf3771..7947de70459 100644
--- a/server-tools/instance-manager/mysql_connection.cc
+++ b/server-tools/instance-manager/mysql_connection.cc
@@ -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;
diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc
index 9ad8ce2c858..e1d267b492e 100644
--- a/server-tools/instance-manager/mysqlmanager.cc
+++ b/server-tools/instance-manager/mysqlmanager.cc
@@ -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
diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc
index 05493e10ad8..9e4205b1407 100644
--- a/server-tools/instance-manager/options.cc
+++ b/server-tools/instance-manager/options.cc
@@ -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::bind_address= 0; /* No default value */
+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);
+}
diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h
index fc2b44c8b53..3df259864be 100644
--- a/server-tools/instance-manager/options.h
+++ b/server-tools/instance-manager/options.h
@@ -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
diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc
index d029267f9b8..4c897ddf933 100644
--- a/server-tools/instance-manager/parse.cc
+++ b/server-tools/instance-manager/parse.cc
@@ -50,29 +50,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
*/
diff --git a/server-tools/instance-manager/parse.h b/server-tools/instance-manager/parse.h
index 236a9bee53a..92519893302 100644
--- a/server-tools/instance-manager/parse.h
+++ b/server-tools/instance-manager/parse.h
@@ -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 */
diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc
new file mode 100644
index 00000000000..6fee0797395
--- /dev/null
+++ b/server-tools/instance-manager/parse_output.cc
@@ -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;
+}
diff --git a/server-tools/instance-manager/parse_output.h b/server-tools/instance-manager/parse_output.h
new file mode 100644
index 00000000000..20503a74629
--- /dev/null
+++ b/server-tools/instance-manager/parse_output.h
@@ -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);
+
diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc
index 4b47fe5b593..dd192370aaf 100644
--- a/server-tools/instance-manager/priv.cc
+++ b/server-tools/instance-manager/priv.cc
@@ -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
diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h
index 8014df7260c..decc3605dff 100644
--- a/server-tools/instance-manager/priv.h
+++ b/server-tools/instance-manager/priv.h
@@ -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;
diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc
index 581157ccd72..60cd0e8714f 100644
--- a/server-tools/instance-manager/protocol.cc
+++ b/server-tools/instance-manager/protocol.cc
@@ -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 */
diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc
index d0bf51f3d61..bf01aa6d7b2 100644
--- a/server-tools/instance-manager/thread_registry.cc
+++ b/server-tools/instance-manager/thread_registry.cc
@@ -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);
}
diff --git a/server-tools/instance-manager/thread_repository.cc b/server-tools/instance-manager/thread_repository.cc
deleted file mode 100644
index d0b302d29fb..00000000000
--- a/server-tools/instance-manager/thread_repository.cc
+++ /dev/null
@@ -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);
-}
-
diff --git a/server-tools/instance-manager/thread_repository.h b/server-tools/instance-manager/thread_repository.h
deleted file mode 100644
index 7bd21d66e3d..00000000000
--- a/server-tools/instance-manager/thread_repository.h
+++ /dev/null
@@ -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
diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc
index 8cbceceac7c..7cb2cd67d7f 100644
--- a/server-tools/instance-manager/user_map.cc
+++ b/server-tools/instance-manager/user_map.cc
@@ -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))