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.cc944
1 files changed, 0 insertions, 944 deletions
diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc
deleted file mode 100644
index 1ad728d40eb..00000000000
--- a/server-tools/instance-manager/instance.cc
+++ /dev/null
@@ -1,944 +0,0 @@
-/* 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+= (uint) 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);
- }
-}
-
-/**************************************************************************
- }}}
-**************************************************************************/