mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-30 04:26:45 +03:00 
			
		
		
		
	The problem was a race condition on shutdown -- when IM got shutdown request while a guarded mysqld is starting. In this case the Guardian thread tried to stop the mysqld, but might fail if the mysqld hadn't created pid-file so far. When this happened, the mysqld-monitor thread didn't stop, so the assert in Thread_registry happened. The fix is to make several attempts to stop mysqld if it is active. server-tools/instance-manager/guardian.cc: Try to stop mysqld several times if it is still active. server-tools/instance-manager/instance.cc: Make Instance::kill_mysqld() to return operation status. server-tools/instance-manager/instance.h: Make Instance::kill_mysqld() to return operation status. server-tools/instance-manager/thread_registry.cc: Log unregistered thread ids.
		
			
				
	
	
		
			945 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			945 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* 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; version 2 of the License.
 | |
| 
 | |
|    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 */
 | |
| 
 | |
| #if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
 | |
| #pragma implementation
 | |
| #endif
 | |
| 
 | |
| #include <my_global.h>
 | |
| #include <mysql.h>
 | |
| 
 | |
| #include <signal.h>
 | |
| #ifndef __WIN__
 | |
| #include <sys/wait.h>
 | |
| #endif
 | |
| 
 | |
| #include "manager.h"
 | |
| #include "guardian.h"
 | |
| #include "instance.h"
 | |
| #include "log.h"
 | |
| #include "mysql_manager_error.h"
 | |
| #include "portability.h"
 | |
| #include "priv.h"
 | |
| #include "thread_registry.h"
 | |
| #include "instance_map.h"
 | |
| 
 | |
| 
 | |
| /*************************************************************************
 | |
|   {{{ Platform-specific functions.
 | |
| *************************************************************************/
 | |
| 
 | |
| #ifndef __WIN__
 | |
| typedef pid_t My_process_info;
 | |
| #else
 | |
| typedef PROCESS_INFORMATION My_process_info;
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|   Wait for an instance
 | |
| 
 | |
|   SYNOPSIS
 | |
|     wait_process()
 | |
|     pi                   Pointer to the process information structure
 | |
|                          (platform-dependent).
 | |
| 
 | |
|   RETURN
 | |
|    0  -  Success
 | |
|    1  -  Error
 | |
| */
 | |
| 
 | |
| #ifndef __WIN__
 | |
| static int wait_process(My_process_info *pi)
 | |
