summaryrefslogtreecommitdiff
path: root/server-tools/instance-manager/instance.cc
diff options
context:
space:
mode:
Diffstat (limited to 'server-tools/instance-manager/instance.cc')
-rw-r--r--server-tools/instance-manager/instance.cc540
1 files changed, 299 insertions, 241 deletions
diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc
index e91dfab7ad9..84986c8c5ec 100644
--- a/server-tools/instance-manager/instance.cc
+++ b/server-tools/instance-manager/instance.cc
@@ -20,7 +20,6 @@
#include "instance.h"
-#include <my_global.h>
#include <mysql.h>
#include <signal.h>
@@ -29,21 +28,15 @@
#endif
#include "guardian.h"
-#include "instance_map.h"
+#include "manager.h"
#include "log.h"
#include "mysql_manager_error.h"
#include "portability.h"
#include "priv.h"
#include "thread_registry.h"
+#include "instance_map.h"
-
-const LEX_STRING
-Instance::DFLT_INSTANCE_NAME= { C_STRING_WITH_LEN("mysqld") };
-
-static const char * const INSTANCE_NAME_PREFIX= Instance::DFLT_INSTANCE_NAME.str;
-static const int INSTANCE_NAME_PREFIX_LEN= Instance::DFLT_INSTANCE_NAME.length;
-
-
+/* {{{ Platform-specific functions. */
#ifndef __WIN__
typedef pid_t My_process_info;
@@ -73,15 +66,16 @@ private:
void Instance_monitor::run()
{
- start_and_monitor_instance(&instance->options, instance->get_map(),
- &instance->thread_registry);
+ start_and_monitor_instance(&instance->options,
+ Manager::get_instance_map(),
+ Manager::get_thread_registry());
delete this;
}
/*
Wait for an instance
- SYNOPSYS
+ SYNOPSIS
wait_process()
pi Pointer to the process information structure
(platform-dependent).
@@ -139,11 +133,10 @@ static int wait_process(My_process_info *pi)
}
#endif
-
/*
Launch an instance
- SYNOPSYS
+ SYNOPSIS
start_process()
instance_options Pointer to the options of the instance to be
launched.
@@ -151,13 +144,13 @@ static int wait_process(My_process_info *pi)
(platform-dependent).
RETURN
- 0 - Success
- 1 - Cannot create an instance
+ FALSE - Success
+ TRUE - Cannot create an instance
*/
#ifndef __WIN__
-static int start_process(Instance_options *instance_options,
- My_process_info *pi)
+static bool start_process(Instance_options *instance_options,
+ My_process_info *pi)
{
#ifndef __QNX__
*pi= fork();
@@ -177,15 +170,16 @@ static int start_process(Instance_options *instance_options,
/* exec never returns */
exit(1);
case -1:
- log_info("cannot create a new process to start instance '%s'.",
+ log_info("Instance '%s': can not start mysqld: fork() failed.",
(const char *) instance_options->instance_name.str);
- return 1;
+ return TRUE;
}
- return 0;
+
+ return FALSE;
}
#else
-static int start_process(Instance_options *instance_options,
- My_process_info *pi)
+static bool start_process(Instance_options *instance_options,
+ My_process_info *pi)
{
STARTUPINFO si;
@@ -200,7 +194,7 @@ static int start_process(Instance_options *instance_options,
char *cmdline= new char[cmdlen];
if (cmdline == NULL)
- return 1;
+ return TRUE;
cmdline[0]= 0;
for (int i= 0; instance_options->argv[i] != 0; i++)
@@ -224,14 +218,87 @@ static int start_process(Instance_options *instance_options,
pi); /* Pointer to PROCESS_INFORMATION structure */
delete cmdline;
- return (!result);
+ return !result;
+}
+#endif
+
+#ifdef __WIN__
+
+BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode)
+{
+ DWORD dwTID, dwCode, dwErr= 0;
+ HANDLE hProcessDup= INVALID_HANDLE_VALUE;
+ HANDLE hRT= NULL;
+ HINSTANCE hKernel= GetModuleHandle("Kernel32");
+ BOOL bSuccess= FALSE;
+
+ BOOL bDup= DuplicateHandle(GetCurrentProcess(),
+ hProcess, GetCurrentProcess(), &hProcessDup,
+ PROCESS_ALL_ACCESS, FALSE, 0);
+
+ // Detect the special case where the process is
+ // already dead...
+ if (GetExitCodeProcess((bDup) ? hProcessDup : hProcess, &dwCode) &&
+ (dwCode == STILL_ACTIVE))
+ {
+ FARPROC pfnExitProc;
+
+ pfnExitProc= GetProcAddress(hKernel, "ExitProcess");
+
+ hRT= CreateRemoteThread((bDup) ? hProcessDup : hProcess, NULL, 0,
+ (LPTHREAD_START_ROUTINE)pfnExitProc,
+ (PVOID)uExitCode, 0, &dwTID);
+
+ if (hRT == NULL)
+ dwErr= GetLastError();
+ }
+ else
+ dwErr= ERROR_PROCESS_ABORTED;
+
+ if (hRT)
+ {
+ // Must wait process to terminate to
+ // guarantee that it has exited...
+ WaitForSingleObject((bDup) ? hProcessDup : hProcess, INFINITE);
+
+ CloseHandle(hRT);
+ bSuccess= TRUE;
+ }
+
+ if (bDup)
+ CloseHandle(hProcessDup);
+
+ if (!bSuccess)
+ SetLastError(dwErr);
+
+ return bSuccess;
+}
+
+int kill(pid_t pid, int signum)
+{
+ HANDLE processhandle= ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
+ if (signum == SIGTERM)
+ ::SafeTerminateProcess(processhandle, 0);
+ else
+ ::TerminateProcess(processhandle, -1);
+ return 0;
}
#endif
+/* }}} */
+
+/* {{{ Static constants. */
+
+const LEX_STRING
+Instance::DFLT_INSTANCE_NAME= { C_STRING_WITH_LEN("mysqld") };
+
+/* }}} */
+
+
/*
Fork child, exec an instance and monitor it.
- SYNOPSYS
+ SYNOPSIS
start_and_monitor_instance()
old_instance_options Pointer to the options of the instance to be
launched. This info is likely to become obsolete
@@ -246,6 +313,13 @@ static int start_process(Instance_options *instance_options,
set appropriate flags and wake all threads waiting for instance
to stop.
+ NOTE
+ A separate thread for starting/monitoring instance is a simple way
+ to avoid all pitfalls of the threads implementation in the OS (e.g.
+ LinuxThreads). For one, 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.
+
RETURN
Function returns no value
*/
@@ -261,8 +335,8 @@ start_and_monitor_instance(Instance_options *old_instance_options,
My_process_info process_info;
Thread_info thread_info;
- log_info("Monitoring thread (instance: '%s'): started.",
- (const char *) instance_name.get_c_str());
+ log_info("Instance '%s': Monitor: started.",
+ (const char *) instance->get_name()->str);
if (!old_instance_options->nonguarded)
{
@@ -285,8 +359,8 @@ start_and_monitor_instance(Instance_options *old_instance_options,
are using is destroyed. (E.g. by "FLUSH INSTANCES")
*/
- log_info("starting instance %s...",
- (const char *) instance_name.get_c_str());
+ log_info("Instance '%s': Monitor: starting mysqld...",
+ (const char *) instance->get_name()->str);
if (start_process(old_instance_options, &process_info))
{
@@ -297,8 +371,10 @@ start_and_monitor_instance(Instance_options *old_instance_options,
/* allow users to delete instances */
instance_map->unlock();
- /* don't check for return value */
- wait_process(&process_info);
+ log_info("Instance '%s': Monitor: waiting for mysqld to stop...",
+ (const char *) instance->get_name()->str);
+
+ wait_process(&process_info); /* Don't check for return value. */
instance_map->lock();
@@ -312,16 +388,17 @@ start_and_monitor_instance(Instance_options *old_instance_options,
if (!old_instance_options->nonguarded)
thread_registry->unregister_thread(&thread_info);
- log_info("Monitoring thread (instance: '%s'): finished.",
- (const char *) instance_name.get_c_str());
+ log_info("Instance '%s': Monitor: finished.",
+ (const char *) instance->get_name()->str);
}
bool Instance::is_name_valid(const LEX_STRING *name)
{
- const char *name_suffix= name->str + INSTANCE_NAME_PREFIX_LEN;
+ const char *name_suffix= name->str + DFLT_INSTANCE_NAME.length;
- if (strncmp(name->str, INSTANCE_NAME_PREFIX, INSTANCE_NAME_PREFIX_LEN) != 0)
+ if (strncmp(name->str, Instance::DFLT_INSTANCE_NAME.str,
+ Instance::DFLT_INSTANCE_NAME.length) != 0)
return FALSE;
return *name_suffix == 0 || my_isdigit(default_charset_info, *name_suffix);
@@ -330,31 +407,139 @@ bool Instance::is_name_valid(const LEX_STRING *name)
bool Instance::is_mysqld_compatible_name(const LEX_STRING *name)
{
- return strcmp(name->str, INSTANCE_NAME_PREFIX) == 0;
+ return strcmp(name->str, DFLT_INSTANCE_NAME.str) == 0;
}
-Instance_map *Instance::get_map()
+
+/* {{{ Constructor & destructor */
+
+Instance::Instance()
+ :crashed(FALSE),
+ configured(FALSE)
{
- return instance_map;
+ pthread_mutex_init(&LOCK_instance, 0);
+ pthread_cond_init(&COND_instance_stopped, 0);
}
-void Instance::remove_pid()
+Instance::~Instance()
{
- int pid;
- if ((pid= options.get_pid()) != 0) /* check the pidfile */
- if (options.unlink_pidfile()) /* remove stalled pidfile */
- log_error("cannot remove pidfile for instance '%s', this might be "
- "since IM lacks permmissions or hasn't found the pidifle",
- (const char *) options.instance_name.str);
+ log_info("Instance '%s': destroying...", (const char *) get_name()->str);
+
+ pthread_cond_destroy(&COND_instance_stopped);
+ pthread_mutex_destroy(&LOCK_instance);
}
+/* }}} */
+
+/*
+ Initialize instance options.
+
+ SYNOPSIS
+ init()
+ name_arg name of the instance
+
+ RETURN:
+ FALSE - ok
+ TRUE - error
+*/
+
+bool Instance::init(const LEX_STRING *name_arg)
+{
+ mysqld_compatible= is_mysqld_compatible_name(name_arg);
+
+ return options.init(name_arg);
+}
+
+
+/*
+ Complete instance options initialization.
+
+ SYNOPSIS
+ complete_initialization()
+
+ RETURN
+ FALSE - ok
+ TRUE - error
+*/
+
+bool Instance::complete_initialization()
+{
+ configured= ! options.complete_initialization();
+ return FALSE;
+ /*
+ TODO: return actual status (from
+ Instance_options::complete_initialization()) here.
+ */
+}
+
+/*
+ Determine if mysqld is accepting connections.
+
+ SYNOPSIS
+ is_mysqld_running()
+
+ DESCRIPTION
+ Try to connect to mysqld with fake login/password to check whether it is
+ accepting connections or not.
+
+ MT-NOTE: this operation must be called under acquired LOCK_instance.
+
+ RETURN
+ TRUE - mysqld is alive and accept connections
+ FALSE - otherwise.
+*/
+
+bool Instance::is_mysqld_running()
+{
+ MYSQL mysql;
+ uint port= options.get_mysqld_port(); /* 0 if not specified. */
+ const char *socket= NULL;
+ static const char *password= "check_connection";
+ static const char *username= "MySQL_Instance_Manager";
+ static const char *access_denied_message= "Access denied for user";
+ bool return_val;
+
+ if (options.mysqld_socket)
+ socket= options.mysqld_socket;
+
+ /* no port was specified => instance falled back to default value */
+ if (!port && !options.mysqld_socket)
+ port= SERVER_DEFAULT_PORT;
+
+ pthread_mutex_lock(&LOCK_instance);
+
+ 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))
+ {
+ /*
+ We have successfully connected to the server using fake
+ username/password. Write a warning to the logfile.
+ */
+ log_error("Instance '%s': was able to log into mysqld.",
+ (const char *) get_name()->str);
+ pthread_mutex_unlock(&LOCK_instance);
+ return_val= TRUE; /* server is alive */
+ }
+ else
+ return_val= test(!strncmp(access_denied_message, mysql_error(&mysql),
+ sizeof(access_denied_message) - 1));
+
+ mysql_close(&mysql);
+ pthread_mutex_unlock(&LOCK_instance);
+
+ return return_val;
+}
/*
The method starts an instance.
- SYNOPSYS
+ SYNOPSIS
start()
RETURN
@@ -372,7 +557,7 @@ int Instance::start()
pthread_mutex_unlock(&LOCK_instance);
- if (configured && !is_running())
+ if (configured && !is_mysqld_running())
{
Instance_monitor *instance_monitor;
remove_pid();
@@ -399,7 +584,7 @@ int Instance::start()
The method sets the crash flag and wakes all waiters on
COND_instance_stopped and COND_guardian
- SYNOPSYS
+ SYNOPSIS
set_crash_flag_n_wake_all()
DESCRIPTION
@@ -425,97 +610,14 @@ void Instance::set_crash_flag_n_wake_all()
*/
pthread_cond_signal(&COND_instance_stopped);
/* wake guardian */
- pthread_cond_signal(&instance_map->guardian->COND_guardian);
-}
-
-
-
-Instance::Instance(Thread_registry &thread_registry_arg):
- thread_registry(thread_registry_arg), crashed(FALSE), configured(FALSE)
-{
- 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);
-}
-
-
-bool Instance::is_crashed()
-{
- bool 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;
- static const char *password= "check_connection";
- static const char *username= "MySQL_Instance_Manager";
- static const char *access_denied_message= "Access denied for user";
- bool return_val;
-
- if (options.mysqld_port)
- {
- /*
- NOTE: it is important to check mysqld_port here, but use
- mysqld_port_val. The idea is that if the option is unset, mysqld_port
- will be NULL, but mysqld_port_val will not be reset.
- */
- port= options.mysqld_port_val;
- }
-
- if (options.mysqld_socket)
- socket= options.mysqld_socket;
-
- /* no port was specified => instance falled back to default value */
- if (!options.mysqld_port && !options.mysqld_socket)
- port= SERVER_DEFAULT_PORT;
-
- pthread_mutex_lock(&LOCK_instance);
-
- 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))
- {
- /*
- 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_val= TRUE; /* server is alive */
- }
- else
- return_val= test(!strncmp(access_denied_message, mysql_error(&mysql),
- sizeof(access_denied_message) - 1));
-
- mysql_close(&mysql);
- pthread_mutex_unlock(&LOCK_instance);
-
- return return_val;
+ pthread_cond_signal(&Manager::get_guardian()->COND_guardian);
}
/*
Stop an instance.
- SYNOPSYS
+ SYNOPSIS
stop()
RETURN:
@@ -529,19 +631,11 @@ int Instance::stop()
struct timespec timeout;
uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY;
- if (is_running())
+ if (is_mysqld_running())
{
- if (options.shutdown_delay)
- {
- /*
- NOTE: it is important to check shutdown_delay here, but use
- shutdown_delay_val. The idea is that if the option is unset,
- shutdown_delay will be NULL, but shutdown_delay_val will not be reset.
- */
- waitchild= options.shutdown_delay_val;
- }
+ waitchild= options.get_shutdown_delay();
- kill_instance(SIGTERM);
+ kill_mysqld(SIGTERM);
/* sleep on condition to wait for SIGCHLD */
timeout.tv_sec= time(NULL) + waitchild;
@@ -549,7 +643,7 @@ int Instance::stop()
if (pthread_mutex_lock(&LOCK_instance))
return ER_STOP_INSTANCE;
- while (options.get_pid() != 0) /* while server isn't stopped */
+ while (options.load_pid() != 0) /* while server isn't stopped */
{
int status;
@@ -562,7 +656,7 @@ int Instance::stop()
pthread_mutex_unlock(&LOCK_instance);
- kill_instance(SIGKILL);
+ kill_mysqld(SIGKILL);
return 0;
}
@@ -570,120 +664,84 @@ int Instance::stop()
return ER_INSTANCE_IS_NOT_STARTED;
}
-#ifdef __WIN__
-BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode)
-{
- DWORD dwTID, dwCode, dwErr= 0;
- HANDLE hProcessDup= INVALID_HANDLE_VALUE;
- HANDLE hRT= NULL;
- HINSTANCE hKernel= GetModuleHandle("Kernel32");
- BOOL bSuccess= FALSE;
-
- BOOL bDup= DuplicateHandle(GetCurrentProcess(),
- hProcess, GetCurrentProcess(), &hProcessDup,
- PROCESS_ALL_ACCESS, FALSE, 0);
-
- // Detect the special case where the process is
- // already dead...
- if (GetExitCodeProcess((bDup) ? hProcessDup : hProcess, &dwCode) &&
- (dwCode == STILL_ACTIVE))
- {
- FARPROC pfnExitProc;
-
- pfnExitProc= GetProcAddress(hKernel, "ExitProcess");
+/*
+ Send signal to mysqld.
- hRT= CreateRemoteThread((bDup) ? hProcessDup : hProcess, NULL, 0,
- (LPTHREAD_START_ROUTINE)pfnExitProc,
- (PVOID)uExitCode, 0, &dwTID);
+ SYNOPSIS
+ kill_mysqld()
+*/
- if (hRT == NULL)
- dwErr= GetLastError();
- }
- else
- dwErr= ERROR_PROCESS_ABORTED;
+void Instance::kill_mysqld(int signum)
+{
+ pid_t mysqld_pid= options.load_pid();
- if (hRT)
+ if (mysqld_pid == 0)
{
- // Must wait process to terminate to
- // guarantee that it has exited...
- WaitForSingleObject((bDup) ? hProcessDup : hProcess, INFINITE);
-
- CloseHandle(hRT);
- bSuccess= TRUE;
+ log_info("Instance '%s': no pid file to send a signal (%d).",
+ (const char *) get_name()->str,
+ (int) signum);
+ return;
}
- if (bDup)
- CloseHandle(hProcessDup);
-
- if (!bSuccess)
- SetLastError(dwErr);
+ log_info("Instance '%s': sending %d to %d...",
+ (const char *) get_name()->str,
+ (int) signum,
+ (int) mysqld_pid);
- return bSuccess;
-}
-
-int kill(pid_t pid, int signum)
-{
- HANDLE processhandle= ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
- if (signum == SIGTERM)
- ::SafeTerminateProcess(processhandle, 0);
- else
- ::TerminateProcess(processhandle, -1);
- return 0;
-}
-#endif
+ if (kill(mysqld_pid, signum))
+ {
+ log_info("Instance '%s': kill() failed.",
+ (const char *) get_name()->str);
+ return;
+ }
-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 */
+ /* Kill suceeded */
+ if (signum == SIGKILL) /* really killed instance with SIGKILL */
{
- if (kill(pid, signum) == 0)
- {
- /* Kill suceeded */
- if (signum == SIGKILL) /* really killed instance with SIGKILL */
- {
- log_error("The instance '%s' is being stopped forcibly. Normally"
- "it should not happen. Probably the instance has been"
- "hanging. You should also check your IM setup",
- (const char *) options.instance_name.str);
- /* After sucessful hard kill the pidfile need to be removed */
- options.unlink_pidfile();
- }
- }
+ log_error("The instance '%s' is being stopped forcibly. Normally"
+ "it should not happen. Probably the instance has been"
+ "hanging. You should also check your IM setup",
+ (const char *) options.instance_name.str);
+ /* After sucessful hard kill the pidfile need to be removed */
+ options.unlink_pidfile();
}
- return;
}
/*
- Initialize instance parameters.
+ Return crashed flag.
- SYNOPSYS
- Instance::init()
- name_arg name of the instance
+ SYNOPSIS
+ is_crashed()
- RETURN:
- 0 ok
- !0 error
+ RETURN
+ TRUE - mysqld crashed
+ FALSE - mysqld hasn't crashed yet
*/
-int Instance::init(const LEX_STRING *name_arg)
+bool Instance::is_crashed()
{
- mysqld_compatible= is_mysqld_compatible_name(name_arg);
-
- return options.init(name_arg);
+ bool val;
+ pthread_mutex_lock(&LOCK_instance);
+ val= crashed;
+ pthread_mutex_unlock(&LOCK_instance);
+ return val;
}
+/*
+ Remove pid file.
+*/
-int Instance::complete_initialization(Instance_map *instance_map_arg,
- const char *mysqld_path)
+void Instance::remove_pid()
{
- instance_map= instance_map_arg;
- configured= !options.complete_initialization(mysqld_path);
- return 0;
- /*
- TODO: return actual status (from
- Instance_options::complete_initialization()) here.
- */
+ int mysqld_pid= options.load_pid();
+
+ if (mysqld_pid == 0)
+ return;
+
+ if (options.unlink_pidfile())
+ {
+ log_error("Instance '%s': can not unlink pid file.",
+ (const char *) options.instance_name.str);
+ }
}