diff options
Diffstat (limited to 'server-tools/instance-manager/instance.cc')
-rw-r--r-- | server-tools/instance-manager/instance.cc | 540 |
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); + } } |