| {
 | |
|   /*
 | |
|     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 (Manager::is_linux_threads())
 | |
|     wait(NULL);                               /* LinuxThreads were detected */
 | |
|   else
 | |
|     waitpid(*pi, NULL, 0);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| #else
 | |
| static int wait_process(My_process_info *pi)
 | |
| {
 | |
|   /* Wait until child process exits. */
 | |
|   WaitForSingleObject(pi->hProcess, INFINITE);
 | |
| 
 | |
|   DWORD exitcode;
 | |
|   ::GetExitCodeProcess(pi->hProcess, &exitcode);
 | |
| 
 | |
|   /* Close process and thread handles. */
 | |
|   CloseHandle(pi->hProcess);
 | |
|   CloseHandle(pi->hThread);
 | |
| 
 | |
|   /*
 | |
|     GetExitCodeProces returns zero on failure. We should revert this value
 | |
|     to report an error.
 | |
|   */
 | |
|   return (!exitcode);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|   Launch an instance
 | |
| 
 | |
|   SYNOPSIS
 | |
|     start_process()
 | |
|     instance_options     Pointer to the options of the instance to be
 | |
|                          launched.
 | |
|     pi                   Pointer to the process information structure
 | |
|                          (platform-dependent).
 | |
| 
 | |
|   RETURN
 | |
|    FALSE - Success
 | |
|    TRUE  - Cannot create an instance
 | |
| */
 | |
| 
 | |
| #ifndef __WIN__
 | |
| static bool start_process(Instance_options *instance_options,
 | |
|                           My_process_info *pi)
 | |
| {
 | |
| #ifndef __QNX__
 | |
|   *pi= fork();
 | |
| #else
 | |
|   /*
 | |
|      On QNX one cannot use fork() in multithreaded environment and we
 | |
|      should use spawn() or one of it's siblings instead.
 | |
|      Here we use spawnv(), which  is a combination of fork() and execv()
 | |
|      in one call. It returns the pid of newly created process (>0) or -1
 | |
|   */
 | |
|   *pi= spawnv(P_NOWAIT, instance_options->mysqld_path, instance_options->argv);
 | |
| #endif
 | |
| 
 | |
|   switch (*pi) {
 | |
|   case 0:                                       /* never happens on QNX */
 | |
|     execv(instance_options->mysqld_path.str, instance_options->argv);
 | |
|     /* exec never returns */
 | |
|     exit(1);
 | |
|   case -1:
 | |
|     log_error("Instance '%s': can not start mysqld: fork() failed.",
 | |
|               (const char *) instance_options->instance_name.str);
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| #else
 | |
| static bool start_process(Instance_options *instance_options,
 | |
|                           My_process_info *pi)
 | |
| {
 | |
|   STARTUPINFO si;
 | |
| 
 | |
|   ZeroMemory(&si, sizeof(STARTUPINFO));
 | |
|   si.cb= sizeof(STARTUPINFO);
 | |
|   ZeroMemory(pi, sizeof(PROCESS_INFORMATION));
 | |
| 
 | |
|   int cmdlen= 0;
 | |
|   for (int i= 0; instance_options->argv[i] != 0; i++)
 | |
|     cmdlen+= strlen(instance_options->argv[i]) + 3;
 | |
|   cmdlen++;   /* make room for the null */
 | |
| 
 | |
|   char *cmdline= new char[cmdlen];
 | |
|   if (cmdline == NULL)
 | |
|     return TRUE;
 | |
| 
 | |
|   cmdline[0]= 0;
 | |
|   for (int i= 0; instance_options->argv[i] != 0; i++)
 | |
|   {
 | |
|     strcat(cmdline, "\"");
 | |
|     strcat(cmdline, instance_options->argv[i]);
 | |
|     strcat(cmdline, "\" ");
 | |
|   }
 | |
| 
 | |
|   /* Start the child process */
 | |
|   BOOL result=
 | |
|     CreateProcess(NULL,          /* Put it all in cmdline */
 | |
|                   cmdline,       /* Command line */
 | |
|                   NULL,          /* Process handle not inheritable */
 | |
|                   NULL,          /* Thread handle not inheritable */
 | |
|                   FALSE,         /* Set handle inheritance to FALSE */
 | |
|                   0,             /* No creation flags */
 | |
|                   NULL,          /* Use parent's environment block */
 | |
|                   NULL,          /* Use parent's starting directory */
 | |
|                   &si,           /* Pointer to STARTUPINFO structure */
 | |
|                   pi);           /* Pointer to PROCESS_INFORMATION structure */
 | |
|   delete cmdline;
 | |
| 
 | |
|   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") };
 | |
| 
 | |
| /*************************************************************************
 | |
|   }}}
 | |
| *************************************************************************/
 | |
| 
 | |
| 
 | |
| /*************************************************************************
 | |
|   {{{ Instance Monitor thread.
 | |
| *************************************************************************/
 | |
| 
 | |
| /**
 | |
|   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.
 | |
| 
 | |
|   Instance Monitor Thread forks a child process, execs mysqld and waits for
 | |
|   the child to die.
 | |
| 
 | |
|   Instance Monitor assumes that the monitoring instance will not be dropped.
 | |
|   This is guaranteed by having flag monitoring_thread_active and
 | |
|   Instance::is_active() operation.
 | |
| */
 | |
| 
 | |
| class Instance_monitor: public Thread
 | |
| {
 | |
| public:
 | |
|   Instance_monitor(Instance *instance_arg) :instance(instance_arg) {}
 | |
| protected:
 | |
|   virtual void run();
 | |
|   void start_and_monitor_instance();
 | |
| private:
 | |
|   Instance *instance;
 | |
| };
 | |
| 
 | |
| 
 | |
| void Instance_monitor::run()
 | |
| {
 | |
|   start_and_monitor_instance();
 | |
|   delete this;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Instance_monitor::start_and_monitor_instance()
 | |
| {
 | |
|   Thread_registry *thread_registry= Manager::get_thread_registry();
 | |
|   Guardian *guardian= Manager::get_guardian();
 | |
| 
 | |
|   My_process_info mysqld_process_info;
 | |
|   Thread_info monitor_thread_info;
 | |
| 
 | |
|   log_info("Instance '%s': Monitor: started.",
 | |
|            (const char *) instance->get_name()->str);
 | |
| 
 | |
|   /*
 | |
|     For guarded instance register the thread in Thread_registry to wait for
 | |
|     the thread to stop on shutdown (nonguarded instances are not stopped on
 | |
|     shutdown, so the thread will no finish).
 | |
|   */
 | |
| 
 | |
|   if (instance->is_guarded())
 | |
|   {
 | |
|     thread_registry->register_thread(&monitor_thread_info, FALSE);
 | |
|   }
 | |
| 
 | |
|   /* Starting mysqld. */
 | |
| 
 | |
|   log_info("Instance '%s': Monitor: starting mysqld...",
 | |
|            (const char *) instance->get_name()->str);
 | |
| 
 | |
|   if (start_process(&instance->options, &mysqld_process_info))
 | |
|   {
 | |
|     instance->lock();
 | |
|     instance->monitoring_thread_active= FALSE;
 | |
|     instance->unlock();
 | |
| 
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   /* Waiting for mysqld to die. */
 | |
| 
 | |
|   log_info("Instance '%s': Monitor: waiting for mysqld to stop...",
 | |
|            (const char *) instance->get_name()->str);
 | |
| 
 | |
|   wait_process(&mysqld_process_info); /* Don't check for return value. */
 | |
| 
 | |
|   log_info("Instance '%s': Monitor: mysqld stopped.",
 | |
|            (const char *) instance->get_name()->str);
 | |
| 
 | |
|   /* Update instance status. */
 | |
| 
 | |
|   instance->lock();
 | |
| 
 | |
|   if (instance->is_guarded())
 | |
|     thread_registry->unregister_thread(&monitor_thread_info);
 | |
| 
 | |
|   instance->crashed= TRUE;
 | |
|   instance->monitoring_thread_active= FALSE;
 | |
| 
 | |
|   log_info("Instance '%s': Monitor: finished.",
 | |
|            (const char *) instance->get_name()->str);
 | |
| 
 | |
|   instance->unlock();
 | |
| 
 | |
|   /* Wake up guardian. */
 | |
| 
 | |
|   guardian->ping();
 | |
| }
 | |
| 
 | |
| /**************************************************************************
 | |
|   }}}
 | |
| **************************************************************************/
 | |
| 
 | |
| 
 | |
| /**************************************************************************
 | |
|   {{{ Static operations.
 | |
| **************************************************************************/
 | |
| 
 | |
| /**
 | |
|   The operation is intended to check whether string is a well-formed
 | |
|   instance name or not.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     is_name_valid()
 | |
|     name  string to check
 | |
| 
 | |
|   RETURN
 | |
|     TRUE    string is a valid instance name
 | |
|     FALSE   string is not a valid instance name
 | |
| 
 | |
|   TODO: Move to Instance_name class: Instance_name::is_valid().
 | |
| */
 | |
| 
 | |
| bool Instance::is_name_valid(const LEX_STRING *name)
 | |
| {
 | |
|   const char *name_suffix= name->str + DFLT_INSTANCE_NAME.length;
 | |
| 
 | |
|   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);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   The operation is intended to check if the given instance name is
 | |
|   mysqld-compatible or not.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     is_mysqld_compatible_name()
 | |
|     name  name to check
 | |
| 
 | |
|   RETURN
 | |
|     TRUE    name is mysqld-compatible
 | |
|     FALSE   otherwise
 | |
| 
 | |
|   TODO: Move to Instance_name class: Instance_name::is_mysqld_compatible().
 | |
| */
 | |
| 
 | |
| bool Instance::is_mysqld_compatible_name(const LEX_STRING *name)
 | |
| {
 | |
|   return strcmp(name->str, DFLT_INSTANCE_NAME.str) == 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Return client state name. Must not be used outside the class.
 | |
|   Use Instance::get_state_name() instead.
 | |
| */
 | |
| 
 | |
| const char * Instance::get_instance_state_name(enum_instance_state state)
 | |
| {
 | |
|   switch (state) {
 | |
|   case STOPPED:
 | |
|     return "offline";
 | |
| 
 | |
|   case NOT_STARTED:
 | |
|     return "not started";
 | |
| 
 | |
|   case STARTING:
 | |
|     return "starting";
 | |
| 
 | |
|   case STARTED:
 | |
|     return "online";
 | |
| 
 | |
|   case JUST_CRASHED:
 | |
|     return "failed";
 | |
| 
 | |
|   case CRASHED:
 | |
|     return "crashed";
 | |
| 
 | |
|   case CRASHED_AND_ABANDONED:
 | |
|     return "abandoned";
 | |
| 
 | |
|   case STOPPING:
 | |
|     return "stopping";
 | |
|   }
 | |
| 
 | |
|   return NULL; /* just to ignore compiler warning. */
 | |
| }
 | |
| 
 | |
| /**************************************************************************
 | |
|   }}}
 | |
| **************************************************************************/
 | |
| 
 | |
| 
 | |
| /**************************************************************************
 | |
|   {{{ Initialization & deinitialization.
 | |
| **************************************************************************/
 | |
| 
 | |
| Instance::Instance()
 | |
|   :monitoring_thread_active(FALSE),
 | |
|   crashed(FALSE),
 | |
|   configured(FALSE),
 | |
|   /* mysqld_compatible is initialized in init() */
 | |
|   state(NOT_STARTED),
 | |
|   restart_counter(0),
 | |
|   crash_moment(0),
 | |
|   last_checked(0)
 | |
| {
 | |
|   pthread_mutex_init(&LOCK_instance, 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| Instance::~Instance()
 | |
| {
 | |
|   log_info("Instance '%s': destroying...", (const char *) get_name()->str);
 | |
| 
 | |
|   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);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @brief Complete instance options initialization.
 | |
| 
 | |
|   @return Error status.
 | |
|     @retval FALSE ok
 | |
|     @retval TRUE error
 | |
| */
 | |
| 
 | |
| bool Instance::complete_initialization()
 | |
| {
 | |
|   configured= ! options.complete_initialization();
 | |
|   return !configured;
 | |
| }
 | |
| 
 | |
| /**************************************************************************
 | |
|   }}}
 | |
| **************************************************************************/
 | |
| 
 | |
| 
 | |
| /**************************************************************************
 | |
|   {{{ Instance: public interface implementation.
 | |
| **************************************************************************/
 | |
| 
 | |
| /**
 | |
|   Determine if there is some activity with the instance.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     is_active()
 | |
| 
 | |
|   DESCRIPTION
 | |
|     An instance is active if one of the following conditions is true:
 | |
|       - Instance-monitoring thread is running;
 | |
|       - Instance is guarded and its state is other than STOPPED;
 | |
|       - Corresponding mysqld-server accepts connections.
 | |
| 
 | |
|     MT-NOTE: instance must be locked before calling the operation.
 | |
| 
 | |
|   RETURN
 | |
|     TRUE  - instance is active
 | |
|     FALSE - otherwise.
 | |
| */
 | |
| 
 | |
| bool Instance::is_active()
 | |
| {
 | |
|   if (monitoring_thread_active)
 | |
|     return TRUE;
 | |
| 
 | |
|   if (is_guarded() && get_state() != STOPPED)
 | |
|     return TRUE;
 | |
| 
 | |
|   return is_mysqld_running();
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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: instance must be locked before calling the operation.
 | |
| 
 | |
|   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;
 | |
| 
 | |
|   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);
 | |
|     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);
 | |
| 
 | |
|   return return_val;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @brief Start mysqld.
 | |
| 
 | |
|   Reset flags and start Instance Monitor thread, which will start mysqld.
 | |
| 
 | |
|   @note Instance must be locked before calling the operation.
 | |
| 
 | |
|   @return Error status code
 | |
|     @retval FALSE Ok
 | |
|     @retval TRUE Could not start instance
 | |
| */
 | |
| 
 | |
| bool Instance::start_mysqld()
 | |
| {
 | |
|   Instance_monitor *instance_monitor;
 | |
| 
 | |
|   if (!configured)
 | |
|     return TRUE;
 | |
| 
 | |
|   /*
 | |
|     Prepare instance to start Instance Monitor thread.
 | |
| 
 | |
|     NOTE: It's important to set these actions here in order to avoid
 | |
|     race conditions -- these actions must be done under acquired lock on
 | |
|     Instance.
 | |
|   */
 | |
| 
 | |
|   crashed= FALSE;
 | |
|   monitoring_thread_active= TRUE;
 | |
| 
 | |
|   remove_pid();
 | |
| 
 | |
|   /* Create and start the Instance Monitor thread. */
 | |
| 
 | |
|   instance_monitor= new Instance_monitor(this);
 | |
| 
 | |
|   if (instance_monitor == NULL || instance_monitor->start(Thread::DETACHED))
 | |
|   {
 | |
|     delete instance_monitor;
 | |
|     monitoring_thread_active= FALSE;
 | |
| 
 | |
|     log_error("Instance '%s': can not create instance monitor thread.",
 | |
|               (const char *) get_name()->str);
 | |
| 
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   ++restart_counter;
 | |
| 
 | |
|   /* The Instance Monitor thread will delete itself when it's finished. */
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Stop mysqld.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     stop_mysqld()
 | |
| 
 | |
|   DESCRIPTION
 | |
|     Try to stop mysqld gracefully. Otherwise kill it with SIGKILL.
 | |
| 
 | |
|     MT-NOTE: instance must be locked before calling the operation.
 | |
| 
 | |
|   RETURN
 | |
|     FALSE - ok
 | |
|     TRUE  - could not stop the instance
 | |
| */
 | |
| 
 | |
| bool Instance::stop_mysqld()
 | |
| {
 | |
|   log_info("Instance '%s': stopping mysqld...",
 | |
|            (const char *) get_name()->str);
 | |
| 
 | |
|   kill_mysqld(SIGTERM);
 | |
| 
 | |
|   if (!wait_for_stop())
 | |
|   {
 | |
|     log_info("Instance '%s': mysqld stopped gracefully.",
 | |
|              (const char *) get_name()->str);
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   log_info("Instance '%s': mysqld failed to stop gracefully within %d seconds.",
 | |
|            (const char *) get_name()->str,
 | |
|            (int) options.get_shutdown_delay());
 | |
| 
 | |
|   log_info("Instance'%s': killing mysqld...",
 | |
|            (const char *) get_name()->str);
 | |
| 
 | |
|   kill_mysqld(SIGKILL);
 | |
| 
 | |
|   if (!wait_for_stop())
 | |
|   {
 | |
|     log_info("Instance '%s': mysqld has been killed.",
 | |
|              (const char *) get_name()->str);
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   log_info("Instance '%s': can not kill mysqld within %d seconds.",
 | |
|            (const char *) get_name()->str,
 | |
|            (int) options.get_shutdown_delay());
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Send signal to mysqld.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     kill_mysqld()
 | |
| 
 | |
|   DESCRIPTION
 | |
|     Load pid from the pid file and send the given signal to that process.
 | |
|     If the signal is SIGKILL, remove the pid file after sending the signal.
 | |
| 
 | |
|     MT-NOTE: instance must be locked before calling the operation.
 | |
| 
 | |
|   TODO
 | |
|     This too low-level and OS-specific operation for public interface.
 | |
|     Also, it has some implicit behaviour for SIGKILL signal. Probably, we
 | |
|     should have the following public operations instead:
 | |
|       - start_mysqld() -- as is;
 | |
|       - stop_mysqld -- request mysqld to shutdown gracefully (send SIGTERM);
 | |
|         don't wait for complete shutdown;
 | |
|       - wait_for_stop() (or join_mysqld()) -- wait for mysqld to stop within
 | |
|         time interval;
 | |
|       - kill_mysqld() -- request to terminate mysqld; don't wait for
 | |
|         completion.
 | |
|     These operations should also be used in Guardian to manage instances.
 | |
| */
 | |
| 
 | |
| bool Instance::kill_mysqld(int signum)
 | |
| {
 | |
|   pid_t mysqld_pid= options.load_pid();
 | |
| 
 | |
|   if (mysqld_pid == 0)
 | |
|   {
 | |
|     log_info("Instance '%s': no pid file to send a signal (%d).",
 | |
|              (const char *) get_name()->str,
 | |
|              (int) signum);
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   log_info("Instance '%s': sending %d to %d...",
 | |
|            (const char *) get_name()->str,
 | |
|            (int) signum,
 | |
|            (int) mysqld_pid);
 | |
| 
 | |
|   if (kill(mysqld_pid, signum))
 | |
|   {
 | |
|     log_info("Instance '%s': kill() failed.",
 | |
|              (const char *) get_name()->str);
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   /* Kill suceeded */
 | |
|   if (signum == SIGKILL)      /* really killed instance with SIGKILL */
 | |
|   {
 | |
|     log_error("Instance '%s': killed.",
 | |
|               (const char *) options.instance_name.str);
 | |
| 
 | |
|     /* After sucessful hard kill the pidfile need to be removed */
 | |
|     options.unlink_pidfile();
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Lock instance.
 | |
| */
 | |
| 
 | |
| void Instance::lock()
 | |
| {
 | |
|   pthread_mutex_lock(&LOCK_instance);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Unlock instance.
 | |
| */
 | |
| 
 | |
| void Instance::unlock()
 | |
| {
 | |
|   pthread_mutex_unlock(&LOCK_instance);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Return instance state name.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     get_state_name()
 | |
| 
 | |
|   DESCRIPTION
 | |
|     The operation returns user-friendly state name. The operation can be
 | |
|     used both for guarded and non-guarded instances.
 | |
| 
 | |
|     MT-NOTE: instance must be locked before calling the operation.
 | |
| 
 | |
|   TODO: Replace with the static get_state_name(state_code) function.
 | |
| */
 | |
| 
 | |
| const char *Instance::get_state_name()
 | |
| {
 | |
|   if (!is_configured())
 | |
|     return "misconfigured";
 | |
| 
 | |
|   if (is_guarded())
 | |
|   {
 | |
|     /* The instance is managed by Guardian: we can report precise state. */
 | |
| 
 | |
|     return get_instance_state_name(get_state());
 | |
|   }
 | |
| 
 | |
|   /* The instance is not managed by Guardian: we can report status only.  */
 | |
| 
 | |
|   return is_active() ? "online" : "offline";
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Reset statistics.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     reset_stat()
 | |
| 
 | |
|   DESCRIPTION
 | |
|     The operation resets statistics used for guarding the instance.
 | |
| 
 | |
|     MT-NOTE: instance must be locked before calling the operation.
 | |
| 
 | |
|   TODO: Make private.
 | |
| */
 | |
| 
 | |
| void Instance::reset_stat()
 | |
| {
 | |
|   restart_counter= 0;
 | |
|   crash_moment= 0;
 | |
|   last_checked= 0;
 | |
| }
 | |
| 
 | |
| /**************************************************************************
 | |
|   }}}
 | |
| **************************************************************************/
 | |
| 
 | |
| 
 | |
| /**************************************************************************
 | |
|   {{{ Instance: implementation of private operations.
 | |
| **************************************************************************/
 | |
| 
 | |
| /**
 | |
|   Remove pid file.
 | |
| */
 | |
| 
 | |
| void Instance::remove_pid()
 | |
| {
 | |
|   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);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Wait for mysqld to stop within shutdown interval.
 | |
| */
 | |
| 
 | |
| bool Instance::wait_for_stop()
 | |
| {
 | |
|   int start_time= (int) time(NULL);
 | |
|   int finish_time= start_time + options.get_shutdown_delay();
 | |
| 
 | |
|   log_info("Instance '%s': waiting for mysqld to stop "
 | |
|            "(timeout: %d seconds)...",
 | |
|            (const char *) get_name()->str,
 | |
|            (int) options.get_shutdown_delay());
 | |
| 
 | |
|   while (true)
 | |
|   {
 | |
|     if (options.load_pid() == 0 && !is_mysqld_running())
 | |
|       return FALSE;
 | |
| 
 | |
|     if (time(NULL) >= finish_time)
 | |
|       return TRUE;
 | |
| 
 | |
|     /* Sleep for 0.3 sec and check again. */
 | |
| 
 | |
|     my_sleep(300000);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************************
 | |
|   }}}
 | |
| **************************************************************************/
 |