diff options
author | unknown <kostja@bodhi.local> | 2006-11-18 01:34:44 +0300 |
---|---|---|
committer | unknown <kostja@bodhi.local> | 2006-11-18 01:34:44 +0300 |
commit | c95482d4539adc271ac41fa75b70d5d6b696634f (patch) | |
tree | 8edacec17e19484691a25cc8da80c303515238f9 /server-tools/instance-manager/instance.cc | |
parent | 08df05560edb4beeccdf605f43008750731f6e18 (diff) | |
download | mariadb-git-c95482d4539adc271ac41fa75b70d5d6b696634f.tar.gz |
Port cleanups, trivial refactoring and code rearrangements from
Alik's patch for BUG#22306: STOP INSTANCE can not be applied for
instances in Crashed, Failed and Abandoned" to ease review process.
Evaluate global variable linuxthreads before starting threads to avoid
a race.
server-tools/instance-manager/buffer.cc:
Fix spelling.
server-tools/instance-manager/command.h:
Fix spelling.
server-tools/instance-manager/commands.cc:
Fix spelling.
server-tools/instance-manager/commands.h:
Fix spelling, tidy up.
server-tools/instance-manager/guardian.cc:
Cleanup logging, options.get_shutdown_delay() is a method, tidy up.
server-tools/instance-manager/instance.cc:
Rearrange methods to be the same as in Alik's patch, fix spelling errors,
clean up logging texts, port comments from Alik's patch,
implement some basic renames from his patch.
No real changes.
server-tools/instance-manager/instance.h:
Tidy up, renames.
server-tools/instance-manager/instance_map.cc:
Fix spellings, port some refactoring from Alik's patch.
server-tools/instance-manager/instance_map.h:
Cleanup.
server-tools/instance-manager/instance_options.cc:
Cleanup. Implement Instance_options::get_shutdown_delay() and
Instance_options::get_mysqld_port().
server-tools/instance-manager/instance_options.h:
Cleanup.
server-tools/instance-manager/listener.cc:
Cleanup.
server-tools/instance-manager/log.cc:
Fix spelling.
server-tools/instance-manager/manager.cc:
Cleanup.
server-tools/instance-manager/manager.h:
Add getters for Manager members.
server-tools/instance-manager/mysqlmanager.cc:
Evaluate linuxthreads before starting threads to avoid a race.
server-tools/instance-manager/parse_output.cc:
Fix spelling.
server-tools/instance-manager/priv.cc:
Cleanup.
server-tools/instance-manager/priv.h:
Cleanup.
server-tools/instance-manager/user_management_commands.cc:
Fix spelling.
server-tools/instance-manager/user_management_commands.h:
Fix spelling.
server-tools/instance-manager/user_map.cc:
Fix spelling.
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); + } } |