summaryrefslogtreecommitdiff
path: root/server-tools
diff options
context:
space:
mode:
Diffstat (limited to 'server-tools')
-rw-r--r--[-rwxr-xr-x]server-tools/CMakeLists.txt0
-rw-r--r--server-tools/Makefile.am4
-rwxr-xr-xserver-tools/instance-manager/CMakeLists.txt1
-rw-r--r--server-tools/instance-manager/IMService.cpp99
-rw-r--r--server-tools/instance-manager/IMService.h7
-rw-r--r--server-tools/instance-manager/Makefile.am21
-rw-r--r--server-tools/instance-manager/WindowsService.cpp59
-rw-r--r--server-tools/instance-manager/WindowsService.h6
-rw-r--r--server-tools/instance-manager/angel.cc407
-rw-r--r--server-tools/instance-manager/angel.h34
-rw-r--r--server-tools/instance-manager/buffer.cc14
-rw-r--r--server-tools/instance-manager/buffer.h8
-rw-r--r--server-tools/instance-manager/command.cc7
-rw-r--r--server-tools/instance-manager/command.h22
-rw-r--r--server-tools/instance-manager/commands.cc1945
-rw-r--r--server-tools/instance-manager/commands.h362
-rw-r--r--server-tools/instance-manager/exit_codes.h40
-rw-r--r--server-tools/instance-manager/guardian.cc580
-rw-r--r--server-tools/instance-manager/guardian.h132
-rw-r--r--server-tools/instance-manager/instance.cc926
-rw-r--r--server-tools/instance-manager/instance.h253
-rw-r--r--server-tools/instance-manager/instance_map.cc588
-rw-r--r--server-tools/instance-manager/instance_map.h61
-rw-r--r--server-tools/instance-manager/instance_options.cc553
-rw-r--r--server-tools/instance-manager/instance_options.h82
-rw-r--r--server-tools/instance-manager/listener.cc235
-rw-r--r--server-tools/instance-manager/listener.h53
-rw-r--r--server-tools/instance-manager/log.cc120
-rw-r--r--server-tools/instance-manager/log.h38
-rw-r--r--server-tools/instance-manager/manager.cc487
-rw-r--r--server-tools/instance-manager/manager.h52
-rw-r--r--server-tools/instance-manager/messages.cc26
-rw-r--r--server-tools/instance-manager/mysql_connection.cc253
-rw-r--r--server-tools/instance-manager/mysql_connection.h58
-rw-r--r--server-tools/instance-manager/mysql_manager_error.h8
-rw-r--r--server-tools/instance-manager/mysqlmanager.cc391
-rw-r--r--server-tools/instance-manager/options.cc472
-rw-r--r--server-tools/instance-manager/options.h96
-rw-r--r--server-tools/instance-manager/parse.cc370
-rw-r--r--server-tools/instance-manager/parse.h162
-rw-r--r--server-tools/instance-manager/parse_output.cc413
-rw-r--r--server-tools/instance-manager/parse_output.h18
-rw-r--r--server-tools/instance-manager/portability.h19
-rw-r--r--server-tools/instance-manager/priv.cc79
-rw-r--r--server-tools/instance-manager/priv.h37
-rw-r--r--server-tools/instance-manager/protocol.cc44
-rw-r--r--server-tools/instance-manager/protocol.h12
-rw-r--r--server-tools/instance-manager/thread_registry.cc245
-rw-r--r--server-tools/instance-manager/thread_registry.h64
-rw-r--r--server-tools/instance-manager/user_management_commands.cc421
-rw-r--r--server-tools/instance-manager/user_management_commands.h167
-rw-r--r--server-tools/instance-manager/user_map.cc339
-rw-r--r--server-tools/instance-manager/user_map.h64
53 files changed, 7850 insertions, 3104 deletions
diff --git a/server-tools/CMakeLists.txt b/server-tools/CMakeLists.txt
index 3f02ba88f1d..3f02ba88f1d 100755..100644
--- a/server-tools/CMakeLists.txt
+++ b/server-tools/CMakeLists.txt
diff --git a/server-tools/Makefile.am b/server-tools/Makefile.am
index 77612af843e..96e9d5a946e 100644
--- a/server-tools/Makefile.am
+++ b/server-tools/Makefile.am
@@ -13,8 +13,8 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-SUBDIRS= instance-manager
-DIST_SUBDIRS= instance-manager
+SUBDIRS = . instance-manager
+DIST_SUBDIRS = . instance-manager
# Don't update the files from bitkeeper
%::SCCS/s.%
diff --git a/server-tools/instance-manager/CMakeLists.txt b/server-tools/instance-manager/CMakeLists.txt
index 1452cdaf20b..2b9bce56ff7 100755
--- a/server-tools/instance-manager/CMakeLists.txt
+++ b/server-tools/instance-manager/CMakeLists.txt
@@ -25,6 +25,7 @@ ADD_EXECUTABLE(mysqlmanager buffer.cc command.cc commands.cc guardian.cc instanc
instance_options.cc listener.cc log.cc manager.cc messages.cc mysql_connection.cc
mysqlmanager.cc options.cc parse.cc parse_output.cc priv.cc protocol.cc
thread_registry.cc user_map.cc IMService.cpp WindowsService.cpp
+ user_management_commands.cc
../../sql/net_serv.cc ../../sql-common/pack.c ../../sql/password.c
../../sql/sql_state.c ../../sql-common/client.c ../../libmysql/get_password.c
../../libmysql/errmsg.c)
diff --git a/server-tools/instance-manager/IMService.cpp b/server-tools/instance-manager/IMService.cpp
index c2844114873..feccaadbecc 100644
--- a/server-tools/instance-manager/IMService.cpp
+++ b/server-tools/instance-manager/IMService.cpp
@@ -13,19 +13,21 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include <windows.h>
+#include <winsock2.h>
#include <signal.h>
-#include "log.h"
-#include "options.h"
+
#include "IMService.h"
+
+#include "log.h"
#include "manager.h"
+#include "options.h"
+
+static const char * const IM_SVC_USERNAME= NULL;
+static const char * const IM_SVC_PASSWORD= NULL;
IMService::IMService(void)
+ :WindowsService("MySqlManager", "MySQL Manager")
{
- serviceName= "MySqlManager";
- displayName= "MySQL Manager";
- username= NULL;
- password= NULL;
}
IMService::~IMService(void)
@@ -35,25 +37,24 @@ IMService::~IMService(void)
void IMService::Stop()
{
ReportStatus(SERVICE_STOP_PENDING);
-
- // stop the IM work
+
+ /* stop the IM work */
raise(SIGTERM);
}
void IMService::Run(DWORD argc, LPTSTR *argv)
{
- // report to the SCM that we're about to start
+ /* report to the SCM that we're about to start */
ReportStatus((DWORD)SERVICE_START_PENDING);
- Options o;
- o.load(argc, argv);
-
- // init goes here
+ Options::load(argc, argv);
+
+ /* init goes here */
ReportStatus((DWORD)SERVICE_RUNNING);
- // wait for main loop to terminate
- manager(o);
- o.cleanup();
+ /* wait for main loop to terminate */
+ (void) Manager::main();
+ Options::cleanup();
}
void IMService::Log(const char *msg)
@@ -61,37 +62,63 @@ void IMService::Log(const char *msg)
log_info(msg);
}
-int HandleServiceOptions(Options options)
+int IMService::main()
{
- int ret_val= 0;
-
IMService winService;
- if (options.install_as_service)
+ if (Options::Service::install_as_service)
{
if (winService.IsInstalled())
- log_info("Service is already installed");
- else if (winService.Install())
- log_info("Service installed successfully");
+ {
+ log_info("Service is already installed.");
+ return 1;
+ }
+
+ if (winService.Install(IM_SVC_USERNAME, IM_SVC_PASSWORD))
+ {
+ log_info("Service installed successfully.");
+ return 0;
+ }
else
{
- log_info("Service failed to install");
- ret_val= 1;
+ log_error("Service failed to install.");
+ return 1;
}
}
- else if (options.remove_service)
+
+ if (Options::Service::remove_service)
{
- if (! winService.IsInstalled())
- log_info("Service is not installed");
- else if (winService.Remove())
- log_info("Service removed successfully");
+ if (!winService.IsInstalled())
+ {
+ log_info("Service is not installed.");
+ return 1;
+ }
+
+ if (winService.Remove())
+ {
+ log_info("Service removed successfully.");
+ return 0;
+ }
else
{
- log_info("Service failed to remove");
- ret_val= 1;
+ log_error("Service failed to remove.");
+ return 1;
}
}
- else
- ret_val= !winService.Init();
- return ret_val;
+
+ log_info("Initializing Instance Manager service...");
+
+ if (!winService.Init())
+ {
+ log_error("Service failed to initialize.");
+
+ fprintf(stderr,
+ "The service should be started by Windows Service Manager.\n"
+ "The MySQL Manager should be started with '--standalone'\n"
+ "to run from command line.");
+
+ return 1;
+ }
+
+ return 0;
}
diff --git a/server-tools/instance-manager/IMService.h b/server-tools/instance-manager/IMService.h
index 5954a8a46af..aceafb2fca6 100644
--- a/server-tools/instance-manager/IMService.h
+++ b/server-tools/instance-manager/IMService.h
@@ -14,11 +14,14 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#pragma once
-#include "windowsservice.h"
+#include "WindowsService.h"
-class IMService : public WindowsService
+class IMService: public WindowsService
{
public:
+ static int main();
+
+private:
IMService(void);
~IMService(void);
diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am
index 1c47033d4cb..19c4ac8de19 100644
--- a/server-tools/instance-manager/Makefile.am
+++ b/server-tools/instance-manager/Makefile.am
@@ -17,8 +17,7 @@ INCLUDES= @ZLIB_INCLUDES@ -I$(top_srcdir)/include \
@openssl_includes@ -I$(top_builddir)/include
DEFS= -DMYSQL_INSTANCE_MANAGER -DMYSQL_SERVER
-EXTRA_DIST = IMService.cpp IMService.h WindowsService.cpp WindowsService.h \
- CMakeLists.txt
+
# As all autoconf variables depend from ${prefix} and being resolved only when
# make is run, we can not put these defines to a header file (e.g. to
# default_options.h, generated from default_options.h.in)
@@ -33,7 +32,7 @@ liboptions_la_CXXFLAGS= $(CXXFLAGS) \
-DDEFAULT_SOCKET_FILE_NAME="/tmp/mysqlmanager.sock" \
-DDEFAULT_PASSWORD_FILE_NAME="/etc/mysqlmanager.passwd" \
-DDEFAULT_MYSQLD_PATH="$(libexecdir)/mysqld$(EXEEXT)" \
- -DDEFAULT_CONFIG_FILE="/etc/my.cnf" \
+ -DDEFAULT_CONFIG_FILE="my.cnf" \
-DPROTOCOL_VERSION=@PROTOCOL_VERSION@
liboptions_la_SOURCES= options.h options.cc priv.h priv.cc
@@ -61,6 +60,8 @@ client_settings.h:
libexec_PROGRAMS= mysqlmanager
+mysqlmanager_CXXFLAGS=
+
mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
manager.h manager.cc log.h log.cc \
thread_registry.h thread_registry.cc \
@@ -76,9 +77,15 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
guardian.cc guardian.h \
parse_output.cc parse_output.h \
mysql_manager_error.h \
- portability.h
-
-mysqlmanager_LDADD= liboptions.la \
+ portability.h \
+ exit_codes.h \
+ user_management_commands.h \
+ user_management_commands.cc \
+ angel.h \
+ angel.cc
+
+mysqlmanager_LDADD= @CLIENT_EXTRA_LDFLAGS@ \
+ liboptions.la \
libnet.a \
$(top_builddir)/vio/libvio.a \
$(top_builddir)/mysys/libmysys.a \
@@ -86,6 +93,8 @@ mysqlmanager_LDADD= liboptions.la \
$(top_builddir)/dbug/libdbug.a \
@openssl_libs@ @yassl_libs@ @ZLIB_LIBS@
+EXTRA_DIST = WindowsService.cpp WindowsService.h IMService.cpp \
+ IMService.h CMakeLists.txt
tags:
ctags -R *.h *.cc
diff --git a/server-tools/instance-manager/WindowsService.cpp b/server-tools/instance-manager/WindowsService.cpp
index 07ef2e6ea39..14795e2225a 100644
--- a/server-tools/instance-manager/WindowsService.cpp
+++ b/server-tools/instance-manager/WindowsService.cpp
@@ -13,20 +13,30 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include "my_global.h"
#include <windows.h>
-#include <assert.h>
-#include ".\windowsservice.h"
+#include "WindowsService.h"
static WindowsService *gService;
-WindowsService::WindowsService(void) :
+WindowsService::WindowsService(const char *p_serviceName,
+ const char *p_displayName) :
statusCheckpoint(0),
- serviceName(NULL),
- inited(false),
+ serviceName(p_serviceName),
+ displayName(p_displayName),
+ inited(FALSE),
dwAcceptedControls(SERVICE_ACCEPT_STOP),
- debugging(false)
+ debugging(FALSE)
{
+ DBUG_ASSERT(serviceName != NULL);
+
+ /* TODO: shouldn't we check displayName too (can it really be NULL)? */
+
+ /* WindowsService is assumed to be singleton. Let's assure this. */
+ DBUG_ASSERT(gService == NULL);
+
gService= this;
+
status.dwServiceType= SERVICE_WIN32_OWN_PROCESS;
status.dwServiceSpecificExitCode= 0;
}
@@ -35,13 +45,14 @@ WindowsService::~WindowsService(void)
{
}
-BOOL WindowsService::Install()
+BOOL WindowsService::Install(const char *username, const char *password)
{
- bool ret_val= false;
+ bool ret_val= FALSE;
SC_HANDLE newService;
SC_HANDLE scm;
- if (IsInstalled()) return true;
+ if (IsInstalled())
+ return TRUE;
// determine the name of the currently executing file
char szFilePath[_MAX_PATH];
@@ -49,7 +60,7 @@ BOOL WindowsService::Install()
// open a connection to the SCM
if (!(scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
- return false;
+ return FALSE;
newService= CreateService(scm, serviceName, displayName,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
@@ -60,7 +71,7 @@ BOOL WindowsService::Install()
if (newService)
{
CloseServiceHandle(newService);
- ret_val= true;
+ ret_val= TRUE;
}
CloseServiceHandle(scm);
@@ -69,36 +80,37 @@ BOOL WindowsService::Install()
BOOL WindowsService::Init()
{
- assert(serviceName != NULL);
+ DBUG_ASSERT(serviceName != NULL);
- if (inited) return true;
+ if (inited)
+ return TRUE;
SERVICE_TABLE_ENTRY stb[] =
{
{ (LPSTR)serviceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{ NULL, NULL }
};
- inited= true;
+ inited= TRUE;
return StartServiceCtrlDispatcher(stb); //register with the Service Manager
}
BOOL WindowsService::Remove()
{
- bool ret_val= false;
+ bool ret_val= FALSE;
- if (! IsInstalled())
- return true;
+ if (!IsInstalled())
+ return TRUE;
// open a connection to the SCM
SC_HANDLE scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE);
- if (! scm)
- return false;
+ if (!scm)
+ return FALSE;
SC_HANDLE service= OpenService(scm, serviceName, DELETE);
if (service)
{
if (DeleteService(service))
- ret_val= true;
+ ret_val= TRUE;
DWORD dw= ::GetLastError();
CloseServiceHandle(service);
}
@@ -131,7 +143,8 @@ void WindowsService::SetAcceptedControls(DWORD acceptedControls)
BOOL WindowsService::ReportStatus(DWORD currentState, DWORD waitHint,
DWORD dwError)
{
- if(debugging) return TRUE;
+ if (debugging)
+ return TRUE;
if(currentState == SERVICE_START_PENDING)
status.dwControlsAccepted= 0;
@@ -204,7 +217,7 @@ void WindowsService::HandleControlCode(DWORD opcode)
void WINAPI WindowsService::ServiceMain(DWORD argc, LPTSTR *argv)
{
- assert(gService != NULL);
+ DBUG_ASSERT(gService != NULL);
// register our service control handler:
gService->RegisterAndRun(argc, argv);
@@ -212,7 +225,7 @@ void WINAPI WindowsService::ServiceMain(DWORD argc, LPTSTR *argv)
void WINAPI WindowsService::ControlHandler(DWORD opcode)
{
- assert(gService != NULL);
+ DBUG_ASSERT(gService != NULL);
return gService->HandleControlCode(opcode);
}
diff --git a/server-tools/instance-manager/WindowsService.h b/server-tools/instance-manager/WindowsService.h
index 033e02ecb7f..02a499e5f0c 100644
--- a/server-tools/instance-manager/WindowsService.h
+++ b/server-tools/instance-manager/WindowsService.h
@@ -21,8 +21,6 @@ protected:
bool inited;
const char *serviceName;
const char *displayName;
- const char *username;
- const char *password;
SERVICE_STATUS_HANDLE statusHandle;
DWORD statusCheckpoint;
SERVICE_STATUS status;
@@ -30,10 +28,10 @@ protected:
bool debugging;
public:
- WindowsService(void);
+ WindowsService(const char *p_serviceName, const char *p_displayName);
~WindowsService(void);
- BOOL Install();
+ BOOL Install(const char *username, const char *password);
BOOL Remove();
BOOL Init();
BOOL IsInstalled();
diff --git a/server-tools/instance-manager/angel.cc b/server-tools/instance-manager/angel.cc
new file mode 100644
index 00000000000..64515c8498c
--- /dev/null
+++ b/server-tools/instance-manager/angel.cc
@@ -0,0 +1,407 @@
+/* Copyright (C) 2003-2006 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 */
+
+#ifndef __WIN__
+
+#include "angel.h"
+
+#include <sys/wait.h>
+/*
+ sys/wait.h is needed for waitpid(). Unfortunately, there is no MySQL
+ include file, that can serve for this. Include it before MySQL system
+ headers so that we can change system defines if needed.
+*/
+
+#include "my_global.h"
+#include "my_alarm.h"
+#include "my_dir.h"
+#include "my_sys.h"
+
+/* Include other IM files. */
+
+#include "log.h"
+#include "manager.h"
+#include "options.h"
+#include "priv.h"
+
+/************************************************************************/
+
+enum { CHILD_OK= 0, CHILD_NEED_RESPAWN, CHILD_EXIT_ANGEL };
+
+static int log_fd;
+
+static volatile sig_atomic_t child_status= CHILD_OK;
+static volatile sig_atomic_t child_exit_code= 0;
+static volatile sig_atomic_t shutdown_request_signo= 0;
+
+
+/************************************************************************/
+/**
+ Open log file.
+
+ @return
+ TRUE on error;
+ FALSE on success.
+*************************************************************************/
+
+static bool open_log_file()
+{
+ log_info("Angel: opening log file '%s'...",
+ (const char *) Options::Daemon::log_file_name);
+
+ log_fd= open(Options::Daemon::log_file_name,
+ O_WRONLY | O_CREAT | O_APPEND | O_NOCTTY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
+ if (log_fd < 0)
+ {
+ log_error("Can not open log file '%s': %s.",
+ (const char *) Options::Daemon::log_file_name,
+ (const char *) strerror(errno));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/************************************************************************/
+/**
+ Detach the process from controlling tty.
+
+ @return
+ TRUE on error;
+ FALSE on success.
+*************************************************************************/
+
+static bool detach_process()
+{
+ /*
+ Become a session leader (the goal is not to have a controlling tty).
+
+ setsid() must succeed because child is guaranteed not to be a process
+ group leader (it belongs to the process group of the parent).
+
+ NOTE: if we now don't have a controlling tty we will not receive
+ tty-related signals - no need to ignore them.
+ */
+
+ if (setsid() < 0)
+ {
+ log_error("setsid() failed: %s.", (const char *) strerror(errno));
+ return -1;
+ }
+
+ /* Close STDIN. */
+
+ log_info("Angel: preparing standard streams.");
+
+ if (close(STDIN_FILENO) < 0)
+ {
+ log_error("Warning: can not close stdin (%s)."
+ "Trying to continue...",
+ (const char *) strerror(errno));
+ }
+
+ /* Dup STDOUT and STDERR to the log file. */
+
+ if (dup2(log_fd, STDOUT_FILENO) < 0 ||
+ dup2(log_fd, STDERR_FILENO) < 0)
+ {
+ log_error("Can not redirect stdout and stderr to the log file: %s.",
+ (const char *) strerror(errno));
+
+ return TRUE;
+ }
+
+ if (log_fd != STDOUT_FILENO && log_fd != STDERR_FILENO)
+ {
+ if (close(log_fd) < 0)
+ {
+ log_error("Can not close original log file handler (%d): %s. "
+ "Trying to continue...",
+ (int) log_fd,
+ (const char *) strerror(errno));
+ }
+ }
+
+ return FALSE;
+}
+
+
+/************************************************************************/
+/**
+ Create PID file.
+
+ @return
+ TRUE on error;
+ FALSE on success.
+*************************************************************************/
+
+static bool create_pid_file()
+{
+ if (create_pid_file(Options::Daemon::angel_pid_file_name, getpid()))
+ {
+ log_error("Angel: can not create pid file (%s).",
+ (const char *) Options::Daemon::angel_pid_file_name);
+
+ return TRUE;
+ }
+
+ log_info("Angel: pid file (%s) created.",
+ (const char *) Options::Daemon::angel_pid_file_name);
+
+ return FALSE;
+}
+
+
+/************************************************************************/
+/**
+ SIGCHLD handler.
+
+ Reap child, analyze child exit code, and set child_status
+ appropriately.
+*************************************************************************/
+
+extern "C" void reap_child(int);
+
+void reap_child(int __attribute__((unused)) signo)
+{
+ /* NOTE: As we have only one child, no need to cycle waitpid(). */
+
+ int exit_code;
+
+ if (waitpid(0, &exit_code, WNOHANG) > 0)
+ {
+ child_exit_code= exit_code;
+ child_status= exit_code ? CHILD_NEED_RESPAWN : CHILD_EXIT_ANGEL;
+ }
+}
+
+
+/************************************************************************/
+/**
+ SIGTERM, SIGHUP, SIGINT handler.
+
+ Set termination status and return.
+*************************************************************************/
+
+extern "C" void terminate(int signo);
+void terminate(int signo)
+{
+ shutdown_request_signo= signo;
+}
+
+
+/************************************************************************/
+/**
+ Angel main loop.
+
+ @return
+ The function returns exit status for global main():
+ 0 -- program completed successfully;
+ !0 -- error occurred.
+*************************************************************************/
+
+static int angel_main_loop()
+{
+ /*
+ Install signal handlers.
+
+ NOTE: Although signal handlers are needed only for parent process
+ (IM-angel), we should install them before fork() in order to avoid race
+ condition (i.e. to be sure, that IM-angel will receive SIGCHLD in any
+ case).
+ */
+
+ sigset_t wait_for_signals_mask;
+
+ struct sigaction sa_chld;
+ struct sigaction sa_term;
+ struct sigaction sa_chld_orig;
+ struct sigaction sa_term_orig;
+ struct sigaction sa_int_orig;
+ struct sigaction sa_hup_orig;
+
+ log_info("Angel: setting necessary signal actions...");
+
+ sigemptyset(&wait_for_signals_mask);
+
+ sigemptyset(&sa_chld.sa_mask);
+ sa_chld.sa_handler= reap_child;
+ sa_chld.sa_flags= SA_NOCLDSTOP;
+
+ sigemptyset(&sa_term.sa_mask);
+ sa_term.sa_handler= terminate;
+ sa_term.sa_flags= 0;
+
+ /* NOTE: sigaction() fails only if arguments are wrong. */
+
+ sigaction(SIGCHLD, &sa_chld, &sa_chld_orig);
+ sigaction(SIGTERM, &sa_term, &sa_term_orig);
+ sigaction(SIGINT, &sa_term, &sa_int_orig);
+ sigaction(SIGHUP, &sa_term, &sa_hup_orig);
+
+ /* The main Angel loop. */
+
+ while (true)
+ {
+ /* Spawn a new Manager. */
+
+ log_info("Angel: forking Manager process...");
+
+ switch (fork()) {
+ case -1:
+ log_error("Angel: can not fork IM-main: %s.",
+ (const char *) strerror(errno));
+
+ return -1;
+
+ case 0:
+ /*
+ We are in child process, which will be IM-main:
+ - Restore default signal actions to let the IM-main work with
+ signals as he wishes;
+ - Call Manager::main();
+ */
+
+ log_info("Angel: Manager process created successfully.");
+
+ /* NOTE: sigaction() fails only if arguments are wrong. */
+
+ sigaction(SIGCHLD, &sa_chld_orig, NULL);
+ sigaction(SIGTERM, &sa_term_orig, NULL);
+ sigaction(SIGINT, &sa_int_orig, NULL);
+ sigaction(SIGHUP, &sa_hup_orig, NULL);
+
+ log_info("Angel: executing Manager...");
+
+ return Manager::main();
+ }
+
+ /* Wait for signals. */
+
+ log_info("Angel: waiting for signals...");
+
+ while (child_status == CHILD_OK && shutdown_request_signo == 0)
+ sigsuspend(&wait_for_signals_mask);
+
+ /* Exit if one of shutdown signals has been caught. */
+
+ if (shutdown_request_signo)
+ {
+ log_info("Angel: received shutdown signal (%d). Exiting...",
+ (int) shutdown_request_signo);
+
+ return 0;
+ }
+
+ /* Manager process died. Respawn it if it was a failure. */
+
+ if (child_status == CHILD_NEED_RESPAWN)
+ {
+ child_status= CHILD_OK;
+
+ log_error("Angel: Manager exited abnormally (exit code: %d).",
+ (int) child_exit_code);
+
+ log_info("Angel: sleeping 1 second...");
+
+ sleep(1); /* don't respawn too fast */
+
+ log_info("Angel: respawning Manager...");
+
+ continue;
+ }
+
+ /* Delete IM-angel PID file. */
+
+ my_delete(Options::Daemon::angel_pid_file_name, MYF(0));
+
+ /* IM-angel finished. */
+
+ log_info("Angel: Manager exited normally. Exiting...");
+
+ return 0;
+ }
+}
+
+
+/************************************************************************/
+/**
+ Angel main function.
+
+ @return
+ The function returns exit status for global main():
+ 0 -- program completed successfully;
+ !0 -- error occurred.
+*************************************************************************/
+
+int Angel::main()
+{
+ log_info("Angel: started.");
+
+ /* Open log file. */
+
+ if (open_log_file())
+ return -1;
+
+ /* Fork a new process. */
+
+ log_info("Angel: daemonizing...");
+
+ switch (fork()) {
+ case -1:
+ /*
+ This is the main Instance Manager process, fork() failed.
+ Log an error and bail out with error code.
+ */
+
+ log_error("fork() failed: %s.", (const char *) strerror(errno));
+ return -1;
+
+ case 0:
+ /* We are in child process. Continue Angel::main() execution. */
+
+ break;
+
+ default:
+ /*
+ We are in the parent process. Return 0 so that parent exits
+ successfully.
+ */
+
+ log_info("Angel: exiting from the original process...");
+
+ return 0;
+ }
+
+ /* Detach child from controlling tty. */
+
+ if (detach_process())
+ return -1;
+
+ /* Create PID file. */
+
+ if (create_pid_file())
+ return -1;
+
+ /* Start Angel main loop. */
+
+ return angel_main_loop();
+}
+
+#endif // __WIN__
diff --git a/server-tools/instance-manager/angel.h b/server-tools/instance-manager/angel.h
new file mode 100644
index 00000000000..db21c250972
--- /dev/null
+++ b/server-tools/instance-manager/angel.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2003-2006 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 */
+
+#ifndef INCLUDES_MYSQL_ANGEL_H
+#define INCLUDES_MYSQL_ANGEL_H
+
+#ifndef __WIN__
+
+#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
+#pragma interface
+#endif
+
+#include <my_global.h>
+
+class Angel
+{
+public:
+ static int main();
+};
+
+#endif // INCLUDES_MYSQL_ANGEL_H
+#endif // __WIN__
diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc
index 57dd1c72f23..f197f42d009 100644
--- a/server-tools/instance-manager/buffer.cc
+++ b/server-tools/instance-manager/buffer.cc
@@ -26,7 +26,7 @@ const uint Buffer::MAX_BUFFER_SIZE= 16777216;
/*
Puts the given string to the buffer.
- SYNOPSYS
+ SYNOPSIS
append()
position start position in the buffer
string string to be put in the buffer
@@ -44,12 +44,12 @@ const uint Buffer::MAX_BUFFER_SIZE= 16777216;
1 - got an error in reserve()
*/
-int Buffer::append(uint position, const char *string, uint len_arg)
+int Buffer::append(size_t position, const char *string, size_t len_arg)
{
if (reserve(position, len_arg))
return 1;
- strnmov(buffer + position, string, len_arg);
+ strnmov((char*) buffer + position, string, len_arg);
return 0;
}
@@ -58,7 +58,7 @@ int Buffer::append(uint position, const char *string, uint len_arg)
Checks whether the current buffer size is ok to put a string of the length
"len_arg" starting from "position" and reallocs it if no.
- SYNOPSYS
+ SYNOPSIS
reserve()
position the number starting byte on the buffer to store a buffer
len_arg the length of the string.
@@ -75,20 +75,20 @@ int Buffer::append(uint position, const char *string, uint len_arg)
1 - realloc error or we have come to the 16Mb barrier
*/
-int Buffer::reserve(uint position, uint len_arg)
+int Buffer::reserve(size_t position, size_t len_arg)
{
if (position + len_arg >= MAX_BUFFER_SIZE)
goto err;
if (position + len_arg >= buffer_size)
{
- buffer= (char*) my_realloc(buffer,
+ buffer= (uchar*) my_realloc(buffer,
min(MAX_BUFFER_SIZE,
max((uint) (buffer_size*1.5),
position + len_arg)), MYF(0));
if (!(buffer))
goto err;
- buffer_size= (uint) (buffer_size*1.5);
+ buffer_size= (size_t) (buffer_size*1.5);
}
return 0;
diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h
index a551dd98a25..3bd7a714437 100644
--- a/server-tools/instance-manager/buffer.h
+++ b/server-tools/instance-manager/buffer.h
@@ -45,7 +45,7 @@ public:
/*
As append() will invokes realloc() anyway, it's ok if malloc returns 0
*/
- if (!(buffer= (char*) my_malloc(buffer_size, MYF(0))))
+ if (!(buffer= (uchar*) my_malloc(buffer_size, MYF(0))))
buffer_size= 0;
}
@@ -55,11 +55,11 @@ public:
}
public:
- char *buffer;
+ uchar *buffer;
int get_size();
int is_error();
- int append(uint position, const char *string, uint len_arg);
- int reserve(uint position, uint len_arg);
+ int append(size_t position, const char *string, size_t len_arg);
+ int reserve(size_t position, size_t len_arg);
};
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H */
diff --git a/server-tools/instance-manager/command.cc b/server-tools/instance-manager/command.cc
index e68698207d1..ba84285ead2 100644
--- a/server-tools/instance-manager/command.cc
+++ b/server-tools/instance-manager/command.cc
@@ -17,11 +17,12 @@
#pragma implementation
#endif
+#include "manager.h"
#include "command.h"
-
-Command::Command(Instance_map *instance_map_arg)
- :instance_map(instance_map_arg)
+Command::Command()
+ :guardian(Manager::get_guardian()),
+ instance_map(Manager::get_instance_map())
{}
Command::~Command()
diff --git a/server-tools/instance-manager/command.h b/server-tools/instance-manager/command.h
index 10f46fae585..25d8c9849e8 100644
--- a/server-tools/instance-manager/command.h
+++ b/server-tools/instance-manager/command.h
@@ -21,10 +21,13 @@
#pragma interface
#endif
-/* Class responsible for allocation of im commands. */
+/* Class responsible for allocation of IM commands. */
+class Guardian;
class Instance_map;
+struct st_net;
+
/*
Command - entry point for any command.
GangOf4: 'Command' design pattern
@@ -33,13 +36,24 @@ class Instance_map;
class Command
{
public:
- Command(Instance_map *instance_map_arg= 0);
+ Command();
virtual ~Command();
- /* method of executing: */
- virtual int execute(struct st_net *net, ulong connection_id) = 0;
+ /*
+ This operation incapsulates behaviour of the command.
+
+ SYNOPSIS
+ net The network connection to the client.
+ connection_id Client connection ID
+
+ RETURN
+ 0 On success
+ non 0 On error. Client error code is returned.
+ */
+ virtual int execute(st_net *net, ulong connection_id) = 0;
protected:
+ Guardian *guardian;
Instance_map *instance_map;
};
diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc
index bb3763ce8c5..56bd720b3e9 100644
--- a/server-tools/instance-manager/commands.cc
+++ b/server-tools/instance-manager/commands.cc
@@ -13,781 +13,1740 @@
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 "commands.h"
+#include <my_global.h>
+#include <m_ctype.h>
+#include <mysql.h>
+#include <my_dir.h>
+
+#include "buffer.h"
+#include "guardian.h"
#include "instance_map.h"
+#include "log.h"
+#include "manager.h"
#include "messages.h"
#include "mysqld_error.h"
#include "mysql_manager_error.h"
-#include "protocol.h"
-#include "buffer.h"
#include "options.h"
+#include "priv.h"
+#include "protocol.h"
-#include <m_string.h>
-#include <mysql.h>
-#include <my_dir.h>
+/**************************************************************************
+ {{{ Static functions.
+**************************************************************************/
+/**
+ modify_defaults_to_im_error -- a map of error codes of
+ mysys::modify_defaults_file() into Instance Manager error codes.
+*/
-/*
- Add a string to a buffer
+static const int modify_defaults_to_im_error[]= { 0, ER_OUT_OF_RESOURCES,
+ ER_ACCESS_OPTION_FILE };
- SYNOPSYS
- put_to_buff()
- buff buffer to add the string
- str string to add
- uint offset in the buff to add a string
+
+/**
+ Parse version number from the version string.
+
+ SYNOPSIS
+ parse_version_number()
+ version_str
+ version
+ version_size
DESCRIPTION
+ TODO
- Function to add a string to the buffer. It is different from
- store_to_protocol_packet, which is used in the protocol.cc. The last
- one also stores the length of the string in a special way.
- This is required for MySQL client/server protocol support only.
+ TODO: Move this function to Instance_options and parse version number
+ only once.
- RETURN
- 0 - ok
- 1 - error occured
+ NOTE: This function is used only in SHOW INSTANCE STATUS statement at the
+ moment.
*/
-
-static inline int put_to_buff(Buffer *buff, const char *str, uint *position)
+static int parse_version_number(const char *version_str, char *version,
+ uint version_size)
{
- size_t len= strlen(str);
- if (buff->append(*position, str, (uint) len))
- return 1;
+ const char *start= version_str;
+ const char *end;
+
+ // skip garbage
+ while (!my_isdigit(default_charset_info, *start))
+ start++;
+
+ end= start;
+ // skip digits and dots
+ while (my_isdigit(default_charset_info, *end) || *end == '.')
+ end++;
+
+ if ((uint)(end - start) >= version_size)
+ return -1;
+
+ strncpy(version, start, end-start);
+ version[end-start]= '\0';
- *position+= (uint) len;
return 0;
}
+/**************************************************************************
+ }}}
+**************************************************************************/
+
+/**************************************************************************
+ Implementation of Instance_name.
+**************************************************************************/
+
+Instance_name::Instance_name(const LEX_STRING *name)
+{
+ str.str= str_buffer;
+ str.length= name->length;
-/* implementation for Show_instances: */
+ if (str.length > MAX_INSTANCE_NAME_SIZE - 1)
+ str.length= MAX_INSTANCE_NAME_SIZE - 1;
+ strmake(str.str, name->str, str.length);
+}
-/*
- The method sends a list of instances in the instance map to the client.
+/**************************************************************************
+ Implementation of Show_instances.
+**************************************************************************/
- SYNOPSYS
- Show_instances::execute()
- net The network connection to the client.
- connection_id Client connection ID
+/**
+ Implementation of SHOW INSTANCES statement.
- RETURN
- 0 - ok
- 1 - error occured
+ Possible error codes:
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
-int Show_instances::execute(struct st_net *net, ulong connection_id)
+int Show_instances::execute(st_net *net, ulong /* connection_id */)
{
- Buffer send_buff; /* buffer for packets */
- LIST name, status;
- NAME_WITH_LENGTH name_field, status_field;
+ int err_code;
+
+ if ((err_code= write_header(net)) ||
+ (err_code= write_data(net)))
+ return err_code;
+
+ if (send_eof(net) || net_flush(net))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+int Show_instances::write_header(st_net *net)
+{
+ LIST name, state;
+ LEX_STRING name_field, state_field;
LIST *field_list;
- uint position=0;
- name_field.name= (char*) "instance_name";
+ name_field.str= (char *) "instance_name";
name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field;
- status_field.name= (char*) "status";
- status_field.length= DEFAULT_FIELD_LENGTH;
- status.data= &status_field;
- field_list= list_add(NULL, &status);
+
+ state_field.str= (char *) "state";
+ state_field.length= DEFAULT_FIELD_LENGTH;
+ state.data= &state_field;
+
+ field_list= list_add(NULL, &state);
field_list= list_add(field_list, &name);
- send_fields(net, field_list);
+ return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0;
+}
+
+
+int Show_instances::write_data(st_net *net)
+{
+ my_bool err_status= FALSE;
+ Instance *instance;
+ Instance_map::Iterator iterator(instance_map);
+
+ instance_map->lock();
+
+ while ((instance= iterator.next()))
{
- Instance *instance;
- Instance_map::Iterator iterator(instance_map);
+ Buffer send_buf; /* buffer for packets */
+ size_t pos= 0;
+
+ instance->lock();
+
+ const char *instance_name= instance->options.instance_name.str;
+ const char *state_name= instance->get_state_name();
- instance_map->lock();
- while ((instance= iterator.next()))
+ if (store_to_protocol_packet(&send_buf, instance_name, &pos) ||
+ store_to_protocol_packet(&send_buf, state_name, &pos) ||
+ my_net_write(net, send_buf.buffer, pos))
{
- position= 0;
- store_to_protocol_packet(&send_buff, instance->options.instance_name,
- &position);
- if (instance->is_running())
- store_to_protocol_packet(&send_buff, (char*) "online", &position);
- else
- store_to_protocol_packet(&send_buff, (char*) "offline", &position);
- if (my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
+ err_status= TRUE;
}
- instance_map->unlock();
+
+ instance->unlock();
+
+ if (err_status)
+ break;
}
- if (send_eof(net))
- goto err;
- if (net_flush(net))
- goto err;
- return 0;
-err:
- return ER_OUT_OF_RESOURCES;
+ instance_map->unlock();
+
+ return err_status ? ER_OUT_OF_RESOURCES : 0;
}
-/* implementation for Flush_instances: */
+/**************************************************************************
+ Implementation of Flush_instances.
+**************************************************************************/
+
+/**
+ Implementation of FLUSH INSTANCES statement.
+
+ Possible error codes:
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+ ER_THERE_IS_ACTIVE_INSTACE If there is an active instance
+*/
-int Flush_instances::execute(struct st_net *net, ulong connection_id)
+int Flush_instances::execute(st_net *net, ulong connection_id)
{
- if (instance_map->flush_instances() ||
- net_send_ok(net, connection_id, NULL))
- return ER_OUT_OF_RESOURCES;
+ int err_status= Manager::flush_instances();
- return 0;
+ if (err_status)
+ return err_status;
+
+ return net_send_ok(net, connection_id, NULL) ? ER_OUT_OF_RESOURCES : 0;
}
-/* implementation for Show_instance_status: */
+/**************************************************************************
+ Implementation of Instance_cmd.
+**************************************************************************/
-Show_instance_status::Show_instance_status(Instance_map *instance_map_arg,
- const char *name, uint len)
- :Command(instance_map_arg)
+Instance_cmd::Instance_cmd(const LEX_STRING *instance_name_arg):
+ instance_name(instance_name_arg)
{
+ /*
+ MT-NOTE: we can not make a search for Instance object here,
+ because it can dissappear after releasing the lock.
+ */
+}
+
+
+/**************************************************************************
+ Implementation of Abstract_instance_cmd.
+**************************************************************************/
+
+Abstract_instance_cmd::Abstract_instance_cmd(
+ const LEX_STRING *instance_name_arg)
+ :Instance_cmd(instance_name_arg)
+{
+}
+
+
+int Abstract_instance_cmd::execute(st_net *net, ulong connection_id)
+{
+ int err_code;
Instance *instance;
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
- else
- instance_name= NULL;
+ instance_map->lock();
+
+ instance= instance_map->find(get_instance_name());
+
+ if (!instance)
+ {
+ instance_map->unlock();
+ return ER_BAD_INSTANCE_NAME;
+ }
+
+ instance->lock();
+ instance_map->unlock();
+
+ err_code= execute_impl(net, instance);
+
+ instance->unlock();
+
+ if (!err_code)
+ err_code= send_ok_response(net, connection_id);
+
+ return err_code;
}
-/*
- The method sends a table with a status of requested instance to the client.
+/**************************************************************************
+ Implementation of Show_instance_status.
+**************************************************************************/
- SYNOPSYS
- Show_instance_status::do_command()
- net The network connection to the client.
- instance_name The name of the instance.
+Show_instance_status::Show_instance_status(const LEX_STRING *instance_name_arg)
+ :Abstract_instance_cmd(instance_name_arg)
+{
+}
- RETURN
- 0 - ok
- 1 - error occured
+
+/**
+ Implementation of SHOW INSTANCE STATUS statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
+int Show_instance_status::execute_impl(st_net *net, Instance *instance)
+{
+ int err_code;
+
+ if ((err_code= write_header(net)) ||
+ (err_code= write_data(net, instance)))
+ return err_code;
+
+ return 0;
+}
-int Show_instance_status::execute(struct st_net *net,
- ulong connection_id)
+
+int Show_instance_status::send_ok_response(st_net *net,
+ ulong /* connection_id */)
{
- enum { MAX_VERSION_LENGTH= 40 };
- Buffer send_buff; /* buffer for packets */
- LIST name, status, version;
+ if (send_eof(net) || net_flush(net))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+int Show_instance_status::write_header(st_net *net)
+{
+ LIST name, state, version, version_number, mysqld_compatible;
LIST *field_list;
- NAME_WITH_LENGTH name_field, status_field, version_field;
- uint position=0;
+ LEX_STRING name_field, state_field, version_field,
+ version_number_field, mysqld_compatible_field;
- if (!instance_name)
- return ER_BAD_INSTANCE_NAME;
+ /* Create list of the fileds to be passed to send_fields(). */
- /* create list of the fileds to be passed to send_fields */
- name_field.name= (char*) "instance_name";
+ name_field.str= (char *) "instance_name";
name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field;
- status_field.name= (char*) "status";
- status_field.length= DEFAULT_FIELD_LENGTH;
- status.data= &status_field;
- version_field.name= (char*) "version";
+
+ state_field.str= (char *) "state";
+ state_field.length= DEFAULT_FIELD_LENGTH;
+ state.data= &state_field;
+
+ version_field.str= (char *) "version";
version_field.length= MAX_VERSION_LENGTH;
version.data= &version_field;
- field_list= list_add(NULL, &version);
- field_list= list_add(field_list, &status);
+
+ version_number_field.str= (char *) "version_number";
+ version_number_field.length= MAX_VERSION_LENGTH;
+ version_number.data= &version_number_field;
+
+ mysqld_compatible_field.str= (char *) "mysqld_compatible";
+ mysqld_compatible_field.length= DEFAULT_FIELD_LENGTH;
+ mysqld_compatible.data= &mysqld_compatible_field;
+
+ field_list= list_add(NULL, &mysqld_compatible);
+ field_list= list_add(field_list, &version);
+ field_list= list_add(field_list, &version_number);
+ field_list= list_add(field_list, &state);
field_list= list_add(field_list, &name);
- send_fields(net, field_list);
+ return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0;
+}
+
+
+int Show_instance_status::write_data(st_net *net, Instance *instance)
+{
+ Buffer send_buf; /* buffer for packets */
+ char version_num_buf[MAX_VERSION_LENGTH];
+ size_t pos= 0;
+
+ const char *state_name= instance->get_state_name();
+ const char *version_tag= "unknown";
+ const char *version_num= "unknown";
+ const char *mysqld_compatible_status=
+ instance->is_mysqld_compatible() ? "yes" : "no";
+ if (instance->options.mysqld_version)
{
- Instance *instance;
-
- store_to_protocol_packet(&send_buff, (char*) instance_name, &position);
- if (!(instance= instance_map->find(instance_name, (uint) strlen(instance_name))))
- goto err;
- if (instance->is_running())
- store_to_protocol_packet(&send_buff, (char*) "online", &position);
- else
- store_to_protocol_packet(&send_buff, (char*) "offline", &position);
-
- if (instance->options.mysqld_version)
- store_to_protocol_packet(&send_buff, instance->options.mysqld_version,
- &position);
- else
- store_to_protocol_packet(&send_buff, (char*) "unknown", &position);
-
-
- if (send_buff.is_error() ||
- my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
+ if (parse_version_number(instance->options.mysqld_version, version_num_buf,
+ sizeof(version_num_buf)))
+ return ER_OUT_OF_RESOURCES;
+
+ version_num= version_num_buf;
+ version_tag= instance->options.mysqld_version;
}
- if (send_eof(net) || net_flush(net))
- goto err;
+ if (store_to_protocol_packet(&send_buf, get_instance_name()->str, &pos) ||
+ store_to_protocol_packet(&send_buf, state_name, &pos) ||
+ store_to_protocol_packet(&send_buf, version_num, &pos) ||
+ store_to_protocol_packet(&send_buf, version_tag, &pos) ||
+ store_to_protocol_packet(&send_buf, mysqld_compatible_status, &pos) ||
+ my_net_write(net, send_buf.buffer, pos))
+ {
+ return ER_OUT_OF_RESOURCES;
+ }
return 0;
+}
+
-err:
- return ER_OUT_OF_RESOURCES;
+/**************************************************************************
+ Implementation of Show_instance_options.
+**************************************************************************/
+
+Show_instance_options::Show_instance_options(
+ const LEX_STRING *instance_name_arg)
+ :Abstract_instance_cmd(instance_name_arg)
+{
}
-/* Implementation for Show_instance_options */
+/**
+ Implementation of SHOW INSTANCE OPTIONS statement.
-Show_instance_options::Show_instance_options(Instance_map *instance_map_arg,
- const char *name, uint len):
- Command(instance_map_arg)
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Show_instance_options::execute_impl(st_net *net, Instance *instance)
{
- Instance *instance;
+ int err_code;
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
- else
- instance_name= NULL;
+ if ((err_code= write_header(net)) ||
+ (err_code= write_data(net, instance)))
+ return err_code;
+
+ return 0;
}
-int Show_instance_options::execute(struct st_net *net, ulong connection_id)
+int Show_instance_options::send_ok_response(st_net *net,
+ ulong /* connection_id */)
+{
+ if (send_eof(net) || net_flush(net))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+int Show_instance_options::write_header(st_net *net)
{
- Buffer send_buff; /* buffer for packets */
LIST name, option;
LIST *field_list;
- NAME_WITH_LENGTH name_field, option_field;
- uint position=0;
+ LEX_STRING name_field, option_field;
- if (!instance_name)
- return ER_BAD_INSTANCE_NAME;
+ /* Create list of the fileds to be passed to send_fields(). */
- /* create list of the fileds to be passed to send_fields */
- name_field.name= (char*) "option_name";
+ name_field.str= (char *) "option_name";
name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field;
- option_field.name= (char*) "value";
+
+ option_field.str= (char *) "value";
option_field.length= DEFAULT_FIELD_LENGTH;
option.data= &option_field;
+
field_list= list_add(NULL, &option);
field_list= list_add(field_list, &name);
- send_fields(net, field_list);
+ return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0;
+}
+
+int Show_instance_options::write_data(st_net *net, Instance *instance)
+{
+ Buffer send_buff; /* buffer for packets */
+ size_t pos= 0;
+
+ if (store_to_protocol_packet(&send_buff, "instance_name", &pos) ||
+ store_to_protocol_packet(&send_buff, get_instance_name()->str, &pos) ||
+ my_net_write(net, send_buff.buffer, pos))
{
- Instance *instance;
-
- if (!(instance= instance_map->find(instance_name, (uint) strlen(instance_name))))
- goto err;
- store_to_protocol_packet(&send_buff, (char*) "instance_name", &position);
- store_to_protocol_packet(&send_buff, (char*) instance_name, &position);
- if (my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
- if ((instance->options.mysqld_path))
- {
- position= 0;
- store_to_protocol_packet(&send_buff, (char*) "mysqld-path", &position);
- store_to_protocol_packet(&send_buff,
- (char*) instance->options.mysqld_path,
- &position);
- if (send_buff.is_error() ||
- my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
- }
+ return ER_OUT_OF_RESOURCES;
+ }
+
+ /* Loop through the options. */
+
+ for (int i= 0; i < instance->options.get_num_options(); i++)
+ {
+ Named_value option= instance->options.get_option(i);
+ const char *option_value= option.get_value()[0] ? option.get_value() : "";
+
+ pos= 0;
- if ((instance->options.nonguarded))
+ if (store_to_protocol_packet(&send_buff, option.get_name(), &pos) ||
+ store_to_protocol_packet(&send_buff, option_value, &pos) ||
+ my_net_write(net, send_buff.buffer, pos))
{
- position= 0;
- store_to_protocol_packet(&send_buff, (char*) "nonguarded", &position);
- store_to_protocol_packet(&send_buff, "", &position);
- if (send_buff.is_error() ||
- my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
+ return ER_OUT_OF_RESOURCES;
}
+ }
+
+ return 0;
+}
+
+
+/**************************************************************************
+ Implementation of Start_instance.
+**************************************************************************/
+
+Start_instance::Start_instance(const LEX_STRING *instance_name_arg)
+ :Abstract_instance_cmd(instance_name_arg)
+{
+}
+
- /* loop through the options stored in DYNAMIC_ARRAY */
- for (uint i= 0; i < instance->options.options_array.elements; i++)
+/**
+ Implementation of START INSTANCE statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_INSTANCE_MISCONFIGURED The instance configuration is invalid
+ ER_INSTANCE_ALREADY_STARTED The instance is already started
+ ER_CANNOT_START_INSTANCE The instance could not have been started
+
+ TODO: as soon as this method operates only with Instance, we probably
+ should introduce a new method (execute_stop_instance()) in Instance and
+ just call it from here.
+*/
+
+int Start_instance::execute_impl(st_net * /* net */, Instance *instance)
+{
+ if (!instance->is_configured())
+ return ER_INSTANCE_MISCONFIGURED;
+
+ if (instance->is_active())
+ return ER_INSTANCE_ALREADY_STARTED;
+
+ if (instance->start_mysqld())
+ return ER_CANNOT_START_INSTANCE;
+
+ instance->reset_stat();
+ instance->set_state(Instance::NOT_STARTED);
+
+ return 0;
+}
+
+
+int Start_instance::send_ok_response(st_net *net, ulong connection_id)
+{
+ if (net_send_ok(net, connection_id, "Instance started"))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+/**************************************************************************
+ Implementation of Stop_instance.
+**************************************************************************/
+
+Stop_instance::Stop_instance(const LEX_STRING *instance_name_arg)
+ :Abstract_instance_cmd(instance_name_arg)
+{
+}
+
+
+/**
+ Implementation of STOP INSTANCE statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+
+ TODO: as soon as this method operates only with Instance, we probably
+ should introduce a new method (execute_stop_instance()) in Instance and
+ just call it from here.
+*/
+
+int Stop_instance::execute_impl(st_net * /* net */, Instance *instance)
+{
+ if (!instance->is_active())
+ return ER_INSTANCE_IS_NOT_STARTED;
+
+ instance->set_state(Instance::STOPPED);
+
+ return instance->stop_mysqld() ? ER_STOP_INSTANCE : 0;
+}
+
+
+int Stop_instance::send_ok_response(st_net *net, ulong connection_id)
+{
+ if (net_send_ok(net, connection_id, NULL))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+/**************************************************************************
+ Implementation for Create_instance.
+**************************************************************************/
+
+Create_instance::Create_instance(const LEX_STRING *instance_name_arg)
+ :Instance_cmd(instance_name_arg)
+{
+}
+
+
+/**
+ This operation initializes Create_instance object.
+
+ SYNOPSIS
+ text [IN/OUT] a pointer to the text containing instance options.
+
+ RETURN
+ FALSE On success.
+ TRUE On error.
+*/
+
+bool Create_instance::init(const char **text)
+{
+ return options.init() || parse_args(text);
+}
+
+
+/**
+ This operation parses CREATE INSTANCE options.
+
+ SYNOPSIS
+ text [IN/OUT] a pointer to the text containing instance options.
+
+ RETURN
+ FALSE On success.
+ TRUE On syntax error.
+*/
+
+bool Create_instance::parse_args(const char **text)
+{
+ size_t len;
+
+ /* Check if we have something (and trim leading spaces). */
+
+ get_word(text, &len, NONSPACE);
+
+ if (len == 0)
+ return FALSE; /* OK: no option. */
+
+ /* Main parsing loop. */
+
+ while (TRUE)
+ {
+ LEX_STRING option_name;
+ char *option_name_str;
+ char *option_value_str= NULL;
+
+ /* Looking for option name. */
+
+ get_word(text, &option_name.length, OPTION_NAME);
+
+ if (option_name.length == 0)
+ return TRUE; /* Syntax error: option name expected. */
+
+ option_name.str= (char *) *text;
+ *text+= option_name.length;
+
+ /* Looking for equal sign. */
+
+ skip_spaces(text);
+
+ if (**text == '=')
{
- char *tmp_option, *option_value;
- get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i);
- option_value= strchr(tmp_option, '=');
- /* split the option string into two parts if it has a value */
+ ++(*text); /* Skip an equal sign. */
+
+ /* Looking for option value. */
+
+ skip_spaces(text);
+
+ if (!**text)
+ return TRUE; /* Syntax error: EOS when option value expected. */
- position= 0;
- if (option_value != NULL)
+ if (**text != '\'' && **text != '"')
{
- *option_value= 0;
- store_to_protocol_packet(&send_buff, tmp_option + 2, &position);
- store_to_protocol_packet(&send_buff, option_value + 1, &position);
- /* join name and the value into the same option again */
- *option_value= '=';
+ /* Option value is a simple token. */
+
+ LEX_STRING option_value;
+
+ get_word(text, &option_value.length, ALPHANUM);
+
+ if (option_value.length == 0)
+ return TRUE; /* internal parser error. */
+
+ option_value.str= (char *) *text;
+ *text+= option_value.length;
+
+ if (!(option_value_str= Named_value::alloc_str(&option_value)))
+ return TRUE; /* out of memory during parsing. */
}
else
{
- store_to_protocol_packet(&send_buff, tmp_option + 2, &position);
- store_to_protocol_packet(&send_buff, "", &position);
+ /* Option value is a string. */
+
+ if (parse_option_value(*text, &len, &option_value_str))
+ return TRUE; /* Syntax error: invalid string specification. */
+
+ *text+= len;
}
+ }
- if (send_buff.is_error() ||
- my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
+ if (!option_value_str)
+ {
+ LEX_STRING empty_str= { C_STRING_WITH_LEN("") };
+
+ if (!(option_value_str= Named_value::alloc_str(&empty_str)))
+ return TRUE; /* out of memory during parsing. */
}
+
+ if (!(option_name_str= Named_value::alloc_str(&option_name)))
+ {
+ Named_value::free_str(&option_value_str);
+ return TRUE; /* out of memory during parsing. */
+ }
+
+ {
+ Named_value option(option_name_str, option_value_str);
+
+ if (options.add_element(&option))
+ {
+ option.free();
+ return TRUE; /* out of memory during parsing. */
+ }
+ }
+
+ skip_spaces(text);
+
+ if (!**text)
+ return FALSE; /* OK: end of options. */
+
+ if (**text != ',')
+ return TRUE; /* Syntax error: comma expected. */
+
+ ++(*text);
}
+}
- if (send_eof(net) || net_flush(net))
- goto err;
- return 0;
+/**
+ Implementation of CREATE INSTANCE statement.
+
+ Possible error codes:
+ ER_MALFORMED_INSTANCE_NAME Instance name is malformed
+ ER_CREATE_EXISTING_INSTANCE There is an instance with the given name
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Create_instance::execute(st_net *net, ulong connection_id)
+{
+ int err_code;
+ Instance *instance;
+
+ /* Check that the name is valid and there is no instance with such name. */
+
+ if (!Instance::is_name_valid(get_instance_name()))
+ return ER_MALFORMED_INSTANCE_NAME;
+
+ /*
+ NOTE: In order to prevent race condition, we should perform all operations
+ on under acquired lock.
+ */
+
+ instance_map->lock();
+
+ if (instance_map->find(get_instance_name()))
+ {
+ instance_map->unlock();
+ return ER_CREATE_EXISTING_INSTANCE;
+ }
+
+ if ((err_code= instance_map->create_instance(get_instance_name(), &options)))
+ {
+ instance_map->unlock();
+ return err_code;
+ }
+
+ instance= instance_map->find(get_instance_name());
+ DBUG_ASSERT(instance);
-err:
- return ER_OUT_OF_RESOURCES;
+ if ((err_code= create_instance_in_file(get_instance_name(), &options)))
+ {
+ instance_map->remove_instance(instance); /* instance is deleted here. */
+
+ instance_map->unlock();
+ return err_code;
+ }
+
+ /*
+ CREATE INSTANCE must not lead to start instance, even if it guarded.
+
+ TODO: The problem however is that if Instance Manager restarts after
+ creating instance, the instance will be restarted (see also BUG#19718).
+ */
+
+ instance->set_state(Instance::STOPPED);
+
+ /* That's all. */
+
+ instance_map->unlock();
+
+ /* Send the result. */
+
+ if (net_send_ok(net, connection_id, NULL))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
}
-/* Implementation for Start_instance */
+/**************************************************************************
+ Implementation for Drop_instance.
+**************************************************************************/
-Start_instance::Start_instance(Instance_map *instance_map_arg,
- const char *name, uint len)
- :Command(instance_map_arg)
+Drop_instance::Drop_instance(const LEX_STRING *instance_name_arg)
+ :Instance_cmd(instance_name_arg)
{
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
}
-int Start_instance::execute(struct st_net *net, ulong connection_id)
+/**
+ Implementation of DROP INSTANCE statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_DROP_ACTIVE_INSTANCE The specified instance is active
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Drop_instance::execute(st_net *net, ulong connection_id)
{
- uint err_code;
- if (instance == 0)
- return ER_BAD_INSTANCE_NAME; /* haven't found an instance */
- else
+ int err_code;
+ Instance *instance;
+
+ /* Lock Guardian, then Instance_map. */
+
+ instance_map->lock();
+
+ /* Find an instance. */
+
+ instance= instance_map->find(get_instance_name());
+
+ if (!instance)
+ {
+ instance_map->unlock();
+ return ER_BAD_INSTANCE_NAME;
+ }
+
+ instance->lock();
+
+ /* Check that the instance is offline. */
+
+ if (instance->is_active())
+ {
+ instance->unlock();
+ instance_map->unlock();
+
+ return ER_DROP_ACTIVE_INSTANCE;
+ }
+
+ /* Try to remove instance from the file. */
+
+ err_code= modify_defaults_file(Options::Main::config_file, NULL, NULL,
+ get_instance_name()->str, MY_REMOVE_SECTION);
+ DBUG_ASSERT(err_code >= 0 && err_code <= 2);
+
+ if (err_code)
{
- if ((err_code= instance->start()))
- return err_code;
+ log_error("Can not remove instance '%s' from defaults file (%s). "
+ "Original error code: %d.",
+ (const char *) get_instance_name()->str,
+ (const char *) Options::Main::config_file,
+ (int) err_code);
- if (!(instance->options.nonguarded))
- instance_map->guardian->guard(instance);
+ instance->unlock();
+ instance_map->unlock();
- net_send_ok(net, connection_id, "Instance started");
- return 0;
+ return modify_defaults_to_im_error[err_code];
}
+
+ /* Unlock the instance before destroy. */
+
+ instance->unlock();
+
+ /*
+ Remove instance from the instance map
+ (the instance will be also destroyed here).
+ */
+
+ instance_map->remove_instance(instance);
+
+ /* Unlock the instance map. */
+
+ instance_map->unlock();
+
+ /* That's all: send ok. */
+
+ if (net_send_ok(net, connection_id, "Instance dropped"))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
}
-/* implementation for Show_instance_log: */
+/**************************************************************************
+ Implementation for Show_instance_log.
+**************************************************************************/
-Show_instance_log::Show_instance_log(Instance_map *instance_map_arg,
- const char *name, uint len,
+Show_instance_log::Show_instance_log(const LEX_STRING *instance_name_arg,
Log_type log_type_arg,
- const char *size_arg,
- const char *offset_arg)
- :Command(instance_map_arg)
+ uint size_arg, uint offset_arg)
+ :Abstract_instance_cmd(instance_name_arg),
+ log_type(log_type_arg),
+ size(size_arg),
+ offset(offset_arg)
{
- Instance *instance;
+}
+
+
+/**
+ Implementation of SHOW INSTANCE LOG statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_OFFSET_ERROR We were requested to read negative number of
+ bytes from the log
+ ER_NO_SUCH_LOG The specified type of log is not available for
+ the given instance
+ ER_GUESS_LOGFILE IM wasn't able to figure out the log
+ placement, while it is enabled. Probably user
+ should specify the path to the logfile
+ explicitly.
+ ER_OPEN_LOGFILE Cannot open the logfile
+ ER_READ_FILE Cannot read the logfile
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Show_instance_log::execute_impl(st_net *net, Instance *instance)
+{
+ int err_code;
+
+ if ((err_code= check_params(instance)))
+ return err_code;
- if (offset_arg != NULL)
- offset= atoi(offset_arg);
- else
- offset= 0;
- size= atoi(size_arg);
- log_type= log_type_arg;
+ if ((err_code= write_header(net)) ||
+ (err_code= write_data(net, instance)))
+ return err_code;
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
- else
- instance_name= NULL;
+ return 0;
}
+int Show_instance_log::send_ok_response(st_net *net,
+ ulong /* connection_id */)
+{
+ if (send_eof(net) || net_flush(net))
+ return ER_OUT_OF_RESOURCES;
-/*
- Open the logfile, read requested part of the log and send the info
- to the client.
+ return 0;
+}
- SYNOPSYS
- Show_instance_log::execute()
- net The network connection to the client.
- connection_id Client connection ID
- DESCRIPTION
+int Show_instance_log::check_params(Instance *instance)
+{
+ const char *logpath= instance->options.logs[log_type];
- Send a table with the content of the log requested. The function also
- deals with errro handling, to be verbose.
+ /* Cannot read negative number of bytes. */
- RETURN
- ER_OFFSET_ERROR We were requested to read negative number of bytes
- from the log
- ER_NO_SUCH_LOG The kind log being read is not enabled in the instance
- ER_GUESS_LOGFILE IM wasn't able to figure out the log placement, while
- it is enabled. Probably user should specify the path
- to the logfile explicitly.
- ER_OPEN_LOGFILE Cannot open the logfile
- ER_READ_FILE Cannot read the logfile
- ER_OUT_OF_RESOURCES We weren't able to allocate some resources
-*/
+ if (offset > size)
+ return ER_OFFSET_ERROR;
+
+ /* Instance has no such log. */
+
+ if (logpath == NULL)
+ return ER_NO_SUCH_LOG;
+
+ if (*logpath == '\0')
+ return ER_GUESS_LOGFILE;
-int Show_instance_log::execute(struct st_net *net, ulong connection_id)
+ return 0;
+}
+
+
+int Show_instance_log::write_header(st_net *net)
{
- Buffer send_buff; /* buffer for packets */
LIST name;
LIST *field_list;
- NAME_WITH_LENGTH name_field;
- uint position= 0;
+ LEX_STRING name_field;
- /* create list of the fileds to be passed to send_fields */
- name_field.name= (char*) "Log";
+ /* Create list of the fields to be passed to send_fields(). */
+
+ name_field.str= (char *) "Log";
name_field.length= DEFAULT_FIELD_LENGTH;
- name.data= &name_field;
- field_list= list_add(NULL, &name);
- if (!instance_name)
- return ER_BAD_INSTANCE_NAME;
+ name.data= &name_field;
- /* cannot read negative number of bytes */
- if (offset > size)
- return ER_OFFSET_ERROR;
+ field_list= list_add(NULL, &name);
- send_fields(net, field_list);
+ return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0;
+}
- {
- Instance *instance;
- const char *logpath;
- File fd;
- if ((instance= instance_map->find(instance_name,
- (uint) strlen(instance_name))) == NULL)
- goto err;
+int Show_instance_log::write_data(st_net *net, Instance *instance)
+{
+ Buffer send_buff; /* buffer for packets */
+ size_t pos= 0;
- logpath= instance->options.logs[log_type];
+ const char *logpath= instance->options.logs[log_type];
+ File fd;
- /* Instance has no such log */
- if (logpath == NULL)
- return ER_NO_SUCH_LOG;
+ size_t buff_size;
+ size_t read_len;
- if (*logpath == '\0')
- return ER_GUESS_LOGFILE;
+ MY_STAT file_stat;
+ Buffer read_buff;
+ if ((fd= my_open(logpath, O_RDONLY | O_BINARY, MYF(MY_WME))) <= 0)
+ return ER_OPEN_LOGFILE;
- if ((fd= my_open(logpath, O_RDONLY | O_BINARY, MYF(MY_WME))) >= 0)
- {
- size_t buff_size;
- int read_len;
- /* calculate buffer size */
- MY_STAT file_stat;
- Buffer read_buff;
+ /* my_fstat doesn't use the flag parameter */
+ if (my_fstat(fd, &file_stat, MYF(0)))
+ {
+ close(fd);
+ return ER_OUT_OF_RESOURCES;
+ }
- /* my_fstat doesn't use the flag parameter */
- if (my_fstat(fd, &file_stat, MYF(0)))
- goto err;
+ /* calculate buffer size */
+ buff_size= (size - offset);
- buff_size= (size - offset);
+ read_buff.reserve(0, buff_size);
- read_buff.reserve(0, (uint) buff_size);
+ /* read in one chunk */
+ read_len= (int)my_seek(fd, file_stat.st_size - size, MY_SEEK_SET, MYF(0));
- /* read in one chunk */
- read_len= (int)my_seek(fd, file_stat.st_size - size, MY_SEEK_SET, MYF(0));
+ if ((read_len= my_read(fd, read_buff.buffer, buff_size, MYF(0))) ==
+ MY_FILE_ERROR)
+ {
+ close(fd);
+ return ER_READ_FILE;
+ }
- if ((read_len= my_read(fd, (byte*) read_buff.buffer,
- (uint) buff_size, MYF(0))) < 0)
- return ER_READ_FILE;
- store_to_protocol_packet(&send_buff, read_buff.buffer,
- &position, read_len);
- close(fd);
- }
- else
- return ER_OPEN_LOGFILE;
+ close(fd);
- if (my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
+ if (store_to_protocol_packet(&send_buff, (char*) read_buff.buffer, &pos,
+ read_len) ||
+ my_net_write(net, send_buff.buffer, pos))
+ {
+ return ER_OUT_OF_RESOURCES;
}
- if (send_eof(net) || net_flush(net))
- goto err;
-
return 0;
-
-err:
- return ER_OUT_OF_RESOURCES;
}
-/* implementation for Show_instance_log_files: */
+/**************************************************************************
+ Implementation of Show_instance_log_files.
+**************************************************************************/
Show_instance_log_files::Show_instance_log_files
- (Instance_map *instance_map_arg, const char *name, uint len)
- :Command(instance_map_arg)
+ (const LEX_STRING *instance_name_arg)
+ :Abstract_instance_cmd(instance_name_arg)
{
- Instance *instance;
+}
+
+
+/**
+ Implementation of SHOW INSTANCE LOG FILES statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Show_instance_log_files::execute_impl(st_net *net, Instance *instance)
+{
+ int err_code;
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
- else
- instance_name= NULL;
+ if ((err_code= write_header(net)) ||
+ (err_code= write_data(net, instance)))
+ return err_code;
+
+ return 0;
}
-/*
- The method sends a table with a list of log files
- used by the instance.
+int Show_instance_log_files::send_ok_response(st_net *net,
+ ulong /* connection_id */)
+{
+ if (send_eof(net) || net_flush(net))
+ return ER_OUT_OF_RESOURCES;
- SYNOPSYS
- Show_instance_log_files::execute()
- net The network connection to the client.
- connection_id The ID of the client connection
+ return 0;
+}
- RETURN
- ER_BAD_INSTANCE_NAME The instance name specified is not valid
- ER_OUT_OF_RESOURCES some error occured
- 0 - ok
-*/
-int Show_instance_log_files::execute(struct st_net *net, ulong connection_id)
+int Show_instance_log_files::write_header(st_net *net)
{
- Buffer send_buff; /* buffer for packets */
LIST name, path, size;
LIST *field_list;
- NAME_WITH_LENGTH name_field, path_field, size_field;
- uint position= 0;
+ LEX_STRING name_field, path_field, size_field;
- if (!instance_name)
- return ER_BAD_INSTANCE_NAME;
+ /* Create list of the fileds to be passed to send_fields(). */
- /* create list of the fileds to be passed to send_fields */
- name_field.name= (char*) "Logfile";
+ name_field.str= (char *) "Logfile";
name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field;
- path_field.name= (char*) "Path";
+
+ path_field.str= (char *) "Path";
path_field.length= DEFAULT_FIELD_LENGTH;
path.data= &path_field;
- size_field.name= (char*) "File size";
+
+ size_field.str= (char *) "File size";
size_field.length= DEFAULT_FIELD_LENGTH;
size.data= &size_field;
+
field_list= list_add(NULL, &size);
field_list= list_add(field_list, &path);
field_list= list_add(field_list, &name);
- send_fields(net, field_list);
+ return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0;
+}
- Instance *instance;
- if ((instance= instance_map->
- find(instance_name, (uint) strlen(instance_name))) == NULL)
- goto err;
+int Show_instance_log_files::write_data(st_net *net, Instance *instance)
+{
+ Buffer send_buff; /* buffer for packets */
+ /*
+ We have alike structure in instance_options.cc. We use such to be able
+ to loop through the options, which we need to handle in some common way.
+ */
+ struct log_files_st
+ {
+ const char *name;
+ const char *value;
+ } logs[]=
+ {
+ {"ERROR LOG", instance->options.logs[IM_LOG_ERROR]},
+ {"GENERAL LOG", instance->options.logs[IM_LOG_GENERAL]},
+ {"SLOW LOG", instance->options.logs[IM_LOG_SLOW]},
+ {NULL, NULL}
+ };
+ struct log_files_st *log_files;
+
+ for (log_files= logs; log_files->name; log_files++)
{
+ if (!log_files->value)
+ continue;
+
+ struct stat file_stat;
/*
- We have alike structure in instance_options.cc. We use such to be able
- to loop through the options, which we need to handle in some common way.
+ Save some more space for the log file names. In fact all
+ we need is strlen("GENERAL_LOG") + 1
*/
- struct log_files_st
- {
- const char *name;
- const char *value;
- } logs[]=
- {
- {"ERROR LOG", instance->options.logs[IM_LOG_ERROR]},
- {"GENERAL LOG", instance->options.logs[IM_LOG_GENERAL]},
- {"SLOW LOG", instance->options.logs[IM_LOG_SLOW]},
- {NULL, NULL}
- };
- struct log_files_st *log_files;
-
- for (log_files= logs; log_files->name; log_files++)
+ enum { LOG_NAME_BUFFER_SIZE= 20 };
+ char buff[LOG_NAME_BUFFER_SIZE];
+
+ size_t pos= 0;
+
+ const char *log_path= "";
+ const char *log_size= "0";
+
+ if (!stat(log_files->value, &file_stat) &&
+ MY_S_ISREG(file_stat.st_mode))
{
- if (log_files->value != NULL)
- {
- struct stat file_stat;
- /*
- Save some more space for the log file names. In fact all
- we need is srtlen("GENERAL_LOG") + 1
- */
- enum { LOG_NAME_BUFFER_SIZE= 20 };
- char buff[LOG_NAME_BUFFER_SIZE];
-
- position= 0;
- /* store the type of the log in the send buffer */
- store_to_protocol_packet(&send_buff, log_files->name, &position);
- if (stat(log_files->value, &file_stat))
- {
- store_to_protocol_packet(&send_buff, "", &position);
- store_to_protocol_packet(&send_buff, (char*) "0", &position);
- }
- else if (MY_S_ISREG(file_stat.st_mode))
- {
- store_to_protocol_packet(&send_buff,
- (char*) log_files->value,
- &position);
- int10_to_str(file_stat.st_size, buff, 10);
- store_to_protocol_packet(&send_buff, (char*) buff, &position);
- }
-
- if (my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
- }
+ int10_to_str(file_stat.st_size, buff, 10);
+
+ log_path= log_files->value;
+ log_size= buff;
}
- }
- if (send_eof(net) || net_flush(net))
- goto err;
+ if (store_to_protocol_packet(&send_buff, log_files->name, &pos) ||
+ store_to_protocol_packet(&send_buff, log_path, &pos) ||
+ store_to_protocol_packet(&send_buff, log_size, &pos) ||
+ my_net_write(net, send_buff.buffer, pos))
+ return ER_OUT_OF_RESOURCES;
+ }
return 0;
-
-err:
- return ER_OUT_OF_RESOURCES;
}
-/* implementation for SET instance_name.option=option_value: */
+/**************************************************************************
+ Implementation of Abstract_option_cmd.
+**************************************************************************/
+
+/**
+ Instance_options_list -- a data class representing a list of options for
+ some instance.
+*/
-Set_option::Set_option(Instance_map *instance_map_arg,
- const char *name, uint len,
- const char *option_arg, uint option_len_arg,
- const char *option_value_arg, uint option_value_len_arg)
- :Command(instance_map_arg)
+class Instance_options_list
{
- Instance *instance;
+public:
+ Instance_options_list(const LEX_STRING *instance_name_arg);
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- {
- instance_name= instance->options.instance_name;
+public:
+ bool init();
- /* add prefix for add_option */
- if ((option_len_arg < MAX_OPTION_LEN - 1) ||
- (option_value_len_arg < MAX_OPTION_LEN - 1))
- {
- strmake(option, option_arg, option_len_arg);
- strmake(option_value, option_value_arg, option_value_len_arg);
- }
- else
- {
- option[0]= 0;
- option_value[0]= 0;
- }
- instance_name_len= len;
- }
- else
+ const LEX_STRING *get_instance_name() const
{
- instance_name= NULL;
- instance_name_len= 0;
+ return instance_name.get_str();
}
+
+public:
+ /*
+ This member is set and used only in Abstract_option_cmd::execute_impl().
+ Normally it is not used (and should not).
+
+ The problem is that construction and execution of commands are made not
+ in one transaction (not under one lock session). So, we can not initialize
+ instance in constructor and use it in execution.
+ */
+ Instance *instance;
+
+ Named_value_arr options;
+
+private:
+ Instance_name instance_name;
+};
+
+
+/**************************************************************************/
+
+Instance_options_list::Instance_options_list(
+ const LEX_STRING *instance_name_arg)
+ :instance(NULL),
+ instance_name(instance_name_arg)
+{
}
-/*
- The method sends a table with a list of log files
- used by the instance.
+bool Instance_options_list::init()
+{
+ return options.init();
+}
- SYNOPSYS
- Set_option::correct_file()
- skip Skip the option, being searched while writing the result file.
- That is, to delete it.
- DESCRIPTION
+/**************************************************************************/
+C_MODE_START
+
+static uchar* get_item_key(const uchar* item, size_t* len,
+ my_bool __attribute__((unused)) t)
+{
+ const Instance_options_list *lst= (const Instance_options_list *) item;
+ *len= lst->get_instance_name()->length;
+ return (uchar *) lst->get_instance_name()->str;
+}
+
+static void delete_item(void *item)
+{
+ delete (Instance_options_list *) item;
+}
+
+C_MODE_END
+
+
+/**************************************************************************/
+
+Abstract_option_cmd::Abstract_option_cmd()
+ :initialized(FALSE)
+{
+}
+
+
+Abstract_option_cmd::~Abstract_option_cmd()
+{
+ if (initialized)
+ hash_free(&instance_options_map);
+}
+
+
+bool Abstract_option_cmd::add_option(const LEX_STRING *instance_name,
+ Named_value *option)
+{
+ Instance_options_list *lst= get_instance_options_list(instance_name);
+
+ if (!lst)
+ return TRUE;
+
+ lst->options.add_element(option);
+
+ return FALSE;
+}
+
+
+bool Abstract_option_cmd::init(const char **text)
+{
+ static const int INITIAL_HASH_SIZE= 16;
+
+ if (hash_init(&instance_options_map, default_charset_info,
+ INITIAL_HASH_SIZE, 0, 0, get_item_key, delete_item, 0))
+ return TRUE;
+
+ if (parse_args(text))
+ return TRUE;
+
+ initialized= TRUE;
+
+ return FALSE;
+}
+
+
+/**
Correct the option file. The "skip" option is used to remove the found
option.
+ SYNOPSIS
+ Abstract_option_cmd::correct_file()
+ skip Skip the option, being searched while writing the result file.
+ That is, to delete it.
+
RETURN
- ER_OUT_OF_RESOURCES out of resources
+ 0 Success
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
ER_ACCESS_OPTION_FILE Cannot access the option file
- 0 - ok
*/
-int Set_option::correct_file(int skip)
+int Abstract_option_cmd::correct_file(Instance *instance, Named_value *option,
+ bool skip)
{
- static const int mysys_to_im_error[]= { 0, ER_OUT_OF_RESOURCES,
- ER_ACCESS_OPTION_FILE };
- int error;
+ int err_code= modify_defaults_file(Options::Main::config_file,
+ option->get_name(),
+ option->get_value(),
+ instance->get_name()->str,
+ skip);
- error= modify_defaults_file(Options::config_file, option,
- option_value, instance_name, skip);
- DBUG_ASSERT(error >= 0 && error <= 2);
+ DBUG_ASSERT(err_code >= 0 && err_code <= 2);
- return mysys_to_im_error[error];
-}
+ if (err_code)
+ {
+ log_error("Can not modify option (%s) in defaults file (%s). "
+ "Original error code: %d.",
+ (const char *) option->get_name(),
+ (const char *) Options::Main::config_file,
+ (int) err_code);
+ }
+ return modify_defaults_to_im_error[err_code];
+}
-/*
- The method sets an option in the the default config file (/etc/my.cnf).
- SYNOPSYS
- Set_option::do_command()
- net The network connection to the client.
+/**
+ Lock Instance Map and call execute_impl().
- RETURN
- 0 - ok
- 1 - error occured
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_INCOMPATIBLE_OPTION The specified option can not be set for
+ mysqld-compatible instance
+ ER_INSTANCE_IS_ACTIVE The specified instance is active
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
-int Set_option::do_command(struct st_net *net)
+int Abstract_option_cmd::execute(st_net *net, ulong connection_id)
{
- int error;
+ int err_code;
- /* we must hold the instance_map mutex while changing config file */
instance_map->lock();
- error= correct_file(FALSE);
+
+ err_code= execute_impl(net, connection_id);
+
instance_map->unlock();
- return error;
+ return err_code;
}
-int Set_option::execute(struct st_net *net, ulong connection_id)
+Instance_options_list *
+Abstract_option_cmd::get_instance_options_list(const LEX_STRING *instance_name)
{
- if (instance_name != NULL)
- {
- int val;
+ Instance_options_list *lst=
+ (Instance_options_list *) hash_search(&instance_options_map,
+ (uchar *) instance_name->str,
+ instance_name->length);
- val= do_command(net);
+ if (!lst)
+ {
+ lst= new Instance_options_list(instance_name);
- if (val == 0)
- net_send_ok(net, connection_id, NULL);
+ if (!lst)
+ return NULL;
- return val;
+ if (lst->init() || my_hash_insert(&instance_options_map, (uchar *) lst))
+ {
+ delete lst;
+ return NULL;
+ }
}
- return ER_BAD_INSTANCE_NAME;
+ return lst;
}
-/* the only function from Unset_option we need to Implement */
+/**
+ Skeleton implementation of option-management command.
-int Unset_option::do_command(struct st_net *net)
+ MT-NOTE: Instance Map is locked before calling this operation.
+*/
+int Abstract_option_cmd::execute_impl(st_net *net, ulong connection_id)
{
- return correct_file(TRUE);
+ int err_code= 0;
+
+ /* Check that all the specified instances exist and are offline. */
+
+ for (uint i= 0; i < instance_options_map.records; ++i)
+ {
+ Instance_options_list *lst=
+ (Instance_options_list *) hash_element(&instance_options_map, i);
+
+ bool instance_is_active;
+
+ lst->instance= instance_map->find(lst->get_instance_name());
+
+ if (!lst->instance)
+ return ER_BAD_INSTANCE_NAME;
+
+ lst->instance->lock();
+ instance_is_active= lst->instance->is_active();
+ lst->instance->unlock();
+
+ if (instance_is_active)
+ return ER_INSTANCE_IS_ACTIVE;
+ }
+
+ /* Perform command-specific (SET/UNSET) actions. */
+
+ for (uint i= 0; i < instance_options_map.records; ++i)
+ {
+ Instance_options_list *lst=
+ (Instance_options_list *) hash_element(&instance_options_map, i);
+
+ lst->instance->lock();
+
+ for (int j= 0; j < lst->options.get_size(); ++j)
+ {
+ Named_value option= lst->options.get_element(j);
+ err_code= process_option(lst->instance, &option);
+
+ if (err_code)
+ break;
+ }
+
+ lst->instance->unlock();
+
+ if (err_code)
+ break;
+ }
+
+ if (err_code == 0)
+ net_send_ok(net, connection_id, NULL);
+
+ return err_code;
}
-/* Implementation for Stop_instance: */
+/**************************************************************************
+ Implementation of Set_option.
+**************************************************************************/
+
+/**
+ This operation parses SET options.
+
+ SYNOPSIS
+ text [IN/OUT] a pointer to the text containing options.
+
+ RETURN
+ FALSE On success.
+ TRUE On syntax error.
+*/
+
+bool Set_option::parse_args(const char **text)
+{
+ size_t len;
+
+ /* Check if we have something (and trim leading spaces). */
+
+ get_word(text, &len, NONSPACE);
+
+ if (len == 0)
+ return TRUE; /* Syntax error: no option. */
+
+ /* Main parsing loop. */
+
+ while (TRUE)
+ {
+ LEX_STRING instance_name;
+ LEX_STRING option_name;
+ char *option_name_str;
+ char *option_value_str= NULL;
+
+ /* Looking for instance name. */
+
+ get_word(text, &instance_name.length, ALPHANUM);
+
+ if (instance_name.length == 0)
+ return TRUE; /* Syntax error: instance name expected. */
+
+ instance_name.str= (char *) *text;
+ *text+= instance_name.length;
+
+ skip_spaces(text);
+
+ /* Check the the delimiter is a dot. */
+
+ if (**text != '.')
+ return TRUE; /* Syntax error: dot expected. */
+
+ ++(*text);
+
+ /* Looking for option name. */
+
+ get_word(text, &option_name.length, OPTION_NAME);
+
+ if (option_name.length == 0)
+ return TRUE; /* Syntax error: option name expected. */
+
+ option_name.str= (char *) *text;
+ *text+= option_name.length;
+
+ /* Looking for equal sign. */
+
+ skip_spaces(text);
+
+ if (**text == '=')
+ {
+ ++(*text); /* Skip an equal sign. */
+
+ /* Looking for option value. */
+
+ skip_spaces(text);
+
+ if (!**text)
+ return TRUE; /* Syntax error: EOS when option value expected. */
+
+ if (**text != '\'' && **text != '"')
+ {
+ /* Option value is a simple token. */
+
+ LEX_STRING option_value;
+
+ get_word(text, &option_value.length, ALPHANUM);
+
+ if (option_value.length == 0)
+ return TRUE; /* internal parser error. */
+
+ option_value.str= (char *) *text;
+ *text+= option_value.length;
+
+ if (!(option_value_str= Named_value::alloc_str(&option_value)))
+ return TRUE; /* out of memory during parsing. */
+ }
+ else
+ {
+ /* Option value is a string. */
+
+ if (parse_option_value(*text, &len, &option_value_str))
+ return TRUE; /* Syntax error: invalid string specification. */
+
+ *text+= len;
+ }
+ }
+
+ if (!option_value_str)
+ {
+ LEX_STRING empty_str= { C_STRING_WITH_LEN("") };
+
+ if (!(option_value_str= Named_value::alloc_str(&empty_str)))
+ return TRUE; /* out of memory during parsing. */
+ }
+
+ if (!(option_name_str= Named_value::alloc_str(&option_name)))
+ {
+ Named_value::free_str(&option_name_str);
+ return TRUE; /* out of memory during parsing. */
+ }
-Stop_instance::Stop_instance(Instance_map *instance_map_arg,
- const char *name, uint len)
- :Command(instance_map_arg)
+ {
+ Named_value option(option_name_str, option_value_str);
+
+ if (add_option(&instance_name, &option))
+ {
+ option.free();
+ return TRUE; /* out of memory during parsing. */
+ }
+ }
+
+ skip_spaces(text);
+
+ if (!**text)
+ return FALSE; /* OK: end of options. */
+
+ if (**text != ',')
+ return TRUE; /* Syntax error: comma expected. */
+
+ ++(*text); /* Skip a comma. */
+ }
+}
+
+
+int Set_option::process_option(Instance *instance, Named_value *option)
{
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
+ /* Check that the option is valid. */
+
+ if (instance->is_mysqld_compatible() &&
+ Instance_options::is_option_im_specific(option->get_name()))
+ {
+ log_error("IM-option (%s) can not be used "
+ "in the configuration of mysqld-compatible instance (%s).",
+ (const char *) option->get_name(),
+ (const char *) instance->get_name()->str);
+ return ER_INCOMPATIBLE_OPTION;
+ }
+
+ /* Update the configuration file. */
+
+ int err_code= correct_file(instance, option, FALSE);
+
+ if (err_code)
+ return err_code;
+
+ /* Update the internal cache. */
+
+ if (instance->options.set_option(option))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
}
-int Stop_instance::execute(struct st_net *net, ulong connection_id)
+/**************************************************************************
+ Implementation of Unset_option.
+**************************************************************************/
+
+/**
+ This operation parses UNSET options.
+
+ SYNOPSIS
+ text [IN/OUT] a pointer to the text containing options.
+
+ RETURN
+ FALSE On success.
+ TRUE On syntax error.
+*/
+
+bool Unset_option::parse_args(const char **text)
{
- uint err_code;
+ size_t len;
+
+ /* Check if we have something (and trim leading spaces). */
+
+ get_word(text, &len, NONSPACE);
+
+ if (len == 0)
+ return TRUE; /* Syntax error: no option. */
+
+ /* Main parsing loop. */
- if (instance == 0)
- return ER_BAD_INSTANCE_NAME; /* haven't found an instance */
+ while (TRUE)
+ {
+ LEX_STRING instance_name;
+ LEX_STRING option_name;
+ char *option_name_str;
+ char *option_value_str;
+
+ /* Looking for instance name. */
+
+ get_word(text, &instance_name.length, ALPHANUM);
+
+ if (instance_name.length == 0)
+ return TRUE; /* Syntax error: instance name expected. */
+
+ instance_name.str= (char *) *text;
+ *text+= instance_name.length;
+
+ skip_spaces(text);
+
+ /* Check the the delimiter is a dot. */
+
+ if (**text != '.')
+ return TRUE; /* Syntax error: dot expected. */
+
+ ++(*text); /* Skip a dot. */
+
+ /* Looking for option name. */
+
+ get_word(text, &option_name.length, OPTION_NAME);
- if (!(instance->options.nonguarded))
- instance_map->guardian->stop_guard(instance);
+ if (option_name.length == 0)
+ return TRUE; /* Syntax error: option name expected. */
+
+ option_name.str= (char *) *text;
+ *text+= option_name.length;
+
+ if (!(option_name_str= Named_value::alloc_str(&option_name)))
+ return TRUE; /* out of memory during parsing. */
+
+ {
+ LEX_STRING empty_str= { C_STRING_WITH_LEN("") };
+
+ if (!(option_value_str= Named_value::alloc_str(&empty_str)))
+ {
+ Named_value::free_str(&option_name_str);
+ return TRUE;
+ }
+ }
+
+ {
+ Named_value option(option_name_str, option_value_str);
- if ((err_code= instance->stop()))
+ if (add_option(&instance_name, &option))
+ {
+ option.free();
+ return TRUE; /* out of memory during parsing. */
+ }
+ }
+
+ skip_spaces(text);
+
+ if (!**text)
+ return FALSE; /* OK: end of options. */
+
+ if (**text != ',')
+ return TRUE; /* Syntax error: comma expected. */
+
+ ++(*text); /* Skip a comma. */
+ }
+}
+
+
+/**
+ Implementation of UNSET statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance name specified is not valid
+ ER_INSTANCE_IS_ACTIVE The specified instance is active
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Unset_option::process_option(Instance *instance, Named_value *option)
+{
+ /* Update the configuration file. */
+
+ int err_code= correct_file(instance, option, TRUE);
+
+ if (err_code)
return err_code;
- net_send_ok(net, connection_id, NULL);
+ /* Update the internal cache. */
+
+ instance->options.unset_option(option->get_name());
+
return 0;
}
-int Syntax_error::execute(struct st_net *net, ulong connection_id)
+/**************************************************************************
+ Implementation of Syntax_error.
+**************************************************************************/
+
+int Syntax_error::execute(st_net * /* net */, ulong /* connection_id */)
{
return ER_SYNTAX_ERROR;
}
diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h
index 2ab31c18b32..5c2b2f9bbb7 100644
--- a/server-tools/instance-manager/commands.h
+++ b/server-tools/instance-manager/commands.h
@@ -15,201 +15,379 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#include <my_global.h>
+#include <my_sys.h>
+#include <m_string.h>
+#include <hash.h>
+
#include "command.h"
#include "instance.h"
#include "parse.h"
-/*
+#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
+#pragma interface
+#endif
+
+
+/**
Print all instances of this instance manager.
- Grammar: SHOW ISTANCES
+ Grammar: SHOW INSTANCES
*/
-class Show_instances : public Command
+class Show_instances: public Command
{
public:
- Show_instances(Instance_map *instance_map_arg): Command(instance_map_arg)
- {}
+ Show_instances()
+ { }
- int execute(struct st_net *net, ulong connection_id);
+public:
+ int execute(st_net *net, ulong connection_id);
+
+private:
+ int write_header(st_net *net);
+ int write_data(st_net *net);
};
-/*
- Reread configuration file and refresh instance map.
+/**
+ Reread configuration file and refresh internal cache.
Grammar: FLUSH INSTANCES
*/
-class Flush_instances : public Command
+class Flush_instances: public Command
+{
+public:
+ Flush_instances()
+ { }
+
+public:
+ int execute(st_net *net, ulong connection_id);
+};
+
+
+/**
+ Base class for Instance-specific commands
+ (commands that operate on one instance).
+
+ Instance_cmd extends Command class by:
+ - an attribute for storing instance name;
+ - code to initialize instance name in constructor;
+ - an accessor to get instance name.
+*/
+
+class Instance_cmd : public Command
+{
+public:
+ Instance_cmd(const LEX_STRING *instance_name_arg);
+
+protected:
+ inline const LEX_STRING *get_instance_name() const
+ {
+ return instance_name.get_str();
+ }
+
+private:
+ Instance_name instance_name;
+};
+
+
+/**
+ Abstract class for Instance-specific commands.
+
+ Abstract_instance_cmd extends Instance_cmd by providing a common
+ framework for writing command-implementations. Basically, the class
+ implements Command::execute() pure virtual function in the following
+ way:
+ - Lock Instance_map;
+ - Get an instance by name. Return an error, if there is no such
+ instance;
+ - Lock the instance;
+ - Unlock Instance_map;
+ - Call execute_impl(), which should be implemented in derived class;
+ - Unlock the instance;
+ - Send response to the client and return error status.
+*/
+
+class Abstract_instance_cmd: public Instance_cmd
{
public:
- Flush_instances(Instance_map *instance_map_arg): Command(instance_map_arg)
- {}
+ Abstract_instance_cmd(const LEX_STRING *instance_name_arg);
+
+public:
+ virtual int execute(st_net *net, ulong connection_id);
+
+protected:
+ /**
+ This operation is intended to contain command-specific implementation.
+
+ MT-NOTE: this operation is called under acquired Instance's lock.
+ */
+ virtual int execute_impl(st_net *net, Instance *instance) = 0;
+
+ /**
+ This operation is invoked on successful return of execute_impl() and is
+ intended to send closing data.
- int execute(struct st_net *net, ulong connection_id);
+ MT-NOTE: this operation is called under released Instance's lock.
+ */
+ virtual int send_ok_response(st_net *net, ulong connection_id) = 0;
};
-/*
+/**
Print status of an instance.
- Grammar: SHOW ISTANCE STATUS <instance_name>
+ Grammar: SHOW INSTANCE STATUS <instance_name>
*/
-class Show_instance_status : public Command
+class Show_instance_status: public Abstract_instance_cmd
{
public:
+ Show_instance_status(const LEX_STRING *instance_name_arg);
- Show_instance_status(Instance_map *instance_map_arg,
- const char *name, uint len);
- int execute(struct st_net *net, ulong connection_id);
- const char *instance_name;
+protected:
+ virtual int execute_impl(st_net *net, Instance *instance);
+ virtual int send_ok_response(st_net *net, ulong connection_id);
+
+private:
+ int write_header(st_net *net);
+ int write_data(st_net *net, Instance *instance);
};
-/*
- Print options if chosen instance.
+/**
+ Print options of chosen instance.
Grammar: SHOW INSTANCE OPTIONS <instance_name>
*/
-class Show_instance_options : public Command
+class Show_instance_options: public Abstract_instance_cmd
{
public:
+ Show_instance_options(const LEX_STRING *instance_name_arg);
- Show_instance_options(Instance_map *instance_map_arg,
- const char *name, uint len);
+protected:
+ virtual int execute_impl(st_net *net, Instance *instance);
+ virtual int send_ok_response(st_net *net, ulong connection_id);
- int execute(struct st_net *net, ulong connection_id);
- const char *instance_name;
+private:
+ int write_header(st_net *net);
+ int write_data(st_net *net, Instance *instance);
};
-/*
+/**
Start an instance.
Grammar: START INSTANCE <instance_name>
*/
-class Start_instance : public Command
+class Start_instance: public Abstract_instance_cmd
{
public:
- Start_instance(Instance_map *instance_map_arg, const char *name, uint len);
+ Start_instance(const LEX_STRING *instance_name_arg);
- int execute(struct st_net *net, ulong connection_id);
- const char *instance_name;
- Instance *instance;
+protected:
+ virtual int execute_impl(st_net *net, Instance *instance);
+ virtual int send_ok_response(st_net *net, ulong connection_id);
};
-/*
+/**
Stop an instance.
Grammar: STOP INSTANCE <instance_name>
*/
-class Stop_instance : public Command
+class Stop_instance: public Abstract_instance_cmd
{
public:
- Stop_instance(Instance_map *instance_map_arg, const char *name, uint len);
+ Stop_instance(const LEX_STRING *instance_name_arg);
- Instance *instance;
- int execute(struct st_net *net, ulong connection_id);
- const char *instance_name;
+protected:
+ virtual int execute_impl(st_net *net, Instance *instance);
+ virtual int send_ok_response(st_net *net, ulong connection_id);
};
-/*
- Print requested part of the log
+/**
+ Create an instance.
+ Grammar: CREATE INSTANCE <instance_name> [<options>]
+*/
+
+class Create_instance: public Instance_cmd
+{
+public:
+ Create_instance(const LEX_STRING *instance_name_arg);
+
+public:
+ bool init(const char **text);
+
+protected:
+ virtual int execute(st_net *net, ulong connection_id);
+
+private:
+ bool parse_args(const char **text);
+
+private:
+ Named_value_arr options;
+};
+
+
+/**
+ Drop an instance.
+ Grammar: DROP INSTANCE <instance_name>
+
+ Operation is permitted only if the instance is stopped. On successful
+ completion the instance section is removed from config file and the instance
+ is removed from the instance map.
+*/
+
+class Drop_instance: public Instance_cmd
+{
+public:
+ Drop_instance(const LEX_STRING *instance_name_arg);
+
+protected:
+ virtual int execute(st_net *net, ulong connection_id);
+};
+
+
+/**
+ Print requested part of the log.
Grammar:
- SHOW <instance_name> log {ERROR | SLOW | GENERAL} size[, offset_from_end]
+ SHOW <instance_name> LOG {ERROR | SLOW | GENERAL} size[, offset_from_end]
*/
-class Show_instance_log : public Command
+class Show_instance_log: public Abstract_instance_cmd
{
public:
+ Show_instance_log(const LEX_STRING *instance_name_arg,
+ Log_type log_type_arg, uint size_arg, uint offset_arg);
+
+protected:
+ virtual int execute_impl(st_net *net, Instance *instance);
+ virtual int send_ok_response(st_net *net, ulong connection_id);
- Show_instance_log(Instance_map *instance_map_arg, const char *name,
- uint len, Log_type log_type_arg, const char *size_arg,
- const char *offset_arg);
- int execute(struct st_net *net, ulong connection_id);
+private:
+ int check_params(Instance *instance);
+ int write_header(st_net *net);
+ int write_data(st_net *net, Instance *instance);
+
+private:
Log_type log_type;
- const char *instance_name;
uint size;
uint offset;
};
-/*
+/**
Shows the list of the log files, used by an instance.
Grammar: SHOW <instance_name> LOG FILES
*/
-class Show_instance_log_files : public Command
+class Show_instance_log_files: public Abstract_instance_cmd
{
public:
+ Show_instance_log_files(const LEX_STRING *instance_name_arg);
+
+protected:
+ virtual int execute_impl(st_net *net, Instance *instance);
+ virtual int send_ok_response(st_net *net, ulong connection_id);
- Show_instance_log_files(Instance_map *instance_map_arg,
- const char *name, uint len);
- int execute(struct st_net *net, ulong connection_id);
- const char *instance_name;
- const char *option;
+private:
+ int write_header(st_net *net);
+ int write_data(st_net *net, Instance *instance);
};
-/*
- Syntax error command. This command is issued if parser reported a syntax
- error. We need it to distinguish the parse error and the situation when
- parser internal error occured. E.g. parsing failed because we hadn't had
- enought memory. In the latter case parse_command() should return an error.
+/**
+ Abstract class for option-management commands.
*/
-class Syntax_error : public Command
+class Instance_options_list;
+
+class Abstract_option_cmd: public Command
{
public:
- Syntax_error() {}
- int execute(struct st_net *net, ulong connection_id);
+ ~Abstract_option_cmd();
+
+public:
+ bool add_option(const LEX_STRING *instance_name, Named_value *option);
+
+public:
+ bool init(const char **text);
+
+ virtual int execute(st_net *net, ulong connection_id);
+
+protected:
+ Abstract_option_cmd();
+
+ int correct_file(Instance *instance, Named_value *option, bool skip);
+
+protected:
+ virtual bool parse_args(const char **text) = 0;
+ virtual int process_option(Instance *instance, Named_value *option) = 0;
+
+private:
+ Instance_options_list *
+ get_instance_options_list(const LEX_STRING *instance_name);
+
+ int execute_impl(st_net *net, ulong connection_id);
+
+private:
+ HASH instance_options_map;
+ bool initialized;
};
-/*
+
+/**
Set an option for the instance.
- Grammar: SET instance_name.option=option_value
+ Grammar: SET instance_name.option[=option_value][, ...]
*/
-class Set_option : public Command
+class Set_option: public Abstract_option_cmd
{
public:
- Set_option(Instance_map *instance_map_arg, const char *name, uint len,
- const char *option_arg, uint option_len,
- const char *option_value_arg, uint option_value_len);
- /*
- the following function is virtual to let Unset_option to use
- */
- virtual int do_command(struct st_net *net);
- int execute(struct st_net *net, ulong connection_id);
+ Set_option()
+ { }
+
protected:
- int correct_file(int skip);
-public:
- const char *instance_name;
- uint instance_name_len;
- /* buffer for the option */
- enum { MAX_OPTION_LEN= 1024 };
- char option[MAX_OPTION_LEN];
- char option_value[MAX_OPTION_LEN];
+ virtual bool parse_args(const char **text);
+ virtual int process_option(Instance *instance, Named_value *option);
};
-/*
- Remove option of the instance from config file
- Grammar: UNSET instance_name.option
+/**
+ Remove option of the instance.
+ Grammar: UNSET instance_name.option[, ...]
*/
-class Unset_option: public Set_option
+class Unset_option: public Abstract_option_cmd
{
public:
- Unset_option(Instance_map *instance_map_arg, const char *name, uint len,
- const char *option_arg, uint option_len,
- const char *option_value_arg, uint option_value_len):
- Set_option(instance_map_arg, name, len, option_arg, option_len,
- option_value_arg, option_value_len)
- {}
- int do_command(struct st_net *net);
+ Unset_option()
+ { }
+
+protected:
+ virtual bool parse_args(const char **text);
+ virtual int process_option(Instance *instance, Named_value *option);
};
+/**
+ Syntax error command.
+
+ This command is issued if parser reported a syntax error. We need it to
+ distinguish between syntax error and internal parser error. E.g. parsing
+ failed because we hadn't had enought memory. In the latter case the parser
+ just returns NULL.
+*/
+
+class Syntax_error: public Command
+{
+public:
+ Syntax_error()
+ { }
+
+public:
+ int execute(st_net *net, ulong connection_id);
+};
+
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H */
diff --git a/server-tools/instance-manager/exit_codes.h b/server-tools/instance-manager/exit_codes.h
new file mode 100644
index 00000000000..1609debd8f9
--- /dev/null
+++ b/server-tools/instance-manager/exit_codes.h
@@ -0,0 +1,40 @@
+#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_EXIT_CODES_H
+#define INCLUDES_MYSQL_INSTANCE_MANAGER_EXIT_CODES_H
+
+/*
+ Copyright (C) 2006 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
+*/
+
+/*
+ This file contains a list of exit codes, which are used when Instance
+ Manager is working in user-management mode.
+*/
+
+const int ERR_OK = 0;
+
+const int ERR_OUT_OF_MEMORY = 1;
+const int ERR_INVALID_USAGE = 2;
+const int ERR_INTERNAL_ERROR = 3;
+const int ERR_IO_ERROR = 4;
+const int ERR_PASSWORD_FILE_CORRUPTED = 5;
+const int ERR_PASSWORD_FILE_DOES_NOT_EXIST = 6;
+
+const int ERR_CAN_NOT_READ_USER_NAME = 10;
+const int ERR_CAN_NOT_READ_PASSWORD = 11;
+const int ERR_USER_ALREADY_EXISTS = 12;
+const int ERR_USER_NOT_FOUND = 13;
+
+#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_EXIT_CODES_H
diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc
index ad5d766ea14..b49b0ec0a00 100644
--- a/server-tools/instance-manager/guardian.cc
+++ b/server-tools/instance-manager/guardian.cc
@@ -19,171 +19,228 @@
#endif
#include "guardian.h"
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
-#include "instance_map.h"
#include "instance.h"
-#include "mysql_manager_error.h"
+#include "instance_map.h"
#include "log.h"
-#include "portability.h"
+#include "mysql_manager_error.h"
+#include "options.h"
-#include <string.h>
-#include <sys/types.h>
-#include <signal.h>
+/*************************************************************************
+ {{{ Constructor & destructor.
+*************************************************************************/
+/**
+ Guardian constructor.
-pthread_handler_t guardian(void *arg)
-{
- Guardian_thread *guardian_thread= (Guardian_thread *) arg;
- guardian_thread->run();
- return 0;
-}
+ SYNOPSIS
+ Guardian()
+ thread_registry_arg
+ instance_map_arg
+
+ DESCRIPTION
+ Nominal contructor intended for assigning references and initialize
+ trivial objects. Real initialization is made by init() method.
+*/
-Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg,
- Instance_map *instance_map_arg,
- uint monitoring_interval_arg) :
- Guardian_thread_args(thread_registry_arg, instance_map_arg,
- monitoring_interval_arg),
- thread_info(pthread_self()), guarded_instances(0)
+Guardian::Guardian(Thread_registry *thread_registry_arg,
+ Instance_map *instance_map_arg)
+ :shutdown_requested(FALSE),
+ stopped(FALSE),
+ thread_registry(thread_registry_arg),
+ instance_map(instance_map_arg)
{
pthread_mutex_init(&LOCK_guardian, 0);
pthread_cond_init(&COND_guardian, 0);
- shutdown_requested= FALSE;
- stopped= FALSE;
- init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
}
-Guardian_thread::~Guardian_thread()
+Guardian::~Guardian()
{
- /* delay guardian destruction to the moment when no one needs it */
- pthread_mutex_lock(&LOCK_guardian);
- free_root(&alloc, MYF(0));
- pthread_mutex_unlock(&LOCK_guardian);
+ /*
+ NOTE: it's necessary to synchronize here, because Guiardian thread can be
+ still alive an hold the mutex (because it is detached and we have no
+ control over it).
+ */
+
+ lock();
+ unlock();
+
pthread_mutex_destroy(&LOCK_guardian);
pthread_cond_destroy(&COND_guardian);
}
+/*************************************************************************
+ }}}
+*************************************************************************/
+
-void Guardian_thread::request_shutdown()
+/**
+ Send request to stop Guardian.
+
+ SYNOPSIS
+ request_shutdown()
+*/
+
+void Guardian::request_shutdown()
{
- pthread_mutex_lock(&LOCK_guardian);
- /* stop instances or just clean up Guardian repository */
stop_instances();
+
+ lock();
shutdown_requested= TRUE;
- pthread_mutex_unlock(&LOCK_guardian);
+ unlock();
+
+ ping();
}
-void Guardian_thread::process_instance(Instance *instance,
- GUARD_NODE *current_node,
- LIST **guarded_instances_arg,
- LIST *node)
+/**
+ Process an instance.
+
+ SYNOPSIS
+ process_instance()
+ instance a pointer to the instance for processing
+
+ MT-NOTE:
+ - the given instance must be locked before calling this operation;
+ - Guardian must be locked before calling this operation.
+*/
+
+void Guardian::process_instance(Instance *instance)
{
- uint waitchild= (uint) Instance::DEFAULT_SHUTDOWN_DELAY;
- /* The amount of times, Guardian attempts to restart an instance */
int restart_retry= 100;
time_t current_time= time(NULL);
- if (current_node->state == STOPPING)
+ if (instance->get_state() == Instance::STOPPING)
{
- /* this brach is executed during shutdown */
- if (instance->options.shutdown_delay_val)
- waitchild= instance->options.shutdown_delay_val;
+ /* This brach is executed during shutdown. */
- /* this returns true if and only if an instance was stopped for sure */
+ /* This returns TRUE if and only if an instance was stopped for sure. */
if (instance->is_crashed())
- *guarded_instances_arg= list_delete(*guarded_instances_arg, node);
- else if ( (uint) (current_time - current_node->last_checked) > waitchild)
{
- instance->kill_instance(SIGKILL);
- /*
- Later we do node= node->next. This is ok, as we are only removing
- the node from the list. The pointer to the next one is still valid.
- */
- *guarded_instances_arg= list_delete(*guarded_instances_arg, node);
+ log_info("Guardian: '%s' stopped.",
+ (const char *) instance->get_name()->str);
+
+ instance->set_state(Instance::STOPPED);
+ }
+ else if ((uint) (current_time - instance->last_checked) >=
+ instance->options.get_shutdown_delay())
+ {
+ log_info("Guardian: '%s' hasn't stopped within %d secs.",
+ (const char *) instance->get_name()->str,
+ (int) instance->options.get_shutdown_delay());
+
+ instance->kill_mysqld(SIGKILL);
+
+ log_info("Guardian: pretend that '%s' is killed.",
+ (const char *) instance->get_name()->str);
+
+ instance->set_state(Instance::STOPPED);
+ }
+ else
+ {
+ log_info("Guardian: waiting for '%s' to stop (%d secs left).",
+ (const char *) instance->get_name()->str,
+ (int) (instance->options.get_shutdown_delay() -
+ current_time + instance->last_checked));
}
return;
}
- if (instance->is_running())
+ if (instance->is_mysqld_running())
{
/* The instance can be contacted on it's port */
/* If STARTING also check that pidfile has been created */
- if (current_node->state == STARTING &&
- current_node->instance->options.get_pid() == 0)
+ if (instance->get_state() == Instance::STARTING &&
+ instance->options.load_pid() == 0)
{
/* Pid file not created yet, don't go to STARTED state yet */
}
- else if (current_node->state != STARTED)
+ else if (instance->get_state() != Instance::STARTED)
{
/* clear status fields */
- log_info("guardian: instance '%s' is running, set state to STARTED.",
- (const char *) instance->options.instance_name);
- current_node->restart_counter= 0;
- current_node->crash_moment= 0;
- current_node->state= STARTED;
+ log_info("Guardian: '%s' is running, set state to STARTED.",
+ (const char *) instance->options.instance_name.str);
+ instance->reset_stat();
+ instance->set_state(Instance::STARTED);
}
}
else
{
- switch (current_node->state) {
- case NOT_STARTED:
- log_info("guardian: starting instance '%s'...",
- (const char *) instance->options.instance_name);
-
- /* NOTE, set state to STARTING _before_ start() is called */
- current_node->state= STARTING;
- instance->start();
- current_node->last_checked= current_time;
- break;
- case STARTED: /* fallthrough */
- case STARTING: /* let the instance start or crash */
- if (instance->is_crashed())
+ switch (instance->get_state()) {
+ case Instance::NOT_STARTED:
+ log_info("Guardian: starting '%s'...",
+ (const char *) instance->options.instance_name.str);
+
+ /* NOTE: set state to STARTING _before_ start() is called. */
+ instance->set_state(Instance::STARTING);
+ instance->last_checked= current_time;
+
+ instance->start_mysqld();
+
+ return;
+
+ case Instance::STARTED: /* fallthrough */
+ case Instance::STARTING: /* let the instance start or crash */
+ if (!instance->is_crashed())
+ return;
+
+ instance->crash_moment= current_time;
+ instance->last_checked= current_time;
+ instance->set_state(Instance::JUST_CRASHED);
+ /* fallthrough -- restart an instance immediately */
+
+ case Instance::JUST_CRASHED:
+ if (current_time - instance->crash_moment <= 2)
{
- current_node->crash_moment= current_time;
- current_node->last_checked= current_time;
- current_node->state= JUST_CRASHED;
- /* fallthrough -- restart an instance immediately */
+ if (instance->is_crashed())
+ {
+ instance->start_mysqld();
+ log_info("Guardian: starting '%s'...",
+ (const char *) instance->options.instance_name.str);
+ }
}
else
- break;
- case JUST_CRASHED:
- if (current_time - current_node->crash_moment <= 2)
+ instance->set_state(Instance::CRASHED);
+
+ return;
+
+ case Instance::CRASHED: /* just regular restarts */
+ if ((ulong) (current_time - instance->last_checked) <=
+ (ulong) Options::Main::monitoring_interval)
+ return;
+
+ if (instance->restart_counter < restart_retry)
{
if (instance->is_crashed())
{
- instance->start();
- log_info("guardian: starting instance '%s'...",
- (const char *) instance->options.instance_name);
+ instance->start_mysqld();
+ instance->last_checked= current_time;
+
+ log_info("Guardian: restarting '%s'...",
+ (const char *) instance->options.instance_name.str);
}
}
else
- current_node->state= CRASHED;
- break;
- case CRASHED: /* just regular restarts */
- if (current_time - current_node->last_checked >
- monitoring_interval)
{
- if ((current_node->restart_counter < restart_retry))
- {
- if (instance->is_crashed())
- {
- instance->start();
- current_node->last_checked= current_time;
- current_node->restart_counter++;
- log_info("guardian: restarting instance '%s'...",
- (const char *) instance->options.instance_name);
- }
- }
- else
- current_node->state= CRASHED_AND_ABANDONED;
+ log_info("Guardian: can not start '%s'. "
+ "Abandoning attempts to (re)start it",
+ (const char *) instance->options.instance_name.str);
+
+ instance->set_state(Instance::CRASHED_AND_ABANDONED);
}
- break;
- case CRASHED_AND_ABANDONED:
- break; /* do nothing */
+
+ return;
+
+ case Instance::CRASHED_AND_ABANDONED:
+ return; /* do nothing */
+
default:
DBUG_ASSERT(0);
}
@@ -191,192 +248,147 @@ void Guardian_thread::process_instance(Instance *instance,
}
-/*
- Run guardian thread
+/**
+ Main function of Guardian thread.
- SYNOPSYS
+ SYNOPSIS
run()
DESCRIPTION
-
- Check for all guarded instances and restart them if needed. If everything
- is fine go and sleep for some time.
+ Check for all guarded instances and restart them if needed.
*/
-void Guardian_thread::run()
+void Guardian::run()
{
- Instance *instance;
- LIST *node;
struct timespec timeout;
- thread_registry.register_thread(&thread_info);
+ log_info("Guardian: started.");
- my_thread_init();
- pthread_mutex_lock(&LOCK_guardian);
+ thread_registry->register_thread(&thread_info);
+
+ /* Loop, until all instances were shut down at the end. */
- /* loop, until all instances were shut down at the end */
- while (!(shutdown_requested && (guarded_instances == NULL)))
+ while (true)
{
- node= guarded_instances;
+ Instance_map::Iterator instances_it(instance_map);
+ Instance *instance;
+ bool all_instances_stopped= TRUE;
+
+ instance_map->lock();
- while (node != NULL)
+ while ((instance= instances_it.next()))
{
- GUARD_NODE *current_node= (GUARD_NODE *) node->data;
- instance= ((GUARD_NODE *) node->data)->instance;
- process_instance(instance, current_node, &guarded_instances, node);
+ instance->lock();
- node= node->next;
- }
- set_timespec(timeout, monitoring_interval);
-
- /* check the loop predicate before sleeping */
- if (!(shutdown_requested && (!(guarded_instances))))
- thread_registry.cond_timedwait(&thread_info, &COND_guardian,
- &LOCK_guardian, &timeout);
- }
+ if (!instance->is_guarded() ||
+ instance->get_state() == Instance::STOPPED)
+ {
+ instance->unlock();
+ continue;
+ }
- stopped= TRUE;
- pthread_mutex_unlock(&LOCK_guardian);
- /* now, when the Guardian is stopped we can stop the IM */
- thread_registry.unregister_thread(&thread_info);
- thread_registry.request_shutdown();
- my_thread_end();
-}
+ process_instance(instance);
+ if (instance->get_state() != Instance::STOPPED)
+ all_instances_stopped= FALSE;
-int Guardian_thread::is_stopped()
-{
- int var;
- pthread_mutex_lock(&LOCK_guardian);
- var= stopped;
- pthread_mutex_unlock(&LOCK_guardian);
- return var;
-}
+ instance->unlock();
+ }
+ instance_map->unlock();
-/*
- Initialize the list of guarded instances: loop through the Instance_map and
- add all of the instances, which don't have 'nonguarded' option specified.
+ lock();
- SYNOPSYS
- Guardian_thread::init()
+ if (shutdown_requested && all_instances_stopped)
+ {
+ log_info("Guardian: all guarded mysqlds stopped.");
- NOTE: One should always lock guardian before calling this routine.
+ stopped= TRUE;
+ unlock();
+ break;
+ }
- RETURN
- 0 - ok
- 1 - error occured
-*/
+ set_timespec(timeout, Options::Main::monitoring_interval);
-int Guardian_thread::init()
-{
- Instance *instance;
- Instance_map::Iterator iterator(instance_map);
+ thread_registry->cond_timedwait(&thread_info, &COND_guardian,
+ &LOCK_guardian, &timeout);
+ unlock();
+ }
- /* clear the list of guarded instances */
- free_root(&alloc, MYF(0));
- init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
- guarded_instances= NULL;
+ log_info("Guardian: stopped.");
- while ((instance= iterator.next()))
- {
- if (!(instance->options.nonguarded))
- if (guard(instance, TRUE)) /* do not lock guardian */
- return 1;
- }
+ /* Now, when the Guardian is stopped we can stop the IM. */
+
+ thread_registry->unregister_thread(&thread_info);
+ thread_registry->request_shutdown();
- return 0;
+ log_info("Guardian: finished.");
}
-/*
- Add instance to the Guardian list
+/**
+ Return the value of stopped flag.
+*/
- SYNOPSYS
- guard()
- instance the instance to be guarded
- nolock whether we prefer do not lock Guardian here,
- but use external locking instead
+bool Guardian::is_stopped()
+{
+ int var;
- DESCRIPTION
+ lock();
+ var= stopped;
+ unlock();
+
+ return var;
+}
- The instance is added to the guarded instances list. Usually guard() is
- called after we start an instance.
- RETURN
- 0 - ok
- 1 - error occured
+/**
+ Wake up Guardian thread.
+
+ MT-NOTE: though usually the mutex associated with condition variable should
+ be acquired before signalling the variable, here this is not needed.
+ Signalling under locked mutex is used to avoid lost signals. In the current
+ logic however locking mutex does not guarantee that the signal will not be
+ lost.
*/
-int Guardian_thread::guard(Instance *instance, bool nolock)
+void Guardian::ping()
{
- LIST *node;
- GUARD_NODE *content;
-
- node= (LIST *) alloc_root(&alloc, sizeof(LIST));
- content= (GUARD_NODE *) alloc_root(&alloc, sizeof(GUARD_NODE));
-
- if ((!(node)) || (!(content)))
- return 1;
- /* we store the pointers to instances from the instance_map's MEM_ROOT */
- content->instance= instance;
- content->restart_counter= 0;
- content->crash_moment= 0;
- content->state= NOT_STARTED;
- node->data= (void*) content;
-
- if (nolock)
- guarded_instances= list_add(guarded_instances, node);
- else
- {
- pthread_mutex_lock(&LOCK_guardian);
- guarded_instances= list_add(guarded_instances, node);
- pthread_mutex_unlock(&LOCK_guardian);
- }
-
- return 0;
+ pthread_cond_signal(&COND_guardian);
}
-/*
- TODO: perhaps it would make sense to create a pool of the LIST nodeents
- and give them upon request. Now we are loosing a bit of memory when
- guarded instance was stopped and then restarted (since we cannot free just
- a piece of the MEM_ROOT).
+/**
+ Prepare list of instances.
+
+ SYNOPSIS
+ init()
+
+ MT-NOTE: Instance Map must be locked before calling the operation.
*/
-int Guardian_thread::stop_guard(Instance *instance)
+void Guardian::init()
{
- LIST *node;
-
- pthread_mutex_lock(&LOCK_guardian);
- node= guarded_instances;
+ Instance *instance;
+ Instance_map::Iterator iterator(instance_map);
- while (node != NULL)
+ while ((instance= iterator.next()))
{
- /*
- We compare only pointers, as we always use pointers from the
- instance_map's MEM_ROOT.
- */
- if (((GUARD_NODE *) node->data)->instance == instance)
- {
- guarded_instances= list_delete(guarded_instances, node);
- pthread_mutex_unlock(&LOCK_guardian);
- return 0;
- }
- else
- node= node->next;
+ instance->lock();
+
+ instance->reset_stat();
+ instance->set_state(Instance::NOT_STARTED);
+
+ instance->unlock();
}
- pthread_mutex_unlock(&LOCK_guardian);
- /* if there is nothing to delete it is also fine */
- return 0;
}
-/*
+
+/**
An internal method which is called at shutdown to unregister instances and
attempt to stop them if requested.
- SYNOPSYS
+ SYNOPSIS
stop_instances()
DESCRIPTION
@@ -385,48 +397,100 @@ int Guardian_thread::stop_guard(Instance *instance)
accordingly.
NOTE
- Guardian object should be locked by the calling function.
+ Guardian object should be locked by the caller.
- RETURN
- 0 - ok
- 1 - error occured
*/
-int Guardian_thread::stop_instances()
+void Guardian::stop_instances()
{
- LIST *node;
- node= guarded_instances;
- while (node != NULL)
+ static const int NUM_STOP_ATTEMPTS = 100;
+
+ Instance_map::Iterator instances_it(instance_map);
+ Instance *instance;
+
+ instance_map->lock();
+
+ while ((instance= instances_it.next()))
{
- GUARD_NODE *current_node= (GUARD_NODE *) node->data;
+ instance->lock();
+
+ if (!instance->is_guarded() ||
+ instance->get_state() == Instance::STOPPED)
+ {
+ instance->unlock();
+ continue;
+ }
+
/*
If instance is running or was running (and now probably hanging),
request stop.
*/
- if (current_node->instance->is_running() ||
- (current_node->state == STARTED))
+
+ if (instance->is_mysqld_running() ||
+ instance->get_state() == Instance::STARTED)
{
- current_node->state= STOPPING;
- current_node->last_checked= time(NULL);
+ instance->set_state(Instance::STOPPING);
+ instance->last_checked= time(NULL);
}
else
- /* otherwise remove it from the list */
- guarded_instances= list_delete(guarded_instances, node);
- /* But try to kill it anyway. Just in case */
- current_node->instance->kill_instance(SIGTERM);
- node= node->next;
+ {
+ /* Otherwise mark it as STOPPED. */
+ instance->set_state(Instance::STOPPED);
+ }
+
+ /* Request mysqld to stop. */
+
+ bool instance_stopped= FALSE;
+
+ for (int cur_attempt= 0; cur_attempt < NUM_STOP_ATTEMPTS; ++cur_attempt)
+ {
+ if (!instance->kill_mysqld(SIGTERM))
+ {
+ instance_stopped= TRUE;
+ break;
+ }
+
+ if (!instance->is_active())
+ {
+ instance_stopped= TRUE;
+ break;
+ }
+
+ /* Sleep for 0.3 sec and check again. */
+
+ my_sleep(300000);
+ }
+
+ /*
+ Abort if we failed to stop mysqld instance. That should not happen,
+ but if it happened, we don't know what to do and prefer to have clear
+ failure with coredump.
+ */
+
+ DBUG_ASSERT(instance_stopped);
+
+ instance->unlock();
}
- return 0;
+
+ instance_map->unlock();
}
-void Guardian_thread::lock()
+/**
+ Lock Guardian.
+*/
+
+void Guardian::lock()
{
pthread_mutex_lock(&LOCK_guardian);
}
-void Guardian_thread::unlock()
+/**
+ Unlock Guardian.
+*/
+
+void Guardian::unlock()
{
pthread_mutex_unlock(&LOCK_guardian);
}
diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h
index 63150de79f4..d78058a6fc5 100644
--- a/server-tools/instance-manager/guardian.h
+++ b/server-tools/instance-manager/guardian.h
@@ -15,12 +15,13 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <my_global.h>
-#include "thread_registry.h"
+#include <my_global.h>
#include <my_sys.h>
#include <my_list.h>
+#include "thread_registry.h"
+
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
@@ -28,95 +29,82 @@
class Instance;
class Instance_map;
class Thread_registry;
-struct GUARD_NODE;
-
-pthread_handler_t guardian(void *arg);
-
-struct Guardian_thread_args
-{
- Thread_registry &thread_registry;
- Instance_map *instance_map;
- int monitoring_interval;
-
- Guardian_thread_args(Thread_registry &thread_registry_arg,
- Instance_map *instance_map_arg,
- uint monitoring_interval_arg) :
- thread_registry(thread_registry_arg),
- instance_map(instance_map_arg),
- monitoring_interval(monitoring_interval_arg)
- {}
-};
-
-/*
+/**
The guardian thread is responsible for monitoring and restarting of guarded
instances.
*/
-class Guardian_thread: public Guardian_thread_args
+class Guardian: public Thread
{
public:
- /* states of an instance */
- enum enum_instance_state { NOT_STARTED= 1, STARTING, STARTED, JUST_CRASHED,
- CRASHED, CRASHED_AND_ABANDONED, STOPPING };
+ Guardian(Thread_registry *thread_registry_arg,
+ Instance_map *instance_map_arg);
+ ~Guardian();
- /*
- The Guardian list node structure. Guardian utilizes it to store
- guarded instances plus some additional info.
- */
+ void init();
- struct GUARD_NODE
- {
- Instance *instance;
- /* state of an instance (i.e. STARTED, CRASHED, etc.) */
- enum_instance_state state;
- /* the amount of attemts to restart instance (cleaned up at success) */
- int restart_counter;
- /* triggered at a crash */
- time_t crash_moment;
- /* General time field. Used to provide timeouts (at shutdown and restart) */
- time_t last_checked;
- };
-
-
- Guardian_thread(Thread_registry &thread_registry_arg,
- Instance_map *instance_map_arg,
- uint monitoring_interval_arg);
- ~Guardian_thread();
- /* Main funtion of the thread */
- void run();
- /* Initialize or refresh the list of guarded instances */
- int init();
- /* Request guardian shutdown. Stop instances if needed */
+public:
void request_shutdown();
- /* Start instance protection */
- int guard(Instance *instance, bool nolock= FALSE);
- /* Stop instance protection */
- int stop_guard(Instance *instance);
- /* Returns true if guardian thread is stopped */
- int is_stopped();
+
+ bool is_stopped();
+
void lock();
void unlock();
-public:
- pthread_cond_t COND_guardian;
+ void ping();
+
+protected:
+ virtual void run();
private:
- /* Prepares Guardian shutdown. Stops instances is needed */
- int stop_instances();
- /* check instance state and act accordingly */
- void process_instance(Instance *instance, GUARD_NODE *current_node,
- LIST **guarded_instances, LIST *elem);
- int stopped;
+ void stop_instances();
+
+ void process_instance(Instance *instance);
private:
+ /*
+ LOCK_guardian protectes the members in this section:
+ - shutdown_requested;
+ - stopped;
+
+ Also, it is used for COND_guardian.
+ */
pthread_mutex_t LOCK_guardian;
- Thread_info thread_info;
- LIST *guarded_instances;
- MEM_ROOT alloc;
- enum { MEM_ROOT_BLOCK_SIZE= 512 };
- /* this variable is set to TRUE when we want to stop Guardian thread */
+
+ /*
+ Guardian's main loop waits on this condition. So, it should be signalled
+ each time, when instance state has been changed and we want Guardian to
+ wake up.
+
+ TODO: Change this to having data-scoped conditions, i.e. conditions,
+ which indicate that some data has been changed.
+ */
+ pthread_cond_t COND_guardian;
+
+ /*
+ This variable is set to TRUE, when Manager thread is shutting down.
+ The flag is used by Guardian thread to understand that it's time to
+ finish.
+ */
bool shutdown_requested;
+
+ /*
+ This flag is set to TRUE on shutdown by Guardian thread, when all guarded
+ mysqlds are stopped.
+
+ The flag is used in the Manager thread to wait for Guardian to stop all
+ mysqlds.
+ */
+ bool stopped;
+
+ Thread_info thread_info;
+ Thread_registry *thread_registry;
+ Instance_map *instance_map;
+
+private:
+ Guardian(const Guardian &);
+ Guardian&operator =(const Guardian &);
};
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */
diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc
index e08bf2f66df..1ad728d40eb 100644
--- a/server-tools/instance-manager/instance.cc
+++ b/server-tools/instance-manager/instance.cc
@@ -17,24 +17,28 @@
#pragma implementation
#endif
-#include "instance.h"
+#include <my_global.h>
+#include <mysql.h>
-#include "mysql_manager_error.h"
-#include "log.h"
-#include "instance_map.h"
-#include "priv.h"
-#include "portability.h"
+#include <signal.h>
#ifndef __WIN__
#include <sys/wait.h>
#endif
-#include <my_sys.h>
-#include <signal.h>
-#include <m_string.h>
-#include <mysql.h>
+
+#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"
-static void start_and_monitor_instance(Instance_options *old_instance_options,
- Instance_map *instance_map);
+/*************************************************************************
+ {{{ Platform-specific functions.
+*************************************************************************/
#ifndef __WIN__
typedef pid_t My_process_info;
@@ -43,24 +47,9 @@ typedef PROCESS_INFORMATION My_process_info;
#endif
/*
- 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.
-*/
-
-pthread_handler_t proxy(void *arg)
-{
- Instance *instance= (Instance *) arg;
- start_and_monitor_instance(&instance->options,
- instance->get_map());
- return 0;
-}
-
-/*
Wait for an instance
- SYNOPSYS
+ SYNOPSIS
wait_process()
pi Pointer to the process information structure
(platform-dependent).
@@ -89,7 +78,8 @@ static int wait_process(My_process_info *pi)
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 (linuxthreads)
+
+ if (Manager::is_linux_threads())
wait(NULL); /* LinuxThreads were detected */
else
waitpid(*pi, NULL, 0);
@@ -117,11 +107,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.
@@ -129,13 +118,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();
@@ -151,19 +140,20 @@ static int start_process(Instance_options *instance_options,
switch (*pi) {
case 0: /* never happens on QNX */
- execv(instance_options->mysqld_path, instance_options->argv);
+ execv(instance_options->mysqld_path.str, instance_options->argv);
/* exec never returns */
exit(1);
case -1:
- log_info("cannot create a new process to start instance '%s'.",
- (const char *) instance_options->instance_name);
- return 1;
+ log_error("Instance '%s': can not start mysqld: fork() failed.",
+ (const char *) instance_options->instance_name.str);
+ 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;
@@ -178,8 +168,8 @@ 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++)
{
@@ -202,228 +192,425 @@ static int start_process(Instance_options *instance_options,
pi); /* Pointer to PROCESS_INFORMATION structure */
delete cmdline;
- return (!result);
+ return !result;
}
#endif
-/*
- Fork child, exec an instance and monitor it.
+#ifdef __WIN__
- SYNOPSYS
- start_and_monitor_instance()
- old_instance_options Pointer to the options of the instance to be
- launched. This info is likely to become obsolete
- when function returns from wait_process()
- instance_map Pointer to the instance_map. We use it to protect
- the instance from deletion, while we are working
- with it.
+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;
- DESCRIPTION
- Fork a child, then exec and monitor it. When the child is dead,
- find appropriate instance (for this purpose we save its name),
- set appropriate flags and wake all threads waiting for instance
- to stop.
+ BOOL bDup= DuplicateHandle(GetCurrentProcess(),
+ hProcess, GetCurrentProcess(), &hProcessDup,
+ PROCESS_ALL_ACCESS, FALSE, 0);
- RETURN
- Function returns no value
+ // 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.
*/
-static void start_and_monitor_instance(Instance_options *old_instance_options,
- Instance_map *instance_map)
+class Instance_monitor: public Thread
{
- enum { MAX_INSTANCE_NAME_LEN= 512 };
- char instance_name_buff[MAX_INSTANCE_NAME_LEN];
- uint instance_name_len;
- Instance *current_instance;
- My_process_info process_info;
+public:
+ Instance_monitor(Instance *instance_arg) :instance(instance_arg) {}
+protected:
+ virtual void run();
+ void start_and_monitor_instance();
+private:
+ Instance *instance;
+};
- /*
- Lock instance map to guarantee that no instances are deleted during
- strmake() and execv() calls.
- */
- instance_map->lock();
+
+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);
/*
- Save the instance name in the case if Instance object we
- are using is destroyed. (E.g. by "FLUSH INSTANCES")
+ 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).
*/
- strmake(instance_name_buff, old_instance_options->instance_name,
- MAX_INSTANCE_NAME_LEN - 1);
- instance_name_len= old_instance_options->instance_name_len;
- log_info("starting instance '%s'...",
- (const char *) instance_name_buff);
+ if (instance->is_guarded())
+ {
+ thread_registry->register_thread(&monitor_thread_info, FALSE);
+ }
+
+ /* Starting mysqld. */
- if (start_process(old_instance_options, &process_info))
+ log_info("Instance '%s': Monitor: starting mysqld...",
+ (const char *) instance->get_name()->str);
+
+ if (start_process(&instance->options, &mysqld_process_info))
{
- instance_map->unlock();
- return; /* error is logged */
+ instance->lock();
+ instance->monitoring_thread_active= FALSE;
+ instance->unlock();
+
+ return;
}
- /* allow users to delete instances */
- instance_map->unlock();
+ /* Waiting for mysqld to die. */
- /* 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);
- current_instance= instance_map->find(instance_name_buff, instance_name_len);
+ wait_process(&mysqld_process_info); /* Don't check for return value. */
- if (current_instance)
- current_instance->set_crash_flag_n_wake_all();
+ log_info("Instance '%s': Monitor: mysqld stopped.",
+ (const char *) instance->get_name()->str);
- return;
-}
+ /* Update instance status. */
+ instance->lock();
-Instance_map *Instance::get_map()
-{
- return instance_map;
-}
+ if (instance->is_guarded())
+ thread_registry->unregister_thread(&monitor_thread_info);
+ instance->crashed= TRUE;
+ instance->monitoring_thread_active= FALSE;
-void Instance::remove_pid()
-{
- 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);
+ log_info("Instance '%s': Monitor: finished.",
+ (const char *) instance->get_name()->str);
+
+ instance->unlock();
+
+ /* Wake up guardian. */
+
+ guardian->ping();
}
+/**************************************************************************
+ }}}
+**************************************************************************/
-/*
- The method starts an instance.
- SYNOPSYS
- start()
+/**************************************************************************
+ {{{ 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
- 0 ok
- ER_CANNOT_START_INSTANCE Cannot start instance
- ER_INSTANCE_ALREADY_STARTED The instance on the specified port/socket
- is already started
+ 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().
*/
-int Instance::start()
+bool Instance::is_name_valid(const LEX_STRING *name)
{
- /* clear crash flag */
- pthread_mutex_lock(&LOCK_instance);
- crashed= 0;
- pthread_mutex_unlock(&LOCK_instance);
-
-
- if (!is_running())
- {
- remove_pid();
+ const char *name_suffix= name->str + DFLT_INSTANCE_NAME.length;
- /*
- No need to monitor this thread in the Thread_registry, as all
- instances are to be stopped during shutdown.
- */
- pthread_t proxy_thd_id;
- pthread_attr_t proxy_thd_attr;
- int rc;
-
- pthread_attr_init(&proxy_thd_attr);
- pthread_attr_setdetachstate(&proxy_thd_attr, PTHREAD_CREATE_DETACHED);
- rc= pthread_create(&proxy_thd_id, &proxy_thd_attr, proxy,
- this);
- pthread_attr_destroy(&proxy_thd_attr);
- if (rc)
- {
- log_error("Instance::start(): pthread_create(proxy) failed");
- return ER_CANNOT_START_INSTANCE;
- }
-
- return 0;
- }
+ if (strncmp(name->str, Instance::DFLT_INSTANCE_NAME.str,
+ Instance::DFLT_INSTANCE_NAME.length) != 0)
+ return FALSE;
- /* the instance is started already */
- return ER_INSTANCE_ALREADY_STARTED;
+ return *name_suffix == 0 || my_isdigit(default_charset_info, *name_suffix);
}
-/*
- The method sets the crash flag and wakes all waiters on
- COND_instance_stopped and COND_guardian
- SYNOPSYS
- set_crash_flag_n_wake_all()
+/**
+ The operation is intended to check if the given instance name is
+ mysqld-compatible or not.
- DESCRIPTION
- The method is called when an instance is crashed or terminated.
- In the former case it might indicate that guardian probably should
- restart it.
+ SYNOPSIS
+ is_mysqld_compatible_name()
+ name name to check
RETURN
- Function returns no value
+ TRUE name is mysqld-compatible
+ FALSE otherwise
+
+ TODO: Move to Instance_name class: Instance_name::is_mysqld_compatible().
*/
-void Instance::set_crash_flag_n_wake_all()
+bool Instance::is_mysqld_compatible_name(const LEX_STRING *name)
{
- /* set instance state to crashed */
- pthread_mutex_lock(&LOCK_instance);
- crashed= 1;
- pthread_mutex_unlock(&LOCK_instance);
+ return strcmp(name->str, DFLT_INSTANCE_NAME.str) == 0;
+}
- /*
- Wake connection threads waiting for an instance to stop. This
- is needed if a user issued command to stop an instance via
- mysql connection. This is not the case if Guardian stop the thread.
- */
- pthread_cond_signal(&COND_instance_stopped);
- /* wake guardian */
- pthread_cond_signal(&instance_map->guardian->COND_guardian);
+
+/**
+ 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. */
}
+/**************************************************************************
+ }}}
+**************************************************************************/
-Instance::Instance(): crashed(0)
+/**************************************************************************
+ {{{ 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);
- pthread_cond_init(&COND_instance_stopped, 0);
}
Instance::~Instance()
{
- pthread_cond_destroy(&COND_instance_stopped);
+ log_info("Instance '%s': destroying...", (const char *) get_name()->str);
+
pthread_mutex_destroy(&LOCK_instance);
}
-int Instance::is_crashed()
+/**
+ 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()
{
- int val;
- pthread_mutex_lock(&LOCK_instance);
- val= crashed;
- pthread_mutex_unlock(&LOCK_instance);
- return val;
+ 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();
}
-bool Instance::is_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= 0;
+ 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_port)
- port= options.mysqld_port_val;
-
if (options.mysqld_socket)
- socket= strchr(options.mysqld_socket, '=') + 1;
+ socket= options.mysqld_socket;
/* no port was specified => instance falled back to default value */
- if (!options.mysqld_port && !options.mysqld_socket)
+ 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,
@@ -435,10 +622,8 @@ bool Instance::is_running()
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);
+ log_error("Instance '%s': was able to log into mysqld.",
+ (const char *) get_name()->str);
return_val= TRUE; /* server is alive */
}
else
@@ -446,163 +631,314 @@ bool Instance::is_running()
sizeof(access_denied_message) - 1));
mysql_close(&mysql);
- pthread_mutex_unlock(&LOCK_instance);
return return_val;
}
-/*
- Stop an instance.
+/**
+ @brief Start mysqld.
- SYNOPSYS
- stop()
+ Reset flags and start Instance Monitor thread, which will start mysqld.
- RETURN:
- 0 ok
- ER_INSTANCE_IS_NOT_STARTED Looks like the instance it is not started
- ER_STOP_INSTANCE mysql_shutdown reported an error
+ @note Instance must be locked before calling the operation.
+
+ @return Error status code
+ @retval FALSE Ok
+ @retval TRUE Could not start instance
*/
-int Instance::stop()
+bool Instance::start_mysqld()
{
- struct timespec timeout;
- uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY;
+ Instance_monitor *instance_monitor;
- if (is_running())
- {
- if (options.shutdown_delay_val)
- waitchild= options.shutdown_delay_val;
+ if (!configured)
+ return TRUE;
- kill_instance(SIGTERM);
+ /*
+ Prepare instance to start Instance Monitor thread.
- /* sleep on condition to wait for SIGCHLD */
- set_timespec(timeout, waitchild);
- if (pthread_mutex_lock(&LOCK_instance))
- return ER_STOP_INSTANCE;
+ 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();
- while (options.get_pid() != 0) /* while server isn't stopped */
- {
- int status;
+ /* Create and start the Instance Monitor thread. */
- status= pthread_cond_timedwait(&COND_instance_stopped,
- &LOCK_instance,
- &timeout);
- if (status == ETIMEDOUT || status == ETIME)
- break;
- }
+ instance_monitor= new Instance_monitor(this);
- pthread_mutex_unlock(&LOCK_instance);
+ if (instance_monitor == NULL || instance_monitor->start(Thread::DETACHED))
+ {
+ delete instance_monitor;
+ monitoring_thread_active= FALSE;
- kill_instance(SIGKILL);
+ log_error("Instance '%s': can not create instance monitor thread.",
+ (const char *) get_name()->str);
- return 0;
+ return TRUE;
}
- return ER_INSTANCE_IS_NOT_STARTED;
+ ++restart_counter;
+
+ /* The Instance Monitor thread will delete itself when it's finished. */
+
+ return FALSE;
}
-#ifdef __WIN__
-BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode)
+/**
+ 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()
{
- DWORD dwTID, dwCode, dwErr= 0;
- HANDLE hProcessDup= INVALID_HANDLE_VALUE;
- HANDLE hRT= NULL;
- HINSTANCE hKernel= GetModuleHandle("Kernel32");
- BOOL bSuccess= FALSE;
+ log_info("Instance '%s': stopping mysqld...",
+ (const char *) get_name()->str);
- BOOL bDup= DuplicateHandle(GetCurrentProcess(),
- hProcess, GetCurrentProcess(), &hProcessDup,
- PROCESS_ALL_ACCESS, FALSE, 0);
+ kill_mysqld(SIGTERM);
- // Detect the special case where the process is
- // already dead...
- if (GetExitCodeProcess((bDup) ? hProcessDup : hProcess, &dwCode) &&
- (dwCode == STILL_ACTIVE))
+ if (!wait_for_stop())
{
- FARPROC pfnExitProc;
+ log_info("Instance '%s': mysqld stopped gracefully.",
+ (const char *) get_name()->str);
+ return FALSE;
+ }
- pfnExitProc= GetProcAddress(hKernel, "ExitProcess");
+ log_info("Instance '%s': mysqld failed to stop gracefully within %d seconds.",
+ (const char *) get_name()->str,
+ (int) options.get_shutdown_delay());
- hRT= CreateRemoteThread((bDup) ? hProcessDup : hProcess, NULL, 0,
- (LPTHREAD_START_ROUTINE)pfnExitProc,
- (PVOID)uExitCode, 0, &dwTID);
+ log_info("Instance'%s': killing mysqld...",
+ (const char *) get_name()->str);
- if (hRT == NULL)
- dwErr= GetLastError();
+ kill_mysqld(SIGKILL);
+
+ if (!wait_for_stop())
+ {
+ log_info("Instance '%s': mysqld has been killed.",
+ (const char *) get_name()->str);
+ return FALSE;
}
- else
- dwErr= ERROR_PROCESS_ABORTED;
- if (hRT)
+ 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)
{
- // Must wait process to terminate to
- // guarantee that it has exited...
- WaitForSingleObject((bDup) ? hProcessDup : hProcess, INFINITE);
+ log_info("Instance '%s': no pid file to send a signal (%d).",
+ (const char *) get_name()->str,
+ (int) signum);
+ return TRUE;
+ }
- CloseHandle(hRT);
- bSuccess= 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;
}
- if (bDup)
- CloseHandle(hProcessDup);
+ /* Kill suceeded */
+ if (signum == SIGKILL) /* really killed instance with SIGKILL */
+ {
+ log_error("Instance '%s': killed.",
+ (const char *) options.instance_name.str);
- if (!bSuccess)
- SetLastError(dwErr);
+ /* After sucessful hard kill the pidfile need to be removed */
+ options.unlink_pidfile();
+ }
- return bSuccess;
+ return FALSE;
}
-int kill(pid_t pid, int signum)
+
+/**
+ Lock instance.
+*/
+
+void Instance::lock()
{
- HANDLE processhandle= ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
- if (signum == SIGTERM)
- ::SafeTerminateProcess(processhandle, 0);
- else
- ::TerminateProcess(processhandle, -1);
- return 0;
+ pthread_mutex_lock(&LOCK_instance);
+}
+
+
+/**
+ Unlock instance.
+*/
+
+void Instance::unlock()
+{
+ pthread_mutex_unlock(&LOCK_instance);
}
-#endif
-void Instance::kill_instance(int signum)
+
+/**
+ 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()
{
- pid_t pid;
- /* if there are no pid, everything seems to be fine */
- if ((pid= options.get_pid()) != 0) /* get pid from pidfile */
+ if (!is_configured())
+ return "misconfigured";
+
+ if (is_guarded())
{
- 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);
- /* After sucessful hard kill the pidfile need to be removed */
- options.unlink_pidfile();
- }
- }
+ /* The instance is managed by Guardian: we can report precise state. */
+
+ return get_instance_state_name(get_state());
}
- return;
+
+ /* The instance is not managed by Guardian: we can report status only. */
+
+ return is_active() ? "online" : "offline";
}
-/*
- We execute this function to initialize instance parameters.
- Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY.
+
+/**
+ 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.
*/
-int Instance::init(const char *name_arg)
+void Instance::reset_stat()
{
- return options.init(name_arg);
+ restart_counter= 0;
+ crash_moment= 0;
+ last_checked= 0;
}
+/**************************************************************************
+ }}}
+**************************************************************************/
+
+
+/**************************************************************************
+ {{{ Instance: implementation of private operations.
+**************************************************************************/
-int Instance::complete_initialization(Instance_map *instance_map_arg,
- const char *mysqld_path,
- uint instance_type)
+/**
+ Remove pid file.
+*/
+
+void Instance::remove_pid()
{
- instance_map= instance_map_arg;
- return options.complete_initialization(mysqld_path, instance_type);
+ 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);
+ }
+}
+
+/**************************************************************************
+ }}}
+**************************************************************************/
diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h
index 21511be546f..aa9c923cba1 100644
--- a/server-tools/instance-manager/instance.h
+++ b/server-tools/instance-manager/instance.h
@@ -16,53 +16,258 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
+#include <m_string.h>
+
#include "instance_options.h"
+#include "priv.h"
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
class Instance_map;
+class Thread_registry;
+
+
+/**
+ Instance_name -- the class represents instance name -- a string of length
+ less than MAX_INSTANCE_NAME_SIZE.
+
+ Generally, this is just a string with self-memory-management and should be
+ eliminated in the future.
+*/
+
+class Instance_name
+{
+public:
+ Instance_name(const LEX_STRING *name);
+
+public:
+ inline const LEX_STRING *get_str() const
+ {
+ return &str;
+ }
+
+ inline const char *get_c_str() const
+ {
+ return str.str;
+ }
+
+ inline uint get_length() const
+ {
+ return str.length;
+ }
+
+private:
+ LEX_STRING str;
+ char str_buffer[MAX_INSTANCE_NAME_SIZE];
+};
+
class Instance
{
public:
- Instance();
+ /* States of an instance. */
+ enum enum_instance_state
+ {
+ STOPPED,
+ NOT_STARTED,
+ STARTING,
+ STARTED,
+ JUST_CRASHED,
+ CRASHED,
+ CRASHED_AND_ABANDONED,
+ STOPPING
+ };
+
+public:
+ /**
+ The constant defines name of the default mysqld-instance ("mysqld").
+ */
+ static const LEX_STRING DFLT_INSTANCE_NAME;
+
+public:
+ static bool is_name_valid(const LEX_STRING *name);
+ static bool is_mysqld_compatible_name(const LEX_STRING *name);
+public:
+ Instance();
~Instance();
- int init(const char *name);
- int complete_initialization(Instance_map *instance_map_arg,
- const char *mysqld_path, uint instance_type);
-
- bool is_running();
- int start();
- int stop();
- /* send a signal to the instance */
- void kill_instance(int signo);
- int is_crashed();
- void set_crash_flag_n_wake_all();
- Instance_map *get_map();
+
+ bool init(const LEX_STRING *name_arg);
+ bool complete_initialization();
+
+public:
+ bool is_active();
+
+ bool is_mysqld_running();
+
+ bool start_mysqld();
+ bool stop_mysqld();
+ bool kill_mysqld(int signo);
+
+ void lock();
+ void unlock();
+
+ const char *get_state_name();
+
+ void reset_stat();
+
+public:
+ /**
+ The operation is intended to check if the instance is mysqld-compatible
+ or not.
+ */
+ inline bool is_mysqld_compatible() const;
+
+ /**
+ The operation is intended to check if the instance is configured properly
+ or not. Misconfigured instances are not managed.
+ */
+ inline bool is_configured() const;
+
+ /**
+ The operation returns TRUE if the instance is guarded and FALSE otherwise.
+ */
+ inline bool is_guarded() const;
+
+ /**
+ The operation returns name of the instance.
+ */
+ inline const LEX_STRING *get_name() const;
+
+ /**
+ The operation returns the current state of the instance.
+
+ NOTE: At the moment should be used only for guarded instances.
+ */
+ inline enum_instance_state get_state() const;
+
+ /**
+ The operation changes the state of the instance.
+
+ NOTE: At the moment should be used only for guarded instances.
+ TODO: Make private.
+ */
+ inline void set_state(enum_instance_state new_state);
+
+ /**
+ The operation returns crashed flag.
+ */
+ inline bool is_crashed();
public:
- enum { DEFAULT_SHUTDOWN_DELAY= 35 };
+ /**
+ This attributes contains instance options.
+
+ TODO: Make private.
+ */
Instance_options options;
private:
- int crashed;
+ /**
+ monitoring_thread_active is TRUE if there is a thread that monitors the
+ corresponding mysqld-process.
+ */
+ bool monitoring_thread_active;
+
+ /**
+ crashed is TRUE when corresponding mysqld-process has been died after
+ start.
+ */
+ bool crashed;
+
+ /**
+ configured is TRUE when the instance is configured and FALSE otherwise.
+ Misconfigured instances are not managed.
+ */
+ bool configured;
+
/*
- Mutex protecting the instance. Currently we use it to avoid the
- double start of the instance. This happens when the instance is starting
- and we issue the start command once more.
+ mysqld_compatible specifies whether the instance is mysqld-compatible
+ or not. Mysqld-compatible instances can contain only mysqld-specific
+ options. At the moment an instance is mysqld-compatible if its name is
+ "mysqld".
+
+ The idea is that [mysqld] section should contain only mysqld-specific
+ options (no Instance Manager-specific options) to be readable by mysqld
+ program.
*/
- pthread_mutex_t LOCK_instance;
+ bool mysqld_compatible;
+
/*
- This condition variable is used to wake threads waiting for instance to
- stop in Instance::stop()
+ Mutex protecting the instance.
*/
- pthread_cond_t COND_instance_stopped;
- Instance_map *instance_map;
+ pthread_mutex_t LOCK_instance;
+
+private:
+ /* Guarded-instance attributes. */
+
+ /* state of an instance (i.e. STARTED, CRASHED, etc.) */
+ enum_instance_state state;
+
+public:
+ /* the amount of attemts to restart instance (cleaned up at success) */
+ int restart_counter;
+
+ /* triggered at a crash */
+ time_t crash_moment;
+
+ /* General time field. Used to provide timeouts (at shutdown and restart) */
+ time_t last_checked;
+
+private:
+ static const char *get_instance_state_name(enum_instance_state state);
+
+private:
+ void remove_pid();
- void remove_pid();
+ bool wait_for_stop();
+
+private:
+ friend class Instance_monitor;
};
+
+inline bool Instance::is_mysqld_compatible() const
+{
+ return mysqld_compatible;
+}
+
+
+inline bool Instance::is_configured() const
+{
+ return configured;
+}
+
+
+inline bool Instance::is_guarded() const
+{
+ return !options.nonguarded;
+}
+
+
+inline const LEX_STRING *Instance::get_name() const
+{
+ return &options.instance_name;
+}
+
+
+inline Instance::enum_instance_state Instance::get_state() const
+{
+ return state;
+}
+
+
+inline void Instance::set_state(enum_instance_state new_state)
+{
+ state= new_state;
+}
+
+
+inline bool Instance::is_crashed()
+{
+ return crashed;
+}
+
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */
diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc
index 571826edd7b..d7328d51cfe 100644
--- a/server-tools/instance-manager/instance_map.cc
+++ b/server-tools/instance-manager/instance_map.cc
@@ -19,45 +19,46 @@
#include "instance_map.h"
+#include <my_global.h>
+#include <m_ctype.h>
+#include <mysql_com.h>
+
#include "buffer.h"
#include "instance.h"
#include "log.h"
+#include "mysqld_error.h"
+#include "mysql_manager_error.h"
#include "options.h"
-
-#include <m_ctype.h>
-#include <mysql_com.h>
-#include <m_string.h>
-
-/*
- Note: As we are going to suppost different types of connections,
- we shouldn't have connection-specific functions. To avoid it we could
- put such functions to the Command-derived class instead.
- The command could be easily constructed for a specific connection if
- we would provide a special factory for each connection.
-*/
+#include "priv.h"
C_MODE_START
-/* Procedure needed for HASH initialization */
+/**
+ HASH-routines: get key of instance for storing in hash.
+*/
-static byte* get_instance_key(const byte* u, uint* len,
- my_bool __attribute__((unused)) t)
+static uchar* get_instance_key(const uchar* u, size_t* len,
+ my_bool __attribute__((unused)) t)
{
const Instance *instance= (const Instance *) u;
- *len= instance->options.instance_name_len;
- return (byte *) instance->options.instance_name;
+ *len= instance->options.instance_name.length;
+ return (uchar *) instance->options.instance_name.str;
}
+/**
+ HASH-routines: cleanup handler.
+*/
+
static void delete_instance(void *u)
{
Instance *instance= (Instance *) u;
delete instance;
}
-/*
- The option handler to pass to the process_default_option_files finction.
+/**
+ The option handler to pass to the process_default_option_files function.
- SYNOPSYS
+ SYNOPSIS
process_option()
ctx Handler context. Here it is an instance_map structure.
group_name The name of the group the option belongs to.
@@ -78,16 +79,60 @@ static void delete_instance(void *u)
static int process_option(void *ctx, const char *group, const char *option)
{
- Instance_map *map= NULL;
+ Instance_map *map= (Instance_map*) ctx;
+ LEX_STRING group_str;
+
+ group_str.str= (char *) group;
+ group_str.length= strlen(group);
- map = (Instance_map*) ctx;
- return map->process_one_option(group, option);
+ return map->process_one_option(&group_str, option);
}
C_MODE_END
-/*
+/**
+ Parse option string.
+
+ SYNOPSIS
+ parse_option()
+ option_str [IN] option string (e.g. "--name=value")
+ option_name_buf [OUT] parsed name of the option.
+ Must be of (MAX_OPTION_LEN + 1) size.
+ option_value_buf [OUT] parsed value of the option.
+ Must be of (MAX_OPTION_LEN + 1) size.
+
+ DESCRIPTION
+ This is an auxiliary function and should not be used externally. It is
+ intended to parse whole option string into option name and option value.
+*/
+
+static void parse_option(const char *option_str,
+ char *option_name_buf,
+ char *option_value_buf)
+{
+ const char *eq_pos;
+ const char *ptr= option_str;
+
+ while (*ptr == '-')
+ ++ptr;
+
+ strmake(option_name_buf, ptr, MAX_OPTION_LEN + 1);
+
+ eq_pos= strchr(ptr, '=');
+ if (eq_pos)
+ {
+ option_name_buf[eq_pos - ptr]= 0;
+ strmake(option_value_buf, eq_pos + 1, MAX_OPTION_LEN + 1);
+ }
+ else
+ {
+ option_value_buf[0]= 0;
+ }
+}
+
+
+/**
Process one option from the configuration file.
SYNOPSIS
@@ -101,180 +146,354 @@ C_MODE_END
process_option(). The caller ensures proper locking
of the instance map object.
*/
+ /*
+ Process a given option and assign it to appropricate instance. This is
+ required for the option handler, passed to my_search_option_files().
+ */
-int Instance_map::process_one_option(const char *group, const char *option)
+int Instance_map::process_one_option(const LEX_STRING *group,
+ const char *option)
{
Instance *instance= NULL;
- static const char prefix[]= { 'm', 'y', 's', 'q', 'l', 'd' };
- if (strncmp(group, prefix, sizeof prefix) == 0 &&
- ((my_isdigit(default_charset_info, group[sizeof prefix]))
- || group[sizeof(prefix)] == '\0'))
+ if (!Instance::is_name_valid(group))
+ {
+ /*
+ Current section name is not a valid instance name.
+ We should skip it w/o error.
+ */
+ return 0;
+ }
+
+ if (!(instance= (Instance *) hash_search(&hash, (uchar *) group->str,
+ group->length)))
+ {
+ if (!(instance= new Instance()))
+ return 1;
+
+ if (instance->init(group) || add_instance(instance))
{
- if (!(instance= (Instance *) hash_search(&hash, (byte *) group,
- (uint) strlen(group))))
- {
- if (!(instance= new Instance))
- goto err;
- if (instance->init(group) || my_hash_insert(&hash, (byte *) instance))
- goto err_instance;
- }
-
- if (instance->options.add_option(option))
- goto err; /* the instance'll be deleted when we destroy the map */
+ delete instance;
+ return 1;
}
- return 0;
+ if (instance->is_mysqld_compatible())
+ log_info("Warning: instance name '%s' is mysqld-compatible.",
+ (const char *) group->str);
-err_instance:
- delete instance;
-err:
- return 1;
+ log_info("mysqld instance '%s' has been added successfully.",
+ (const char *) group->str);
+ }
+
+ if (option)
+ {
+ char option_name[MAX_OPTION_LEN + 1];
+ char option_value[MAX_OPTION_LEN + 1];
+
+ parse_option(option, option_name, option_value);
+
+ if (instance->is_mysqld_compatible() &&
+ Instance_options::is_option_im_specific(option_name))
+ {
+ log_info("Warning: configuration of mysqld-compatible instance '%s' "
+ "contains IM-specific option '%s'. "
+ "This breaks backward compatibility for the configuration file.",
+ (const char *) group->str,
+ (const char *) option_name);
+ }
+
+ Named_value option(option_name, option_value);
+
+ if (instance->options.set_option(&option))
+ return 1; /* the instance'll be deleted when we destroy the map */
+ }
+
+ return 0;
}
-Instance_map::Instance_map(const char *default_mysqld_path_arg):
-mysqld_path(default_mysqld_path_arg)
+/**
+ Instance_map constructor.
+*/
+
+Instance_map::Instance_map()
{
pthread_mutex_init(&LOCK_instance_map, 0);
}
-int Instance_map::init()
+/**
+ Initialize Instance_map internals.
+*/
+
+bool Instance_map::init()
{
return hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
get_instance_key, delete_instance, 0);
}
+
+/**
+ Reset Instance_map data.
+*/
+
+bool Instance_map::reset()
+{
+ hash_free(&hash);
+ return init();
+}
+
+
+/**
+ Instance_map destructor.
+*/
+
Instance_map::~Instance_map()
{
- pthread_mutex_lock(&LOCK_instance_map);
+ lock();
+
+ /*
+ NOTE: it's necessary to synchronize on each instance before removal,
+ because Instance-monitoring thread can be still alive an hold the mutex
+ (because it is detached and we have no control over it).
+ */
+
+ while (true)
+ {
+ Iterator it(this);
+ Instance *instance= it.next();
+
+ if (!instance)
+ break;
+
+ instance->lock();
+ instance->unlock();
+
+ remove_instance(instance);
+ }
+
hash_free(&hash);
- pthread_mutex_unlock(&LOCK_instance_map);
+ unlock();
+
pthread_mutex_destroy(&LOCK_instance_map);
}
+/**
+ Lock Instance_map.
+*/
+
void Instance_map::lock()
{
pthread_mutex_lock(&LOCK_instance_map);
}
+/**
+ Unlock Instance_map.
+*/
+
void Instance_map::unlock()
{
pthread_mutex_unlock(&LOCK_instance_map);
}
-/*
- Re-read instance configuration file.
- SYNOPSIS
- Instance_map::flush_instances()
+/**
+ Check if there is an active instance or not.
+*/
- DESCRIPTION
- This function will:
- - clear the current list of instances. This removes both
- running and stopped instances.
- - load a new instance configuration from the file.
- - pass on the new map to the guardian thread: it will start
- all instances that are marked `guarded' and not yet started.
- Note, as the check whether an instance is started is currently
- very simple (returns true if there is a MySQL server running
- at the given port), this function has some peculiar
- side-effects:
- * if the port number of a running instance was changed, the
- old instance is forgotten, even if it was running. The new
- instance will be started at the new port.
- * if the configuration was changed in a way that two
- instances swapped their port numbers, the guardian thread
- will not notice that and simply report that both instances
- are configured successfully and running.
- In order to avoid such side effects one should never call
- FLUSH INSTANCES without prior stop of all running instances.
-
- TODO
- FLUSH INSTANCES should return an error if it's called
- while there is a running instance.
+bool Instance_map::is_there_active_instance()
+{
+ Instance *instance;
+ Iterator iterator(this);
+
+ while ((instance= iterator.next()))
+ {
+ bool active_instance_found;
+
+ instance->lock();
+ active_instance_found= instance->is_active();
+ instance->unlock();
+
+ if (active_instance_found)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Add an instance into the internal hash.
+
+ MT-NOTE: Instance Map must be locked before calling the operation.
*/
-int Instance_map::flush_instances()
+int Instance_map::add_instance(Instance *instance)
{
- int rc;
+ return my_hash_insert(&hash, (uchar *) instance);
+}
- /*
- Guardian thread relies on the instance map repository for guarding
- instances. This is why refreshing instance map, we need (1) to stop
- guardian (2) reload the instance map (3) reinitialize the guardian
- with new instances.
- */
- guardian->lock();
- pthread_mutex_lock(&LOCK_instance_map);
- hash_free(&hash);
- hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
- get_instance_key, delete_instance, 0);
- rc= load();
- /* don't init guardian if we failed to load instances */
- if (!rc)
- guardian->init(); // TODO: check error status.
- pthread_mutex_unlock(&LOCK_instance_map);
- guardian->unlock();
- return rc;
+
+/**
+ Remove instance from the internal hash.
+
+ MT-NOTE: Instance Map must be locked before calling the operation.
+*/
+
+int Instance_map::remove_instance(Instance *instance)
+{
+ return hash_delete(&hash, (uchar *) instance);
}
-Instance *
-Instance_map::find(const char *name, uint name_len)
+/**
+ Create a new instance and register it in the internal hash.
+
+ MT-NOTE: Instance Map must be locked before calling the operation.
+*/
+
+int Instance_map::create_instance(const LEX_STRING *instance_name,
+ const Named_value_arr *options)
{
- Instance *instance;
- pthread_mutex_lock(&LOCK_instance_map);
- instance= (Instance *) hash_search(&hash, (byte *) name, name_len);
- pthread_mutex_unlock(&LOCK_instance_map);
- return instance;
+ Instance *instance= new Instance();
+
+ if (!instance)
+ {
+ log_error("Can not allocate instance (name: '%s').",
+ (const char *) instance_name->str);
+ return ER_OUT_OF_RESOURCES;
+ }
+
+ if (instance->init(instance_name))
+ {
+ log_error("Can not initialize instance (name: '%s').",
+ (const char *) instance_name->str);
+ delete instance;
+ return ER_OUT_OF_RESOURCES;
+ }
+
+ for (int i= 0; options && i < options->get_size(); ++i)
+ {
+ Named_value option= options->get_element(i);
+
+ if (instance->is_mysqld_compatible() &&
+ Instance_options::is_option_im_specific(option.get_name()))
+ {
+ log_error("IM-option (%s) can not be used "
+ "in configuration of mysqld-compatible instance (%s).",
+ (const char *) option.get_name(),
+ (const char *) instance_name->str);
+ delete instance;
+ return ER_INCOMPATIBLE_OPTION;
+ }
+
+ instance->options.set_option(&option);
+ }
+
+ if (instance->is_mysqld_compatible())
+ log_info("Warning: instance name '%s' is mysqld-compatible.",
+ (const char *) instance_name->str);
+
+ if (instance->complete_initialization())
+ {
+ log_error("Can not complete initialization of instance (name: '%s').",
+ (const char *) instance_name->str);
+ delete instance;
+ return ER_OUT_OF_RESOURCES;
+ /* TODO: return more appropriate error code in this case. */
+ }
+
+ if (add_instance(instance))
+ {
+ log_error("Can not register instance (name: '%s').",
+ (const char *) instance_name->str);
+ delete instance;
+ return ER_OUT_OF_RESOURCES;
+ }
+
+ return 0;
}
-int Instance_map::complete_initialization()
+/**
+ Return a pointer to the instance or NULL, if there is no such instance.
+
+ MT-NOTE: Instance Map must be locked before calling the operation.
+*/
+
+Instance * Instance_map::find(const LEX_STRING *name)
{
- Instance *instance;
- uint i= 0;
+ return (Instance *) hash_search(&hash, (uchar *) name->str, name->length);
+}
+
+
+/**
+ Init instances command line arguments after all options have been loaded.
+*/
+
+bool Instance_map::complete_initialization()
+{
+ bool mysqld_found;
+ /* Complete initialization of all registered instances. */
- if (hash.records == 0) /* no instances found */
+ for (uint i= 0; i < hash.records; ++i)
{
- if ((instance= new Instance) == 0)
- goto err;
+ Instance *instance= (Instance *) hash_element(&hash, i);
+
+ if (instance->complete_initialization())
+ return TRUE;
+ }
+
+ /* That's all if we are runnning in an ordinary mode. */
+
+ if (!Options::Main::mysqld_safe_compatible)
+ return FALSE;
+
+ /* In mysqld-compatible mode we must ensure that there 'mysqld' instance. */
- if (instance->init("mysqld") || my_hash_insert(&hash, (byte *) instance))
- goto err_instance;
+ mysqld_found= find(&Instance::DFLT_INSTANCE_NAME) != NULL;
+ if (mysqld_found)
+ return FALSE;
+
+ if (create_instance(&Instance::DFLT_INSTANCE_NAME, NULL))
+ {
+ log_error("Can not create default instance.");
+ return TRUE;
+ }
+
+ switch (create_instance_in_file(&Instance::DFLT_INSTANCE_NAME, NULL))
+ {
+ case 0:
+ case ER_CONF_FILE_DOES_NOT_EXIST:
/*
- After an instance have been added to the instance_map,
- hash_free should handle it's deletion => goto err, not
- err_instance.
+ Continue if the instance has been added to the config file
+ successfully, or the config file just does not exist.
*/
- if (instance->complete_initialization(this, mysqld_path,
- DEFAULT_SINGLE_INSTANCE))
- goto err;
+ break;
+
+ default:
+ log_error("Can not add default instance to the config file.");
+
+ Instance *instance= find(&Instance::DFLT_INSTANCE_NAME);
+
+ if (instance)
+ remove_instance(instance); /* instance is deleted here. */
+
+ return TRUE;
}
- else
- while (i < hash.records)
- {
- instance= (Instance *) hash_element(&hash, i);
- if (instance->complete_initialization(this, mysqld_path, USUAL_INSTANCE))
- goto err;
- i++;
- }
- return 0;
-err_instance:
- delete instance;
-err:
- return 1;
+ return FALSE;
}
-/* load options from config files and create appropriate instance structures */
+/**
+ Load options from config files and create appropriate instance
+ structures.
+*/
int Instance_map::load()
{
@@ -298,10 +517,10 @@ int Instance_map::load()
name and start looking for files named "my.cnf.cnf" in all
default dirs. Which is not what we want.
*/
- if (Options::is_forced_default_file)
+ if (Options::Main::is_forced_default_file)
{
snprintf(defaults_file_arg, FN_REFLEN, "--defaults-file=%s",
- Options::config_file);
+ Options::Main::config_file);
argv_options[1]= defaults_file_arg;
argv_options[2]= '\0';
@@ -315,20 +534,18 @@ int Instance_map::load()
If the routine failed, we'll simply fallback to defaults in
complete_initialization().
*/
- if (my_search_option_files(Options::config_file, &argc,
+ if (my_search_option_files(Options::Main::config_file, &argc,
(char ***) &argv, &args_used,
process_option, (void*) this))
- log_info("Falling back to compiled-in defaults");
-
- if (complete_initialization())
- return 1;
+ log_info("Falling back to compiled-in defaults.");
- return 0;
+ return complete_initialization();
}
-/*--- Implementaton of the Instance map iterator class ---*/
-
+/*************************************************************************
+ {{{ Instance_map::Iterator implementation.
+*************************************************************************/
void Instance_map::Iterator::go_to_first()
{
@@ -344,3 +561,88 @@ Instance *Instance_map::Iterator::next()
return NULL;
}
+/*************************************************************************
+ }}}
+*************************************************************************/
+
+
+/**
+ Create a new configuration section for mysqld-instance in the config file.
+
+ SYNOPSIS
+ create_instance_in_file()
+ instance_name mysqld-instance name
+ options options for the new mysqld-instance
+
+ RETURN
+ 0 On success
+ ER_CONF_FILE_DOES_NOT_EXIST If config file does not exist
+ ER_ACCESS_OPTION_FILE If config file is not writable or some I/O
+ error ocurred during writing configuration
+*/
+
+int create_instance_in_file(const LEX_STRING *instance_name,
+ const Named_value_arr *options)
+{
+ File cnf_file;
+
+ if (my_access(Options::Main::config_file, W_OK))
+ {
+ log_error("Configuration file (%s) does not exist.",
+ (const char *) Options::Main::config_file);
+ return ER_CONF_FILE_DOES_NOT_EXIST;
+ }
+
+ cnf_file= my_open(Options::Main::config_file, O_WRONLY | O_APPEND, MYF(0));
+
+ if (cnf_file <= 0)
+ {
+ log_error("Can not open configuration file (%s): %s.",
+ (const char *) Options::Main::config_file,
+ (const char *) strerror(errno));
+ return ER_ACCESS_OPTION_FILE;
+ }
+
+ if (my_write(cnf_file, (uchar*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP)) ||
+ my_write(cnf_file, (uchar*)"[", 1, MYF(MY_NABP)) ||
+ my_write(cnf_file, (uchar*)instance_name->str, instance_name->length,
+ MYF(MY_NABP)) ||
+ my_write(cnf_file, (uchar*)"]", 1, MYF(MY_NABP)) ||
+ my_write(cnf_file, (uchar*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP)))
+ {
+ log_error("Can not write to configuration file (%s): %s.",
+ (const char *) Options::Main::config_file,
+ (const char *) strerror(errno));
+ my_close(cnf_file, MYF(0));
+ return ER_ACCESS_OPTION_FILE;
+ }
+
+ for (int i= 0; options && i < options->get_size(); ++i)
+ {
+ char option_str[MAX_OPTION_STR_LEN];
+ char *ptr;
+ int option_str_len;
+ Named_value option= options->get_element(i);
+
+ ptr= strxnmov(option_str, MAX_OPTION_LEN + 1, option.get_name(), NullS);
+
+ if (option.get_value()[0])
+ ptr= strxnmov(ptr, MAX_OPTION_LEN + 2, "=", option.get_value(), NullS);
+
+ option_str_len= ptr - option_str;
+
+ if (my_write(cnf_file, (uchar*)option_str, option_str_len, MYF(MY_NABP)) ||
+ my_write(cnf_file, (uchar*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP)))
+ {
+ log_error("Can not write to configuration file (%s): %s.",
+ (const char *) Options::Main::config_file,
+ (const char *) strerror(errno));
+ my_close(cnf_file, MYF(0));
+ return ER_ACCESS_OPTION_FILE;
+ }
+ }
+
+ my_close(cnf_file, MYF(0));
+
+ return 0;
+}
diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h
index 18e82f0106b..af2f1868195 100644
--- a/server-tools/instance-manager/instance_map.h
+++ b/server-tools/instance-manager/instance_map.h
@@ -16,30 +16,37 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
-
-#include "protocol.h"
-#include "guardian.h"
-
#include <my_sys.h>
+#include <m_string.h>
#include <hash.h>
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
+class Guardian;
class Instance;
+class Named_value_arr;
+class Thread_registry;
+
extern int load_all_groups(char ***groups, const char *filename);
extern void free_groups(char **groups);
+extern int create_instance_in_file(const LEX_STRING *instance_name,
+ const Named_value_arr *options);
-/*
+
+/**
Instance_map - stores all existing instances
*/
class Instance_map
{
public:
- /* Instance_map iterator */
+ /**
+ Instance_map iterator
+ */
+
class Iterator
{
private:
@@ -53,37 +60,43 @@ public:
void go_to_first();
Instance *next();
};
- friend class Iterator;
+
public:
- /* returns a pointer to the instance or NULL, if there is no such instance */
- Instance *find(const char *name, uint name_len);
+ Instance *find(const LEX_STRING *name);
+
+ bool is_there_active_instance();
- int flush_instances();
void lock();
void unlock();
- int init();
- /*
- Process a given option and assign it to appropricate instance. This is
- required for the option handler, passed to my_search_option_files().
- */
- int process_one_option(const char *group, const char *option);
- Instance_map(const char *default_mysqld_path_arg);
- ~Instance_map();
+ bool init();
+ bool reset();
+
+ int load();
+
+ int process_one_option(const LEX_STRING *group, const char *option);
+
+ int add_instance(Instance *instance);
+
+ int remove_instance(Instance *instance);
+
+ int create_instance(const LEX_STRING *instance_name,
+ const Named_value_arr *options);
public:
- const char *mysqld_path;
- Guardian_thread *guardian;
+ Instance_map();
+ ~Instance_map();
private:
- /* loads options from config files */
- int load();
- /* inits instances argv's after all options have been loaded */
- int complete_initialization();
+ bool complete_initialization();
+
private:
enum { START_HASH_SIZE = 16 };
pthread_mutex_t LOCK_instance_map;
HASH hash;
+
+private:
+ friend class Iterator;
};
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H */
diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc
index 9d88bb0e669..8b96d6f0f96 100644
--- a/server-tools/instance-manager/instance_options.cc
+++ b/server-tools/instance-manager/instance_options.cc
@@ -19,28 +19,25 @@
#include "instance_options.h"
-#include "parse_output.h"
-#include "buffer.h"
-#include "log.h"
-
+#include <my_global.h>
#include <my_sys.h>
-#include <signal.h>
#include <m_string.h>
-#ifdef __WIN__
-#define NEWLINE_LEN 2
-#else
-#define NEWLINE_LEN 1
-#endif
+#include <signal.h>
+
+#include "buffer.h"
+#include "instance.h"
+#include "log.h"
+#include "options.h"
+#include "parse_output.h"
+#include "priv.h"
/* Create "mysqld ..." command in the buffer */
-static inline int create_mysqld_command(Buffer *buf,
- const char *mysqld_path_str,
- uint mysqld_path_len,
- const char *option,
- uint option_len)
+static inline bool create_mysqld_command(Buffer *buf,
+ const LEX_STRING *mysqld_path,
+ const LEX_STRING *option)
{
int position= 0;
@@ -49,24 +46,87 @@ static inline int create_mysqld_command(Buffer *buf,
#ifdef __WIN__
buf->append(position++, "\"", 1);
#endif
- buf->append(position, mysqld_path_str, mysqld_path_len);
- position+= mysqld_path_len;
+ buf->append(position, mysqld_path->str, mysqld_path->length);
+ position+= mysqld_path->length;
#ifdef __WIN__
buf->append(position++, "\"", 1);
#endif
/* here the '\0' character is copied from the option string */
- buf->append(position, option, option_len);
+ buf->append(position, option->str, option->length + 1);
+
+ return buf->is_error() ? TRUE : FALSE;
+ }
+ return TRUE;
+}
+
+static inline bool is_path_separator(char ch)
+{
+#if defined(__WIN__) || defined(__NETWARE__)
+ /* On windows and netware more delimiters are possible */
+ return ch == FN_LIBCHAR || ch == FN_DEVCHAR || ch == '/';
+#else
+ return ch == FN_LIBCHAR; /* Unixes */
+#endif
+}
+
- return buf->is_error();
+static char *find_last_path_separator(char *path, uint length)
+{
+ while (length)
+ {
+ if (is_path_separator(path[length]))
+ return path + length;
+ length--;
}
- return 1;
+ return NULL; /* No path separator found */
+}
+
+
+
+bool Instance_options::is_option_im_specific(const char *option_name)
+{
+ static const char *IM_SPECIFIC_OPTIONS[] =
+ {
+ "nonguarded",
+ "mysqld-path",
+ "shutdown-delay",
+ NULL
+ };
+
+ for (int i= 0; IM_SPECIFIC_OPTIONS[i]; ++i)
+ {
+ if (!strcmp(option_name, IM_SPECIFIC_OPTIONS[i]))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+Instance_options::Instance_options()
+ :mysqld_version(NULL), mysqld_socket(NULL), mysqld_datadir(NULL),
+ mysqld_pid_file(NULL),
+ nonguarded(NULL),
+ mysqld_port(NULL),
+ mysqld_port_val(0),
+ shutdown_delay(NULL),
+ shutdown_delay_val(0),
+ filled_default_options(0)
+{
+ mysqld_path.str= NULL;
+ mysqld_path.length= 0;
+
+ mysqld_real_path.str= NULL;
+ mysqld_real_path.length= 0;
+
+ memset(logs, 0, sizeof(logs));
}
/*
Get compiled-in value of default_option
- SYNOPSYS
+ SYNOPSIS
get_default_option()
result buffer to put found value
result_len buffer size
@@ -86,17 +146,18 @@ int Instance_options::get_default_option(char *result, size_t result_len,
const char *option_name)
{
int rc= 1;
- char verbose_option[]= " --no-defaults --verbose --help";
+ LEX_STRING verbose_option=
+ { C_STRING_WITH_LEN(" --no-defaults --verbose --help") };
- /* reserve space fot the path + option + final '\0' */
- Buffer cmd(mysqld_path_len + sizeof(verbose_option));
+ /* reserve space for the path + option + final '\0' */
+ Buffer cmd(mysqld_path.length + verbose_option.length + 1);
- if (create_mysqld_command(&cmd, mysqld_path, mysqld_path_len,
- verbose_option, sizeof(verbose_option)))
+ if (create_mysqld_command(&cmd, &mysqld_path, &verbose_option))
goto err;
/* +2 eats first "--" from the option string (E.g. "--datadir") */
- rc= parse_output_and_get_value(cmd.buffer, option_name + 2,
+ rc= parse_output_and_get_value((char*) cmd.buffer,
+ option_name + 2, strlen(option_name + 2),
result, result_len, GET_VALUE);
err:
return rc;
@@ -106,7 +167,7 @@ err:
/*
Fill mysqld_version option (used at initialization stage)
- SYNOPSYS
+ SYNOPSIS
fill_instance_version()
DESCRIPTION
@@ -114,46 +175,55 @@ err:
Get mysqld version string from "mysqld --version" output.
RETURN
- 0 - ok
- 1 - error occured
+ FALSE - ok
+ TRUE - error occured
*/
-int Instance_options::fill_instance_version()
+bool Instance_options::fill_instance_version()
{
- enum { MAX_VERSION_STRING_LENGTH= 160 };
- char result[MAX_VERSION_STRING_LENGTH];
- char version_option[]= " --no-defaults --version";
- int rc= 1;
- Buffer cmd(mysqld_path_len + sizeof(version_option));
+ char result[MAX_VERSION_LENGTH];
+ LEX_STRING version_option=
+ { C_STRING_WITH_LEN(" --no-defaults --version") };
+ Buffer cmd(mysqld_path.length + version_option.length + 1);
- if (create_mysqld_command(&cmd, mysqld_path, mysqld_path_len,
- version_option, sizeof(version_option)))
- goto err;
+ if (create_mysqld_command(&cmd, &mysqld_path, &version_option))
+ {
+ log_error("Failed to get version of '%s': out of memory.",
+ (const char *) mysqld_path.str);
+ return TRUE;
+ }
- bzero(result, MAX_VERSION_STRING_LENGTH);
+ bzero(result, MAX_VERSION_LENGTH);
- rc= parse_output_and_get_value(cmd.buffer, mysqld_real_path,
- result, MAX_VERSION_STRING_LENGTH,
- GET_LINE);
+ if (parse_output_and_get_value((char*) cmd.buffer, STRING_WITH_LEN("Ver"),
+ result, MAX_VERSION_LENGTH, GET_LINE))
+ {
+ log_error("Failed to get version of '%s': unexpected output.",
+ (const char *) mysqld_path.str);
+ return TRUE;
+ }
+
+ DBUG_ASSERT(*result != '\0');
- if (*result != '\0')
{
- /* chop the newline from the end of the version string */
- result[strlen(result) - NEWLINE_LEN]= '\0';
- mysqld_version= strdup_root(&alloc, result);
+ char *start;
+
+ /* trim leading whitespaces */
+ start= result;
+ while (my_isspace(default_charset_info, *start))
+ ++start;
+
+ mysqld_version= strdup_root(&alloc, start);
}
-err:
- if (rc)
- log_error("fill_instance_version: Failed to get version of '%s'",
- mysqld_path);
- return rc;
+
+ return FALSE;
}
/*
Fill mysqld_real_path
- SYNOPSYS
+ SYNOPSIS
fill_mysqld_real_path()
DESCRIPTION
@@ -165,46 +235,55 @@ err:
script(for example libtool) or a symlink.
RETURN
- 0 - ok
- 1 - error occured
+ FALSE - ok
+ TRUE - error occured
*/
-int Instance_options::fill_mysqld_real_path()
+bool Instance_options::fill_mysqld_real_path()
{
char result[FN_REFLEN];
- char help_option[]= " --no-defaults --help";
- int rc= 1;
- Buffer cmd(mysqld_path_len + sizeof(help_option));
+ LEX_STRING help_option=
+ { C_STRING_WITH_LEN(" --no-defaults --help") };
+ Buffer cmd(mysqld_path.length + help_option.length);
- if (create_mysqld_command(&cmd, mysqld_path, mysqld_path_len,
- help_option, sizeof(help_option)))
- goto err;
+ if (create_mysqld_command(&cmd, &mysqld_path, &help_option))
+ {
+ log_error("Failed to get real path of '%s': out of memory.",
+ (const char *) mysqld_path.str);
+ return TRUE;
+ }
bzero(result, FN_REFLEN);
- rc= parse_output_and_get_value(cmd.buffer, "Usage: ",
+ if (parse_output_and_get_value((char*) cmd.buffer,
+ STRING_WITH_LEN("Usage: "),
result, FN_REFLEN,
- GET_LINE);
+ GET_LINE))
+ {
+ log_error("Failed to get real path of '%s': unexpected output.",
+ (const char *) mysqld_path.str);
+ return TRUE;
+ }
+
+ DBUG_ASSERT(*result != '\0');
- if (*result != '\0')
{
char* options_str;
/* chop the path of at [OPTIONS] */
if ((options_str= strstr(result, "[OPTIONS]")))
*options_str= '\0';
- mysqld_real_path= strdup_root(&alloc, result);
+ mysqld_real_path.str= strdup_root(&alloc, result);
+ mysqld_real_path.length= strlen(mysqld_real_path.str);
}
-err:
- if (rc)
- log_error("fill_mysqld_real_path: Failed to get real path of mysqld");
- return rc;
+
+ return FALSE;
}
/*
Fill various log options
- SYNOPSYS
+ SYNOPSIS
fill_log_options()
DESCRIPTION
@@ -214,11 +293,11 @@ err:
file name and placement.
RETURN
- 0 - ok
- 1 - error occured
+ FALSE - ok
+ TRUE - error occured
*/
-int Instance_options::fill_log_options()
+bool Instance_options::fill_log_options()
{
Buffer buff;
enum { MAX_LOG_OPTION_LENGTH= 256 };
@@ -244,20 +323,19 @@ int Instance_options::fill_log_options()
if (mysqld_datadir == NULL)
{
if (get_default_option(datadir, MAX_LOG_OPTION_LENGTH, "--datadir"))
- goto err;
+ return TRUE;
}
else
{
/* below is safe, as --datadir always has a value */
- strmake(datadir,
- strchr(mysqld_datadir, '=') + 1, MAX_LOG_OPTION_LENGTH - 1);
+ strmake(datadir, mysqld_datadir, MAX_LOG_OPTION_LENGTH - 1);
}
if (gethostname(hostname,sizeof(hostname)-1) < 0)
strmov(hostname, "mysql");
hostname[MAX_LOG_OPTION_LENGTH - 1]= 0; /* Safety */
- hostname_length= (uint) strlen(hostname);
+ hostname_length= strlen(hostname);
for (log_files= logs_st; log_files->name; log_files++)
@@ -283,7 +361,7 @@ int Instance_options::fill_log_options()
if ((MAX_LOG_OPTION_LENGTH - strlen(full_name)) <=
strlen(log_files->default_suffix))
- goto err;
+ return TRUE;
strmov(full_name + strlen(full_name), log_files->default_suffix);
@@ -303,22 +381,20 @@ int Instance_options::fill_log_options()
datadir, "", MY_UNPACK_FILENAME | MY_SAFE_PATH);
if (!(*(log_files->value)= strdup_root(&alloc, full_name)))
- goto err;
+ return TRUE;
}
}
}
}
- return 0;
-err:
- return 1;
+ return FALSE;
}
/*
Get the full pid file name with path
- SYNOPSYS
+ SYNOPSIS
get_pid_filaname()
result buffer to sotre the pidfile value
@@ -336,7 +412,6 @@ err:
int Instance_options::get_pid_filename(char *result)
{
- const char *pid_file= mysqld_pid_file;
char datadir[MAX_PATH_LEN];
if (mysqld_datadir == NULL)
@@ -346,14 +421,10 @@ int Instance_options::get_pid_filename(char *result)
return 1;
}
else
- strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1,
- "/", NullS);
-
- DBUG_ASSERT(mysqld_pid_file);
- pid_file= strchr(pid_file, '=') + 1;
+ strxnmov(datadir, MAX_PATH_LEN - 1, mysqld_datadir, "/", NullS);
/* get the full path to the pidfile */
- my_load_path(result, pid_file, datadir);
+ my_load_path(result, mysqld_pid_file, datadir);
return 0;
}
@@ -364,7 +435,7 @@ int Instance_options::unlink_pidfile()
}
-pid_t Instance_options::get_pid()
+pid_t Instance_options::load_pid()
{
FILE *pid_file_stream;
@@ -383,36 +454,62 @@ pid_t Instance_options::get_pid()
}
-int Instance_options::complete_initialization(const char *default_path,
- uint instance_type)
+bool Instance_options::complete_initialization()
{
+ int arg_idx;
const char *tmp;
char *end;
+ char bin_name_firstchar;
- if (!mysqld_path)
+ if (!mysqld_path.str)
{
- // Need one extra byte, as convert_dirname() adds a slash at the end.
- if (!(mysqld_path= alloc_root(&alloc, (uint) strlen(default_path) + 2)))
- goto err;
- strcpy((char *)mysqld_path, default_path);
+ /*
+ Need to copy the path to allocated memory, as convert_dirname() might
+ need to change it
+ */
+ mysqld_path.str= strdup_root(&alloc, Options::Main::default_mysqld_path);
+ if (!mysqld_path.str)
+ return TRUE;
}
- // it's safe to cast this to char* since this is a buffer we are allocating
- end= convert_dirname((char*)mysqld_path, mysqld_path, NullS);
- end[-1]= 0;
+ mysqld_path.length= strlen(mysqld_path.str);
+
+ /*
+ If we found path with no slashes (end == NULL), we should not call
+ convert_dirname() at all. As we have got relative path to the binary.
+ That is, user supposes that mysqld resides in the same dir as
+ mysqlmanager.
+ */
+ if ((end= find_last_path_separator(mysqld_path.str, mysqld_path.length)))
+ {
+ bin_name_firstchar= end[1];
+
+ /*
+ Below we will conver the path to mysqld in the case, it was given
+ in a format of another OS (e.g. uses '/' instead of '\' etc).
+ Here we strip the path to get rid of the binary name ("mysqld"),
+ we do it by removing first letter of the binary name (e.g. 'm'
+ in "mysqld"). Later we put it back.
+ */
+ end[1]= 0;
+
+ /* convert dirname to the format of current OS */
+ convert_dirname((char*)mysqld_path.str, mysqld_path.str, NullS);
- mysqld_path_len= (uint) strlen(mysqld_path);
+ /* put back the first character of the binary name*/
+ end[1]= bin_name_firstchar;
+ }
if (mysqld_port)
- mysqld_port_val= atoi(strchr(mysqld_port, '=') + 1);
+ mysqld_port_val= atoi(mysqld_port);
if (shutdown_delay)
shutdown_delay_val= atoi(shutdown_delay);
if (!(tmp= strdup_root(&alloc, "--no-defaults")))
- goto err;
+ return TRUE;
- if (!(mysqld_pid_file))
+ if (!mysqld_pid_file)
{
char pidfilename[MAX_PATH_LEN];
char hostname[MAX_PATH_LEN];
@@ -421,127 +518,158 @@ int Instance_options::complete_initialization(const char *default_path,
If we created only one istance [mysqld], because no config. files were
found, we would like to model mysqld pid file values.
*/
+
if (!gethostname(hostname, sizeof(hostname) - 1))
{
- if (instance_type & DEFAULT_SINGLE_INSTANCE)
- strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname,
- ".pid", NullS);
+ if (Instance::is_mysqld_compatible_name(&instance_name))
+ strxnmov(pidfilename, MAX_PATH_LEN - 1, hostname, ".pid", NullS);
else
- strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name,
- "-", hostname, ".pid", NullS);
+ strxnmov(pidfilename, MAX_PATH_LEN - 1, instance_name.str, "-",
+ hostname, ".pid", NullS);
}
else
{
- if (instance_type & DEFAULT_SINGLE_INSTANCE)
- strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", "mysql",
- ".pid", NullS);
+ if (Instance::is_mysqld_compatible_name(&instance_name))
+ strxnmov(pidfilename, MAX_PATH_LEN - 1, "mysql", ".pid", NullS);
else
- strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name,
- ".pid", NullS);
+ strxnmov(pidfilename, MAX_PATH_LEN - 1, instance_name.str, ".pid",
+ NullS);
}
- add_option(pidfilename);
+ Named_value option((char *) "pid-file", pidfilename);
+
+ set_option(&option);
}
if (get_pid_filename(pid_file_with_path))
- goto err;
+ return TRUE;
/* we need to reserve space for the final zero + possible default options */
if (!(argv= (char**)
- alloc_root(&alloc, (options_array.elements + 1
+ alloc_root(&alloc, (get_num_options() + 1
+ MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*))))
- goto err;
+ return TRUE;
+ filled_default_options= 0;
/* the path must be first in the argv */
- if (add_to_argv(mysqld_path))
- goto err;
+ if (add_to_argv(mysqld_path.str))
+ return TRUE;
if (add_to_argv(tmp))
- goto err;
+ return TRUE;
+
+ arg_idx= filled_default_options;
+ for (int opt_idx= 0; opt_idx < get_num_options(); ++opt_idx)
+ {
+ char option_str[MAX_OPTION_STR_LEN];
+ Named_value option= get_option(opt_idx);
+
+ if (is_option_im_specific(option.get_name()))
+ continue;
+
+ char *ptr= strxnmov(option_str, MAX_OPTION_LEN + 3, "--", option.get_name(),
+ NullS);
+
+ if (option.get_value()[0])
+ strxnmov(ptr, MAX_OPTION_LEN + 2, "=", option.get_value(), NullS);
+
+ argv[arg_idx++]= strdup_root(&alloc, option_str);
+ }
- memcpy((gptr) (argv + filled_default_options), options_array.buffer,
- options_array.elements*sizeof(char*));
- argv[filled_default_options + options_array.elements]= 0;
+ argv[arg_idx]= 0;
if (fill_log_options() || fill_mysqld_real_path() || fill_instance_version())
- goto err;
+ return TRUE;
- return 0;
+ return FALSE;
+}
-err:
- return 1;
+
+bool Instance_options::set_option(Named_value *option)
+{
+ bool err_status;
+ int idx= find_option(option->get_name());
+ char *option_name_str;
+ char *option_value_str;
+
+ if (!(option_name_str= Named_value::alloc_str(option->get_name())))
+ return TRUE;
+
+ if (!(option_value_str= Named_value::alloc_str(option->get_value())))
+ {
+ Named_value::free_str(&option_name_str);
+ return TRUE;
+ }
+
+ Named_value option_copy(option_name_str, option_value_str);
+
+ if (idx < 0)
+ err_status= options.add_element(&option_copy);
+ else
+ err_status= options.replace_element(idx, &option_copy);
+
+ if (!err_status)
+ update_var(option_copy.get_name(), option_copy.get_value());
+ else
+ option_copy.free();
+
+ return err_status;
}
-/*
- Assigns given value to the appropriate option from the class.
+void Instance_options::unset_option(const char *option_name)
+{
+ int idx= find_option(option_name);
- SYNOPSYS
- add_option()
- option string with the option prefixed by --
+ if (idx < 0)
+ return; /* the option has not been set. */
- DESCRIPTION
+ options.remove_element(idx);
- The method is called from the option handling routine.
+ update_var(option_name, NULL);
+}
- RETURN
- 0 - ok
- 1 - error occured
-*/
-int Instance_options::add_option(const char* option)
+void Instance_options::update_var(const char *option_name,
+ const char *option_value)
{
- char *tmp;
- enum { SAVE_VALUE= 1, SAVE_WHOLE, SAVE_WHOLE_AND_ADD };
- struct selected_options_st
+ struct options_st
{
const char *name;
- uint length;
- const char **value;
- uint type;
- } options[]=
+ uint name_len;
+ const char **var;
+ } options_def[]=
{
- {"--socket=", 9, &mysqld_socket, SAVE_WHOLE_AND_ADD},
- {"--port=", 7, &mysqld_port, SAVE_WHOLE_AND_ADD},
- {"--datadir=", 10, &mysqld_datadir, SAVE_WHOLE_AND_ADD},
- {"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD},
- {"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD},
- {"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE},
- {"--nonguarded", 9, &nonguarded, SAVE_WHOLE},
- {"--shutdown-delay", 9, &shutdown_delay, SAVE_VALUE},
- {NULL, 0, NULL, 0}
+ {"socket", 6, &mysqld_socket},
+ {"port", 4, &mysqld_port},
+ {"datadir", 7, &mysqld_datadir},
+ {"pid-file", 8, &mysqld_pid_file},
+ {"nonguarded", 10, &nonguarded},
+ {"mysqld-path", 11, (const char **) &mysqld_path.str},
+ {"shutdown-delay", 14, &shutdown_delay},
+ {NULL, 0, NULL}
};
- struct selected_options_st *selected_options;
- if (!(tmp= strdup_root(&alloc, option)))
- goto err;
+ for (options_st *opt= options_def; opt->name; ++opt)
+ {
+ if (!strncmp(opt->name, option_name, opt->name_len))
+ {
+ *(opt->var)= option_value;
+ break;
+ }
+ }
+}
- for (selected_options= options; selected_options->name; selected_options++)
- {
- if (strncmp(tmp, selected_options->name, selected_options->length) == 0)
- switch (selected_options->type) {
- case SAVE_WHOLE_AND_ADD:
- *(selected_options->value)= tmp;
- insert_dynamic(&options_array,(gptr) &tmp);
- return 0;
- case SAVE_VALUE:
- *(selected_options->value)= strchr(tmp, '=') + 1;
- return 0;
- case SAVE_WHOLE:
- *(selected_options->value)= tmp;
- return 0;
- default:
- break;
- }
- }
-
- /* if we haven't returned earlier we should just save the option */
- insert_dynamic(&options_array,(gptr) &tmp);
- return 0;
+int Instance_options::find_option(const char *option_name)
+{
+ for (int i= 0; i < get_num_options(); i++)
+ {
+ if (!strcmp(get_option(i).get_name(), option_name))
+ return i;
+ }
-err:
- return 1;
+ return -1;
}
@@ -559,7 +687,10 @@ int Instance_options::add_to_argv(const char* option)
void Instance_options::print_argv()
{
int i;
- printf("printing out an instance %s argv:\n", instance_name);
+
+ printf("printing out an instance %s argv:\n",
+ (const char *) instance_name.str);
+
for (i=0; argv[i] != NULL; i++)
printf("argv: %s\n", argv[i]);
}
@@ -567,32 +698,56 @@ void Instance_options::print_argv()
/*
We execute this function to initialize some options.
- Return value: 0 - ok. 1 - unable to allocate memory.
+
+ RETURN
+ FALSE - ok
+ TRUE - memory allocation error
*/
-int Instance_options::init(const char *instance_name_arg)
+bool Instance_options::init(const LEX_STRING *instance_name_arg)
{
- instance_name_len= (uint) strlen(instance_name_arg);
+ instance_name.length= instance_name_arg->length;
init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
- if (my_init_dynamic_array(&options_array, sizeof(char*), 0, 32))
- goto err;
+ if (options.init())
+ return TRUE;
- if (!(instance_name= strmake_root(&alloc, (char*) instance_name_arg,
- instance_name_len)))
- goto err;
+ if (!(instance_name.str= strmake_root(&alloc, instance_name_arg->str,
+ instance_name_arg->length)))
+ return TRUE;
- return 0;
-
-err:
- return 1;
+ return FALSE;
}
Instance_options::~Instance_options()
{
free_root(&alloc, MYF(0));
- delete_dynamic(&options_array);
+}
+
+
+uint Instance_options::get_shutdown_delay() const
+{
+ static const uint DEFAULT_SHUTDOWN_DELAY= 35;
+
+ /*
+ 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.
+ */
+
+ return shutdown_delay ? shutdown_delay_val : DEFAULT_SHUTDOWN_DELAY;
+}
+
+int Instance_options::get_mysqld_port() const
+{
+ /*
+ 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.
+ */
+
+ return mysqld_port ? mysqld_port_val : 0;
}
diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h
index fdae77985c2..b0503815036 100644
--- a/server-tools/instance-manager/instance_options.h
+++ b/server-tools/instance-manager/instance_options.h
@@ -17,8 +17,9 @@
#include <my_global.h>
#include <my_sys.h>
+
#include "parse.h"
-#include "portability.h"
+#include "portability.h" /* for pid_t on Win32 */
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
@@ -34,30 +35,34 @@
don't have to synchronize between threads.
*/
-#define USUAL_INSTANCE 0
-#define DEFAULT_SINGLE_INSTANCE 1
-
class Instance_options
{
public:
- Instance_options() :
- mysqld_version(0), mysqld_socket(0), mysqld_datadir(0),
- mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0),
- mysqld_port_val(0), mysqld_path(0), mysqld_real_path(0),
- nonguarded(0), shutdown_delay(0),
- shutdown_delay_val(0), filled_default_options(0)
- {}
+ /* The operation is used to check if the option is IM-specific or not. */
+ static bool is_option_im_specific(const char *option_name);
+
+public:
+ Instance_options();
~Instance_options();
- /* fills in argv */
- int complete_initialization(const char *default_path, uint instance_type);
- int add_option(const char* option);
- int init(const char *instance_name_arg);
- pid_t get_pid();
+ bool complete_initialization();
+
+ bool set_option(Named_value *option);
+ void unset_option(const char *option_name);
+
+ inline int get_num_options() const;
+ inline Named_value get_option(int idx) const;
+
+public:
+ bool init(const LEX_STRING *instance_name_arg);
+ pid_t load_pid();
int get_pid_filename(char *result);
int unlink_pidfile();
void print_argv();
+ uint get_shutdown_delay() const;
+ int get_mysqld_port() const;
+
public:
/*
We need this value to be greater or equal then FN_REFLEN found in
@@ -65,7 +70,6 @@ public:
*/
enum { MAX_PATH_LEN= 512 };
enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 2 };
- enum { MEM_ROOT_BLOCK_SIZE= 512 };
char pid_file_with_path[MAX_PATH_LEN];
char **argv;
/*
@@ -76,33 +80,47 @@ public:
/* We need the some options, so we store them as a separate pointers */
const char *mysqld_socket;
const char *mysqld_datadir;
- const char *mysqld_bind_address;
const char *mysqld_pid_file;
- const char *mysqld_port;
- uint mysqld_port_val;
- const char *instance_name;
- uint instance_name_len;
- const char *mysqld_path;
- uint mysqld_path_len;
- const char *mysqld_real_path;
+ LEX_STRING instance_name;
+ LEX_STRING mysqld_path;
+ LEX_STRING mysqld_real_path;
const char *nonguarded;
- const char *shutdown_delay;
- uint shutdown_delay_val;
/* log enums are defined in parse.h */
char *logs[3];
- /* this value is computed and cashed here */
- DYNAMIC_ARRAY options_array;
private:
- int fill_log_options();
- int fill_instance_version();
- int fill_mysqld_real_path();
+ bool fill_log_options();
+ bool fill_instance_version();
+ bool fill_mysqld_real_path();
int add_to_argv(const char *option);
int get_default_option(char *result, size_t result_len,
const char *option_name);
+
+ void update_var(const char *option_name, const char *option_value);
+ int find_option(const char *option_name);
+
private:
+ const char *mysqld_port;
+ uint mysqld_port_val;
+ const char *shutdown_delay;
+ uint shutdown_delay_val;
+
uint filled_default_options;
MEM_ROOT alloc;
+
+ Named_value_arr options;
};
+
+inline int Instance_options::get_num_options() const
+{
+ return options.get_size();
+}
+
+
+inline Named_value Instance_options::get_option(int idx) const
+{
+ return options.get_element(idx);
+}
+
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H */
diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc
index 36f0cbe85e1..4d8a33e7db1 100644
--- a/server-tools/instance-manager/listener.cc
+++ b/server-tools/instance-manager/listener.cc
@@ -18,97 +18,58 @@
#endif
#include "listener.h"
-#include "priv.h"
-#include <m_string.h>
+
+#include <my_global.h>
#include <mysql.h>
#include <violite.h>
+
+#include <sys/stat.h>
#ifndef __WIN__
#include <sys/un.h>
#endif
-#include <sys/stat.h>
-#include "thread_registry.h"
-#include "options.h"
-#include "instance_map.h"
#include "log.h"
#include "mysql_connection.h"
+#include "options.h"
#include "portability.h"
+#include "priv.h"
+#include "thread_registry.h"
-#ifndef __WIN__
static void set_non_blocking(int socket)
{
+#ifndef __WIN__
int flags= fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);
#else
-static void set_non_blocking(SOCKET socket)
-{
u_long arg= 1;
ioctlsocket(socket, FIONBIO, &arg);
#endif
}
-#ifndef __WIN__
static void set_no_inherit(int socket)
{
+#ifndef __WIN__
int flags= fcntl(socket, F_GETFD, 0);
fcntl(socket, F_SETFD, flags | FD_CLOEXEC);
-#else
-static void set_no_inherit(SOCKET socket)
-{
#endif
}
+const int Listener::LISTEN_BACK_LOG_SIZE= 5; /* standard backlog size */
-/*
- Listener_thread - incapsulates listening functionality
-*/
-
-class Listener_thread: public Listener_thread_args
-{
-public:
- Listener_thread(const Listener_thread_args &args);
- ~Listener_thread();
- void run();
-private:
- static const int LISTEN_BACK_LOG_SIZE;
- ulong total_connection_count;
- Thread_info thread_info;
-
-#ifdef __WIN__
- SOCKET sockets[2];
-#else
- int sockets[2];
-#endif
- int num_sockets;
- fd_set read_fds;
-private:
- void handle_new_mysql_connection(Vio *vio);
- int create_tcp_socket();
- int create_unix_socket(struct sockaddr_un &unix_socket_address);
-};
-
-const int Listener_thread::LISTEN_BACK_LOG_SIZE = 5; /* standard backlog size */
-
-
-Listener_thread::Listener_thread(const Listener_thread_args &args) :
- Listener_thread_args(args.thread_registry, args.options, args.user_map,
- args.instance_map)
- ,total_connection_count(0)
- ,thread_info(pthread_self())
- ,num_sockets(0)
-{
-}
-
-
-Listener_thread::~Listener_thread()
+Listener::Listener(Thread_registry *thread_registry_arg,
+ User_map *user_map_arg)
+ :thread_registry(thread_registry_arg),
+ user_map(user_map_arg),
+ total_connection_count(0),
+ num_sockets(0)
{
}
/*
- Listener_thread::run() - listen all supported sockets and spawn a thread
+ Listener::run() - listen all supported sockets and spawn a thread
to handle incoming connection.
Using 'die' in case of syscall failure is OK now - we don't hold any
resources and 'die' kills the signal thread automatically. To be rewritten
@@ -117,27 +78,17 @@ Listener_thread::~Listener_thread()
architecture.
*/
-void Listener_thread::run()
+void Listener::run()
{
- int i= 0;
+ int i, n= 0;
#ifndef __WIN__
- int n= 0;
- /* we use this var to check whether we are running on LinuxThreads */
- pid_t thread_pid;
-
- thread_pid= getpid();
-
struct sockaddr_un unix_socket_address;
- /* set global variable */
- linuxthreads= (thread_pid != manager_pid);
-#else
- SOCKET n= 0;
#endif
- thread_registry.register_thread(&thread_info);
+ log_info("Listener: started.");
- my_thread_init();
+ thread_registry->register_thread(&thread_info);
FD_ZERO(&read_fds);
@@ -156,7 +107,7 @@ void Listener_thread::run()
n++;
timeval tv;
- while (!thread_registry.is_shutdown())
+ while (!thread_registry->is_shutdown())
{
fd_set read_fds_arg= read_fds;
/*
@@ -171,17 +122,13 @@ void Listener_thread::run()
signal during shutdown. This results in failing assert
(Thread_registry::~Thread_registry). Valgrind 2.2 works fine.
*/
-#ifdef __WIN__
- int rc= select(0, &read_fds_arg, 0, 0, &tv);
-#else
int rc= select(n, &read_fds_arg, 0, 0, &tv);
-#endif
if (rc == 0 || rc == -1)
{
if (rc == -1 && errno != EINTR)
- log_error("Listener_thread::run(): select() failed, %s",
- strerror(errno));
+ log_error("Listener: select() failed: %s.",
+ (const char *) strerror(errno));
continue;
}
@@ -191,23 +138,18 @@ void Listener_thread::run()
/* Assuming that rc > 0 as we asked to wait forever */
if (FD_ISSET(sockets[socket_index], &read_fds_arg))
{
-#ifdef __WIN__
- SOCKET client_fd= accept(sockets[socket_index], 0, 0);
- /* accept may return INVALID_SOCKET on failure */
- if (client_fd != INVALID_SOCKET)
- {
-#else
int client_fd= accept(sockets[socket_index], 0, 0);
/* accept may return -1 (failure or spurious wakeup) */
if (client_fd >= 0) // connection established
{
set_no_inherit(client_fd);
-#endif
- Vio *vio= vio_new(client_fd, socket_index == 0 ?
- VIO_TYPE_SOCKET : VIO_TYPE_TCPIP,
- socket_index == 0 ? 1 : 0);
- if (vio != 0)
+ struct st_vio *vio=
+ vio_new(client_fd,
+ socket_index == 0 ? VIO_TYPE_SOCKET : VIO_TYPE_TCPIP,
+ socket_index == 0 ? 1 : 0);
+
+ if (vio != NULL)
handle_new_mysql_connection(vio);
else
{
@@ -221,7 +163,7 @@ void Listener_thread::run()
/* III. Release all resources and exit */
- log_info("Listener_thread::run(): shutdown requested, exiting...");
+ log_info("Listener: shutdown requested, exiting...");
for (i= 0; i < num_sockets; i++)
closesocket(sockets[i]);
@@ -230,8 +172,9 @@ void Listener_thread::run()
unlink(unix_socket_address.sun_path);
#endif
- thread_registry.unregister_thread(&thread_info);
- my_thread_end();
+ thread_registry->unregister_thread(&thread_info);
+
+ log_info("Listener: finished.");
return;
err:
@@ -241,27 +184,22 @@ err:
for (i= 0; i < num_sockets; i++)
closesocket(sockets[i]);
- thread_registry.set_error_status();
- thread_registry.unregister_thread(&thread_info);
- thread_registry.request_shutdown();
- my_thread_end();
+ thread_registry->set_error_status();
+ thread_registry->unregister_thread(&thread_info);
+ thread_registry->request_shutdown();
return;
}
-int Listener_thread::create_tcp_socket()
+int Listener::create_tcp_socket()
{
/* value to be set by setsockopt */
int arg= 1;
-#ifdef __WIN__
- SOCKET ip_socket= socket(AF_INET, SOCK_STREAM, 0);
-#else
int ip_socket= socket(AF_INET, SOCK_STREAM, 0);
-#endif
if (ip_socket == INVALID_SOCKET)
{
- log_error("Listener_thead::run(): socket(AF_INET) failed, %s",
- strerror(errno));
+ log_error("Listener: socket(AF_INET) failed: %s.",
+ (const char *) strerror(errno));
return -1;
}
@@ -269,14 +207,16 @@ int Listener_thread::create_tcp_socket()
bzero(&ip_socket_address, sizeof(ip_socket_address));
ulong im_bind_addr;
- if (options.bind_address != 0)
+ if (Options::Main::bind_address != 0)
{
- if ((im_bind_addr= (ulong) inet_addr(options.bind_address)) == INADDR_NONE)
+ im_bind_addr= (ulong) inet_addr(Options::Main::bind_address);
+
+ if (im_bind_addr == (ulong) INADDR_NONE)
im_bind_addr= htonl(INADDR_ANY);
}
else
im_bind_addr= htonl(INADDR_ANY);
- uint im_port= options.port_number;
+ uint im_port= Options::Main::port_number;
ip_socket_address.sin_family= AF_INET;
ip_socket_address.sin_addr.s_addr= im_bind_addr;
@@ -289,16 +229,16 @@ int Listener_thread::create_tcp_socket()
if (bind(ip_socket, (struct sockaddr *) &ip_socket_address,
sizeof(ip_socket_address)))
{
- log_error("Listener_thread::run(): bind(ip socket) failed, '%s'",
- strerror(errno));
+ log_error("Listener: bind(ip socket) failed: %s.",
+ (const char *) strerror(errno));
closesocket(ip_socket);
return -1;
}
if (listen(ip_socket, LISTEN_BACK_LOG_SIZE))
{
- log_error("Listener_thread::run(): listen(ip socket) failed, %s",
- strerror(errno));
+ log_error("Listener: listen(ip socket) failed: %s.",
+ (const char *) strerror(errno));
closesocket(ip_socket);
return -1;
}
@@ -311,26 +251,27 @@ int Listener_thread::create_tcp_socket()
FD_SET(ip_socket, &read_fds);
sockets[num_sockets++]= ip_socket;
- log_info("accepting connections on ip socket (port: %d)", (int) im_port);
+ log_info("Listener: accepting connections on ip socket (port: %d)...",
+ (int) im_port);
return 0;
}
#ifndef __WIN__
-int Listener_thread::
+int Listener::
create_unix_socket(struct sockaddr_un &unix_socket_address)
{
int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0);
if (unix_socket == INVALID_SOCKET)
{
- log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s",
- strerror(errno));
+ log_error("Listener: socket(AF_UNIX) failed: %s.",
+ (const char *) strerror(errno));
return -1;
}
bzero(&unix_socket_address, sizeof(unix_socket_address));
unix_socket_address.sun_family= AF_UNIX;
- strmake(unix_socket_address.sun_path, options.socket_file_name,
+ strmake(unix_socket_address.sun_path, Options::Main::socket_file_name,
sizeof(unix_socket_address.sun_path));
unlink(unix_socket_address.sun_path); // in case we have stale socket file
@@ -342,9 +283,9 @@ create_unix_socket(struct sockaddr_un &unix_socket_address)
if (bind(unix_socket, (struct sockaddr *) &unix_socket_address,
sizeof(unix_socket_address)))
{
- log_error("Listener_thread::run(): bind(unix socket) failed, "
- "socket file name is '%s', error '%s'",
- unix_socket_address.sun_path, strerror(errno));
+ log_error("Listener: bind(unix socket) failed for '%s': %s.",
+ (const char *) unix_socket_address.sun_path,
+ (const char *) strerror(errno));
close(unix_socket);
return -1;
}
@@ -353,8 +294,8 @@ create_unix_socket(struct sockaddr_un &unix_socket_address)
if (listen(unix_socket, LISTEN_BACK_LOG_SIZE))
{
- log_error("Listener_thread::run(): listen(unix socket) failed, %s",
- strerror(errno));
+ log_error("Listener: listen(unix socket) failed: %s.",
+ (const char *) strerror(errno));
close(unix_socket);
return -1;
}
@@ -365,8 +306,8 @@ create_unix_socket(struct sockaddr_un &unix_socket_address)
/* make sure that instances won't be listening our sockets */
set_no_inherit(unix_socket);
- log_info("accepting connections on unix socket '%s'",
- unix_socket_address.sun_path);
+ log_info("Listener: accepting connections on unix socket '%s'...",
+ (const char *) unix_socket_address.sun_path);
sockets[num_sockets++]= unix_socket;
FD_SET(unix_socket, &read_fds);
return 0;
@@ -376,51 +317,21 @@ create_unix_socket(struct sockaddr_un &unix_socket_address)
/*
Create new mysql connection. Created thread is responsible for deletion of
- the Mysql_connection_thread_args and Vio instances passed to it.
- SYNOPSYS
+ the Mysql_connection and Vio instances passed to it.
+ SYNOPSIS
handle_new_mysql_connection()
*/
-void Listener_thread::handle_new_mysql_connection(Vio *vio)
+void Listener::handle_new_mysql_connection(struct st_vio *vio)
{
- if (Mysql_connection_thread_args *mysql_thread_args=
- new Mysql_connection_thread_args(vio, thread_registry, user_map,
- ++total_connection_count,
- instance_map)
- )
+ Mysql_connection *mysql_connection=
+ new Mysql_connection(thread_registry, user_map,
+ vio, ++total_connection_count);
+ if (mysql_connection == NULL || mysql_connection->start(Thread::DETACHED))
{
- /*
- Initialize thread attributes to create detached thread; it seems
- easier to do it ad-hoc than have a global variable for attributes.
- */
- pthread_t mysql_thd_id;
- pthread_attr_t mysql_thd_attr;
- pthread_attr_init(&mysql_thd_attr);
- pthread_attr_setdetachstate(&mysql_thd_attr, PTHREAD_CREATE_DETACHED);
- if (set_stacksize_n_create_thread(&mysql_thd_id, &mysql_thd_attr,
- mysql_connection, mysql_thread_args))
- {
- delete mysql_thread_args;
- vio_delete(vio);
- log_error("handle_one_mysql_connection():"
- "set_stacksize_n_create_thread(mysql) failed");
- }
- pthread_attr_destroy(&mysql_thd_attr);
- }
- else
+ log_error("Listener: can not start connection handler.");
+ delete mysql_connection;
vio_delete(vio);
+ }
+ /* The connection will delete itself when the thread is finished */
}
-
-
-pthread_handler_t listener(void *arg)
-{
- Listener_thread_args *args= (Listener_thread_args *) arg;
- Listener_thread listener(*args);
- listener.run();
- /*
- args is a stack variable because listener thread lives as long as the
- manager process itself
- */
- return 0;
-}
-
diff --git a/server-tools/instance-manager/listener.h b/server-tools/instance-manager/listener.h
index a42f8b48fe5..964fb361fb5 100644
--- a/server-tools/instance-manager/listener.h
+++ b/server-tools/instance-manager/listener.h
@@ -16,37 +16,46 @@
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H
-#include <my_global.h>
-#include <my_pthread.h>
+#include "thread_registry.h"
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
-
-pthread_handler_t listener(void *arg);
-
class Thread_registry;
-struct Options;
class User_map;
-class Instance_map;
-struct Listener_thread_args
+/**
+ Listener - a thread listening on sockets and spawning
+ connection threads.
+*/
+
+class Listener: public Thread
{
- Thread_registry &thread_registry;
- const Options &options;
- const User_map &user_map;
- Instance_map &instance_map;
-
- Listener_thread_args(Thread_registry &thread_registry_arg,
- const Options &options_arg,
- const User_map &user_map_arg,
- Instance_map &instance_map_arg) :
- thread_registry(thread_registry_arg)
- ,options(options_arg)
- ,user_map(user_map_arg)
- ,instance_map(instance_map_arg)
- {}
+public:
+ Listener(Thread_registry *thread_registry_arg, User_map *user_map_arg);
+
+protected:
+ virtual void run();
+
+private:
+ static const int LISTEN_BACK_LOG_SIZE;
+
+private:
+ Thread_info thread_info;
+ Thread_registry *thread_registry;
+ User_map *user_map;
+
+ ulong total_connection_count;
+
+ int sockets[2];
+ int num_sockets;
+ fd_set read_fds;
+
+private:
+ void handle_new_mysql_connection(struct st_vio *vio);
+ int create_tcp_socket();
+ int create_unix_socket(struct sockaddr_un &unix_socket_address);
};
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H
diff --git a/server-tools/instance-manager/log.cc b/server-tools/instance-manager/log.cc
index b63696e2ce6..9f276523e49 100644
--- a/server-tools/instance-manager/log.cc
+++ b/server-tools/instance-manager/log.cc
@@ -13,14 +13,16 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <my_global.h>
-
#include "log.h"
-#include "portability.h"
-#include <stdarg.h>
+
+#include <my_global.h>
#include <m_string.h>
#include <my_sys.h>
+#include <stdarg.h>
+
+#include "portability.h" /* for vsnprintf() on Windows. */
+
/*
TODO:
- add flexible header support
@@ -30,11 +32,12 @@
/*
Format log entry and write it to the given stream.
- SYNOPSYS
+ SYNOPSIS
log()
*/
-static inline void log(FILE *file, const char *format, va_list args)
+static void log(FILE *file,const char *level_tag, const char *format,
+ va_list args)
{
/*
log() should be thread-safe; it implies that we either call fprintf()
@@ -49,14 +52,17 @@ static inline void log(FILE *file, const char *format, va_list args)
struct tm bd_time; // broken-down time
localtime_r(&now, &bd_time);
- char buff_date[32];
- sprintf(buff_date, "%02d%02d%02d %2d:%02d:%02d\t",
- bd_time.tm_year % 100,
- bd_time.tm_mon + 1,
- bd_time.tm_mday,
- bd_time.tm_hour,
- bd_time.tm_min,
- bd_time.tm_sec);
+ char buff_date[128];
+ sprintf(buff_date, "[%d/%lu] [%02d/%02d/%02d %02d:%02d:%02d] [%s] ",
+ (int) getpid(),
+ (unsigned long) pthread_self(),
+ (int) bd_time.tm_year % 100,
+ (int) bd_time.tm_mon + 1,
+ (int) bd_time.tm_mday,
+ (int) bd_time.tm_hour,
+ (int) bd_time.tm_min,
+ (int) bd_time.tm_sec,
+ (const char *) level_tag);
/* Format the message */
char buff_stack[256];
@@ -70,7 +76,7 @@ static inline void log(FILE *file, const char *format, va_list args)
{
int size= sizeof(buff_stack) * 2;
buff_msg= (char*) my_malloc(size, MYF(0));
- while (true)
+ while (TRUE)
{
if (buff_msg == 0)
{
@@ -104,57 +110,79 @@ static inline void log(FILE *file, const char *format, va_list args)
/* don't fflush() the file: buffering strategy is set in log_init() */
}
+/**************************************************************************
+ Logging: implementation of public interface.
+**************************************************************************/
-void log_error(const char *format, ...)
-{
- va_list args;
- va_start(args, format);
- log(stderr, format, args);
- va_end(args);
-}
+/*
+ The function initializes logging sub-system.
+ SYNOPSIS
+ log_init()
+*/
-void log_info(const char *format, ...)
+void log_init()
{
- va_list args;
- va_start(args, format);
- log(stdout, format, args);
- va_end(args);
+ /*
+ stderr is unbuffered by default; there is no good of line buffering,
+ as all logging is performed linewise - so remove buffering from stdout
+ also
+ */
+ setbuf(stdout, 0);
}
-/* TODO: rewrite with buffering print */
-void print_info(const char *format, ...)
+
+/*
+ The function is intended to log error messages. It precedes a message
+ with date, time and [ERROR] tag and print it to the stderr and stdout.
+
+ We want to print it on stdout to be able to know in which context we got the
+ error
+
+ SYNOPSIS
+ log_error()
+ format [IN] format string
+ ... [IN] arguments to format
+*/
+
+void log_error(const char *format, ...)
{
va_list args;
va_start(args, format);
- vfprintf(stdout, format, args);
+ log(stdout, "ERROR", format, args);
+ fflush(stdout);
+ log(stderr, "ERROR", format, args);
+ fflush(stderr);
va_end(args);
}
-void print_error(const char *format, ...)
+
+/*
+ The function is intended to log information messages. It precedes
+ a message with date, time and [INFO] tag and print it to the stdout.
+
+ SYNOPSIS
+ log_error()
+ format [IN] format string
+ ... [IN] arguments to format
+*/
+
+void log_info(const char *format, ...)
{
va_list args;
va_start(args, format);
- vfprintf(stderr, format, args);
+ log(stdout, "INFO", format, args);
va_end(args);
}
/*
- log_init()
- RETURN VALUE
- 0 ok
- !0 error
-*/
+ The function prints information to the error log and eixt(1).
-void log_init()
-{
- /*
- stderr is unbuffered by default; there is no good of line buffering,
- as all logging is performed linewise - so remove buffering from stdout
- also
- */
- setbuf(stdout, 0);
-}
+ SYNOPSIS
+ die()
+ format [IN] format string
+ ... [IN] arguments to format
+*/
void die(const char *format, ...)
{
diff --git a/server-tools/instance-manager/log.h b/server-tools/instance-manager/log.h
index 139dcd552ee..e6c3b55c54c 100644
--- a/server-tools/instance-manager/log.h
+++ b/server-tools/instance-manager/log.h
@@ -19,20 +19,23 @@
/*
Logging facilities.
- Two logging streams are supported: error log and info log. Additionally
- libdbug may be used for debug information output.
+ Two logging streams are supported: error log and info log.
+ Additionally libdbug may be used for debug information output.
+
ANSI C buffered I/O is used to perform logging.
+
Logging is performed via stdout/stder, so one can reopen them to point to
- ordinary files. To initialize loggin environment log_init() must be called.
+ ordinary files. To initialize logging environment log_init() must be called.
Rationale:
- no MYSQL_LOG as it has BIN mode, and not easy to fetch from sql_class.h
- no constructors/desctructors to make logging available all the time
- Function names are subject to change.
*/
-/* Precede error message with date and time and print it to the stdout */
+void log_init();
+
+
void log_info(const char *format, ...)
#ifdef __GNUC__
__attribute__ ((format(printf, 1, 2)))
@@ -40,7 +43,6 @@ void log_info(const char *format, ...)
;
-/* Precede error message with date and time and print it to the stderr */
void log_error(const char *format, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
@@ -48,30 +50,6 @@ void log_error(const char *format, ...)
;
-/*
- Now this is simple catchouts for printf (no date/time is logged), to be
- able to replace underlying streams in future.
-*/
-
-void print_info(const char *format, ...)
-#ifdef __GNUC__
- __attribute__ ((format (printf, 1, 2)))
-#endif
- ;
-
-
-void print_error(const char *format, ...)
-#ifdef __GNUC__
- __attribute__ ((format (printf, 1, 2)))
-#endif
- ;
-
-/* initialize logs */
-void log_init();
-
-
-/* print information to the error log and eixt(1) */
-
void die(const char *format, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc
index cadb7a4aa0c..792461e41a9 100644
--- a/server-tools/instance-manager/manager.cc
+++ b/server-tools/instance-manager/manager.cc
@@ -13,40 +13,34 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <my_global.h>
#include "manager.h"
-#include "priv.h"
-#include "thread_registry.h"
-#include "listener.h"
-#include "instance_map.h"
-#include "options.h"
-#include "user_map.h"
-#include "log.h"
-#include "guardian.h"
-
-#include <my_sys.h>
+#include <my_global.h>
#include <m_string.h>
-#include <signal.h>
+#include <my_sys.h>
#include <thr_alarm.h>
+
+#include <signal.h>
#ifndef __WIN__
#include <sys/wait.h>
#endif
+#include "exit_codes.h"
+#include "guardian.h"
+#include "instance_map.h"
+#include "listener.h"
+#include "mysql_manager_error.h"
+#include "mysqld_error.h"
+#include "log.h"
+#include "options.h"
+#include "priv.h"
+#include "thread_registry.h"
+#include "user_map.h"
-int create_pid_file(const char *pid_file_name, int pid)
-{
- if (FILE *pid_file= my_fopen(pid_file_name,
- O_WRONLY | O_CREAT | O_BINARY, MYF(0)))
- {
- fprintf(pid_file, "%d\n", (int) pid);
- my_fclose(pid_file, MYF(0));
- return 0;
- }
- log_error("can't create pid file %s: errno=%d, %s",
- pid_file_name, errno, strerror(errno));
- return 1;
-}
+
+/**********************************************************************
+ {{{ Platform-specific implementation.
+**********************************************************************/
#ifndef __WIN__
void set_signals(sigset_t *mask)
@@ -81,14 +75,14 @@ bool have_signal;
void onsignal(int signo)
{
- have_signal= true;
+ have_signal= TRUE;
}
void set_signals(sigset_t *set)
{
signal(SIGINT, onsignal);
signal(SIGTERM, onsignal);
- have_signal= false;
+ have_signal= FALSE;
}
int my_sigwait(const sigset_t *set, int *sig)
@@ -102,65 +96,228 @@ int my_sigwait(const sigset_t *set, int *sig)
#endif
+/**********************************************************************
+ }}}
+**********************************************************************/
+
+
+/**********************************************************************
+ {{{ Implementation of checking the actual thread model.
+***********************************************************************/
+
+namespace { /* no-indent */
+
+class ThreadModelChecker: public Thread
+{
+public:
+ ThreadModelChecker()
+ :main_pid(getpid())
+ { }
+
+public:
+ inline bool is_linux_threads() const
+ {
+ return linux_threads;
+ }
+
+protected:
+ virtual void run()
+ {
+ linux_threads= main_pid != getpid();
+ }
+
+private:
+ pid_t main_pid;
+ bool linux_threads;
+};
+
+bool check_if_linux_threads(bool *linux_threads)
+{
+ ThreadModelChecker checker;
+
+ if (checker.start() || checker.join())
+ return TRUE;
+
+ *linux_threads= checker.is_linux_threads();
+
+ return FALSE;
+}
+
+}
+
+/**********************************************************************
+ }}}
+***********************************************************************/
+
+
+/**********************************************************************
+ Manager implementation
+***********************************************************************/
+
+Guardian *Manager::p_guardian;
+Instance_map *Manager::p_instance_map;
+Thread_registry *Manager::p_thread_registry;
+User_map *Manager::p_user_map;
+
+#ifndef __WIN__
+bool Manager::linux_threads;
+#endif // __WIN__
+
+
+/**
+ Request shutdown of guardian and threads registered in Thread_registry.
+
+ SYNOPSIS
+ stop_all_threads()
+*/
-void stop_all(Guardian_thread *guardian, Thread_registry *registry)
+void Manager::stop_all_threads()
{
/*
- Let guardian thread know that it should break it's processing cycle,
+ Let Guardian thread know that it should break it's processing cycle,
once it wakes up.
*/
- guardian->request_shutdown();
- /* wake guardian */
- pthread_cond_signal(&guardian->COND_guardian);
- /* stop all threads */
- registry->deliver_shutdown();
+ p_guardian->request_shutdown();
+
+ /* Stop all threads. */
+ p_thread_registry->deliver_shutdown();
/* Set error status in the thread registry. */
- registry->set_error_status();
+ p_thread_registry->set_error_status();
}
-/*
- manager - entry point to the main instance manager process: start
- listener thread, write pid file and enter into signal handling.
- See also comments in mysqlmanager.cc to picture general Instance Manager
- architecture.
+
+/**
+ Initialize user map and load password file.
+
+ SYNOPSIS
+ init_user_map()
+
+ RETURN
+ FALSE on success
+ TRUE on failure
*/
-int manager(const Options &options)
+bool Manager::init_user_map(User_map *user_map)
{
- Thread_registry thread_registry;
+ int err_code;
+ const char *err_msg;
+
+ if (user_map->init())
+ {
+ log_error("Manager: can not initialize user list: out of memory.");
+ return TRUE;
+ }
+
+ err_code= user_map->load(Options::Main::password_file_name, &err_msg);
+
+ if (!err_code)
+ return FALSE;
+
+ if (err_code == ERR_PASSWORD_FILE_DOES_NOT_EXIST &&
+ Options::Main::mysqld_safe_compatible)
+ {
+ /*
+ The password file does not exist, but we are running in
+ mysqld_safe-compatible mode. Continue, but complain in log.
+ */
+
+ log_info("Warning: password file does not exist, "
+ "nobody will be able to connect to Instance Manager.");
+
+ return FALSE;
+ }
+
+ log_error("Manager: %s.", (const char *) err_msg);
+
+ return TRUE;
+}
+
+
+/**
+ Main manager function.
+
+ SYNOPSIS
+ main()
+
+ DESCRIPTION
+ This is an entry point to the main instance manager process:
+ start listener thread, write pid file and enter into signal handling.
+ See also comments in mysqlmanager.cc to picture general Instance Manager
+ architecture.
+
+ RETURNS
+ main() returns exit status (exit code).
+*/
+
+int Manager::main()
+{
+ bool shutdown_complete= FALSE;
+ pid_t manager_pid= getpid();
+
+ log_info("Manager: initializing...");
+
+#ifndef __WIN__
+ if (check_if_linux_threads(&linux_threads))
+ {
+ log_error("Manager: can not determine thread model.");
+ return 1;
+ }
+
+ log_info("Manager: detected threads model: %s.",
+ (const char *) (linux_threads ? "LINUX threads" : "POSIX threads"));
+#endif // __WIN__
+
/*
- All objects created in the manager() function live as long as
- thread_registry lives, and thread_registry is alive until there are
- working threads.
+ All objects created in the Manager object live as long as thread_registry
+ lives, and thread_registry is alive until there are working threads.
+
+ There are two main purposes of the Thread Registry:
+ 1. Interrupt blocking I/O and signal condition variables in case of
+ shutdown;
+ 2. Wait for detached threads before shutting down the main thread.
+
+ NOTE:
+ 1. Handling shutdown can be done in more elegant manner by introducing
+ Event (or Condition) object with support of logical operations.
+ 2. Using Thread Registry to wait for detached threads is definitely not
+ the best way, because when Thread Registry unregisters an thread, the
+ thread is still alive. Accurate way to wait for threads to stop is
+ not using detached threads and join all threads before shutdown.
*/
+ Thread_registry thread_registry;
User_map user_map;
- Instance_map instance_map(options.default_mysqld_path);
- Guardian_thread guardian_thread(thread_registry,
- &instance_map,
- options.monitoring_interval);
+ Instance_map instance_map;
+ Guardian guardian(&thread_registry, &instance_map);
- Listener_thread_args listener_args(thread_registry, options, user_map,
- instance_map);
+ Listener listener(&thread_registry, &user_map);
- manager_pid= getpid();
- instance_map.guardian= &guardian_thread;
+ p_instance_map= &instance_map;
+ p_guardian= &guardian;
+ p_thread_registry= &thread_registry;
+ p_user_map= &user_map;
- if (instance_map.init() || user_map.init())
- return 1;
+ /* Initialize instance map. */
- if (user_map.load(options.password_file_name))
+ if (instance_map.init())
+ {
+ log_error("Manager: can not initialize instance list: out of memory.");
return 1;
+ }
- /* write Instance Manager pid file */
+ /* Initialize user db. */
- log_info("IM pid file: '%s'; PID: %d.",
- (const char *) options.pid_file_name,
- (int) manager_pid);
+ if (init_user_map(&user_map))
+ return 1; /* logging has been already done. */
- if (create_pid_file(options.pid_file_name, manager_pid))
- return 1;
+ /* Write Instance Manager pid file. */
+
+ if (create_pid_file(Options::Main::pid_file_name, manager_pid))
+ return 1; /* necessary logging has been already done. */
+
+ log_info("Manager: pid file (%s) created.",
+ (const char *) Options::Main::pid_file_name);
/*
Initialize signals and alarm-infrastructure.
@@ -168,104 +325,94 @@ int manager(const Options &options)
NOTE: To work nicely with LinuxThreads, the signal thread is the first
thread in the process.
- NOTE:
- After init_thr_alarm() call it's possible to call thr_alarm() (from
- different threads), that results in sending ALARM signal to the alarm
- thread (which can be the main thread). That signal can interrupt
- blocking calls.
-
- In other words, a blocking call can be interrupted in the main thread
- after init_thr_alarm().
+ NOTE: After init_thr_alarm() call it's possible to call thr_alarm()
+ (from different threads), that results in sending ALARM signal to the
+ alarm thread (which can be the main thread). That signal can interrupt
+ blocking calls. In other words, a blocking call can be interrupted in
+ the main thread after init_thr_alarm().
*/
sigset_t mask;
set_signals(&mask);
- /* create guardian thread */
- {
- pthread_t guardian_thd_id;
- pthread_attr_t guardian_thd_attr;
- int rc;
-
- /*
- NOTE: Guardian should be shutdown first. Only then all other threads
- need to be stopped. This should be done, as guardian is responsible
- for shutting down the instances, and this is a long operation.
-
- NOTE: Guardian uses thr_alarm() when detects current state of
- instances (is_running()), but it is not interfere with
- flush_instances() later in the code, because until flush_instances()
- complete in the main thread, Guardian thread is not permitted to
- process instances. And before flush_instances() there is no instances
- to proceed.
- */
-
- pthread_attr_init(&guardian_thd_attr);
- pthread_attr_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED);
- rc= set_stacksize_n_create_thread(&guardian_thd_id, &guardian_thd_attr,
- guardian, &guardian_thread);
- pthread_attr_destroy(&guardian_thd_attr);
- if (rc)
- {
- log_error("manager(): set_stacksize_n_create_thread(guardian) failed");
- goto err;
- }
+ /*
+ Create the guardian thread. The newly started thread will block until
+ we actually load instances.
+
+ NOTE: Guardian should be shutdown first. Only then all other threads
+ can be stopped. This should be done in this order because the guardian
+ is responsible for shutting down all the guarded instances, and this
+ is a long operation.
+
+ NOTE: Guardian uses thr_alarm() when detects the current state of an
+ instance (is_running()), but this does not interfere with
+ flush_instances() call later in the code, because until
+ flush_instances() completes in the main thread, Guardian thread is not
+ permitted to process instances. And before flush_instances() has
+ completed, there are no instances to guard.
+ */
+ if (guardian.start(Thread::DETACHED))
+ {
+ log_error("Manager: can not start Guardian thread.");
+ goto err;
}
/* Load instances. */
- int signo;
- bool shutdown_complete;
-
- shutdown_complete= FALSE;
-
- if (instance_map.flush_instances())
+ if (Manager::flush_instances())
{
- log_error("Cannot init instances repository. This might be caused by "
- "the wrong config file options. For instance, missing mysqld "
- "binary. Aborting.");
- stop_all(&guardian_thread, &thread_registry);
+ log_error("Manager: can not init instances repository.");
+ stop_all_threads();
goto err;
}
- /* create the listener */
- {
- pthread_t listener_thd_id;
- pthread_attr_t listener_thd_attr;
- int rc;
-
- pthread_attr_init(&listener_thd_attr);
- pthread_attr_setdetachstate(&listener_thd_attr, PTHREAD_CREATE_DETACHED);
- rc= set_stacksize_n_create_thread(&listener_thd_id, &listener_thd_attr,
- listener, &listener_args);
- pthread_attr_destroy(&listener_thd_attr);
- if (rc)
- {
- log_error("manager(): set_stacksize_n_create_thread(listener) failed");
- stop_all(&guardian_thread, &thread_registry);
- goto err;
- }
+ /* Initialize the Listener. */
+ if (listener.start(Thread::DETACHED))
+ {
+ log_error("Manager: can not start Listener thread.");
+ stop_all_threads();
+ goto err;
}
/*
After the list of guarded instances have been initialized,
Guardian should start them.
*/
- pthread_cond_signal(&guardian_thread.COND_guardian);
+
+ guardian.ping();
+
+ /* Main loop. */
+
+ log_info("Manager: started.");
while (!shutdown_complete)
{
+ int signo;
int status= 0;
if ((status= my_sigwait(&mask, &signo)) != 0)
{
- log_error("sigwait() failed");
- stop_all(&guardian_thread, &thread_registry);
+ log_error("Manager: sigwait() failed");
+ stop_all_threads();
goto err;
}
+ /*
+ The general idea in this loop is the following:
+ - we are waiting for SIGINT, SIGTERM -- signals that mean we should
+ shutdown;
+ - as shutdown signal is caught, we stop Guardian thread (by calling
+ Guardian::request_shutdown());
+ - as Guardian is stopped, it sends SIGTERM to this thread
+ (by calling Thread_registry::request_shutdown()), so that the
+ my_sigwait() above returns;
+ - as we catch the second SIGTERM, we send signals to all threads
+ registered in Thread_registry (by calling
+ Thread_registry::deliver_shutdown()) and waiting for threads to stop;
+ */
+
#ifndef __WIN__
/*
On some Darwin kernels SIGHUP is delivered along with most
@@ -274,7 +421,7 @@ int manager(const Options &options)
Bug #14164 IM tests fail on MacOS X (powermacg5)
*/
#ifdef IGNORE_SIGHUP_SIGQUIT
- if ( SIGHUP == signo )
+ if (SIGHUP == signo)
continue;
#endif
if (THR_SERVER_ALARM == signo)
@@ -282,10 +429,11 @@ int manager(const Options &options)
else
#endif
{
- if (!guardian_thread.is_stopped())
+ log_info("Manager: got shutdown signal.");
+
+ if (!guardian.is_stopped())
{
- guardian_thread.request_shutdown();
- pthread_cond_signal(&guardian_thread.COND_guardian);
+ guardian.request_shutdown();
}
else
{
@@ -295,15 +443,84 @@ int manager(const Options &options)
}
}
+ log_info("Manager: finished.");
+
err:
/* delete the pid file */
- my_delete(options.pid_file_name, MYF(0));
+ my_delete(Options::Main::pid_file_name, MYF(0));
#ifndef __WIN__
/* free alarm structures */
end_thr_alarm(1);
- /* don't pthread_exit to kill all threads who did not shut down in time */
#endif
return thread_registry.get_error_status() ? 1 : 0;
}
+
+
+/**
+ Re-read instance configuration file.
+
+ SYNOPSIS
+ flush_instances()
+
+ DESCRIPTION
+ This function will:
+ - clear the current list of instances. This removes both
+ running and stopped instances.
+ - load a new instance configuration from the file.
+ - pass on the new map to the guardian thread: it will start
+ all instances that are marked `guarded' and not yet started.
+
+ Note, as the check whether an instance is started is currently
+ very simple (returns TRUE if there is a MySQL server running
+ at the given port), this function has some peculiar
+ side-effects:
+ * if the port number of a running instance was changed, the
+ old instance is forgotten, even if it was running. The new
+ instance will be started at the new port.
+ * if the configuration was changed in a way that two
+ instances swapped their port numbers, the guardian thread
+ will not notice that and simply report that both instances
+ are configured successfully and running.
+
+ In order to avoid such side effects one should never call
+ FLUSH INSTANCES without prior stop of all running instances.
+
+ RETURN
+ 0 On success
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+ ER_THERE_IS_ACTIVE_INSTACE If there is an active instance
+*/
+
+int Manager::flush_instances()
+{
+ p_instance_map->lock();
+
+ if (p_instance_map->is_there_active_instance())
+ {
+ p_instance_map->unlock();
+ return ER_THERE_IS_ACTIVE_INSTACE;
+ }
+
+ if (p_instance_map->reset())
+ {
+ p_instance_map->unlock();
+ return ER_OUT_OF_RESOURCES;
+ }
+
+ if (p_instance_map->load())
+ {
+ p_instance_map->unlock();
+
+ /* Don't init guardian if we failed to load instances. */
+ return ER_OUT_OF_RESOURCES;
+ }
+
+ get_guardian()->init();
+ get_guardian()->ping();
+
+ p_instance_map->unlock();
+
+ return 0;
+}
diff --git a/server-tools/instance-manager/manager.h b/server-tools/instance-manager/manager.h
index 22af0d39115..e6956884603 100644
--- a/server-tools/instance-manager/manager.h
+++ b/server-tools/instance-manager/manager.h
@@ -16,10 +16,56 @@
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H
-struct Options;
+#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
+#pragma interface
+#endif
-int manager(const Options &options);
+#include <my_global.h>
-int create_pid_file(const char *pid_file_name, int pid);
+class Guardian;
+class Instance_map;
+class Thread_registry;
+class User_map;
+
+class Manager
+{
+public:
+ static int main();
+
+ static int flush_instances();
+
+public:
+ /**
+ These methods return a non-NULL value only for the duration
+ of main().
+ */
+ static Instance_map *get_instance_map() { return p_instance_map; }
+ static Guardian *get_guardian() { return p_guardian; }
+ static Thread_registry *get_thread_registry() { return p_thread_registry; }
+ static User_map *get_user_map() { return p_user_map; }
+
+public:
+#ifndef __WIN__
+ static bool is_linux_threads() { return linux_threads; }
+#endif // __WIN__
+
+private:
+ static void stop_all_threads();
+ static bool init_user_map(User_map *user_map);
+
+private:
+ static Guardian *p_guardian;
+ static Instance_map *p_instance_map;
+ static Thread_registry *p_thread_registry;
+ static User_map *p_user_map;
+
+#ifndef __WIN__
+ /*
+ This flag is set if Instance Manager is running on the system using
+ LinuxThreads.
+ */
+ static bool linux_threads;
+#endif // __WIN__
+};
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H
diff --git a/server-tools/instance-manager/messages.cc b/server-tools/instance-manager/messages.cc
index a1a45b05f7c..201ebfd62fc 100644
--- a/server-tools/instance-manager/messages.cc
+++ b/server-tools/instance-manager/messages.cc
@@ -13,15 +13,14 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <my_global.h>
#include "messages.h"
+#include <my_global.h>
+#include <mysql_com.h>
+
#include "mysqld_error.h"
#include "mysql_manager_error.h"
-#include <mysql_com.h>
-#include <assert.h>
-
static const char *mysqld_error_message(unsigned sql_errno)
{
@@ -45,7 +44,7 @@ static const char *mysqld_error_message(unsigned sql_errno)
" corresponds to your MySQL Instance Manager version for the right"
" syntax to use";
case ER_BAD_INSTANCE_NAME:
- return "Bad instance name. Check that the instance with such a name exists";
+ return "Unknown instance name";
case ER_INSTANCE_IS_NOT_STARTED:
return "Cannot stop instance. Perhaps the instance is not started, or was"
" started manually, so IM cannot find the pidfile.";
@@ -69,6 +68,23 @@ static const char *mysqld_error_message(unsigned sql_errno)
" in the instance options";
case ER_ACCESS_OPTION_FILE:
return "Cannot open the option file to edit. Check permissions";
+ case ER_DROP_ACTIVE_INSTANCE:
+ return "Cannot drop an active instance. You should stop it first";
+ case ER_CREATE_EXISTING_INSTANCE:
+ return "Instance already exists";
+ case ER_INSTANCE_MISCONFIGURED:
+ return "Instance is misconfigured. Cannot start it";
+ case ER_MALFORMED_INSTANCE_NAME:
+ return "Malformed instance name.";
+ case ER_INSTANCE_IS_ACTIVE:
+ return "The instance is active. Stop the instance first";
+ case ER_THERE_IS_ACTIVE_INSTACE:
+ return "At least one instance is active. Stop all instances first";
+ case ER_INCOMPATIBLE_OPTION:
+ return "Instance Manager-specific options are prohibited from being used "
+ "in the configuration of mysqld-compatible instances";
+ case ER_CONF_FILE_DOES_NOT_EXIST:
+ return "Configuration file does not exist";
default:
DBUG_ASSERT(0);
return 0;
diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc
index 1803108c39d..bf08f963aa3 100644
--- a/server-tools/instance-manager/mysql_connection.cc
+++ b/server-tools/instance-manager/mysql_connection.cc
@@ -19,84 +19,34 @@
#include "mysql_connection.h"
-#include "priv.h"
-#include "mysql_manager_error.h"
-#include "mysqld_error.h"
-#include "thread_registry.h"
-#include "log.h"
-#include "user_map.h"
-#include "protocol.h"
-#include "messages.h"
-#include "command.h"
-#include "parse.h"
-
-#include <mysql.h>
-#include <violite.h>
-#include <mysql_com.h>
#include <m_string.h>
+#include <m_string.h>
+#include <my_global.h>
+#include <mysql.h>
#include <my_sys.h>
+#include <violite.h>
+#include "command.h"
+#include "log.h"
+#include "messages.h"
+#include "mysqld_error.h"
+#include "mysql_manager_error.h"
+#include "parse.h"
+#include "priv.h"
+#include "protocol.h"
+#include "thread_registry.h"
+#include "user_map.h"
-Mysql_connection_thread_args::Mysql_connection_thread_args(
- struct st_vio *vio_arg,
- Thread_registry &thread_registry_arg,
- const User_map &user_map_arg,
- ulong connection_id_arg,
- Instance_map &instance_map_arg) :
- vio(vio_arg)
- ,thread_registry(thread_registry_arg)
- ,user_map(user_map_arg)
- ,connection_id(connection_id_arg)
- ,instance_map(instance_map_arg)
- {}
-
-/*
- MySQL connection - handle one connection with mysql command line client
- See also comments in mysqlmanager.cc to picture general Instance Manager
- architecture.
- We use conventional technique to work with classes without exceptions:
- class acquires all vital resource in init(); Thus if init() succeed,
- a user must call cleanup(). All other methods are valid only between
- init() and cleanup().
-*/
-class Mysql_connection_thread: public Mysql_connection_thread_args
-{
-public:
- Mysql_connection_thread(const Mysql_connection_thread_args &args);
-
- int init();
- void cleanup();
-
- void run();
-
- ~Mysql_connection_thread();
-private:
- Thread_info thread_info;
- NET net;
- struct rand_struct rand_st;
- char scramble[SCRAMBLE_LENGTH + 1];
- uint status;
- ulong client_capabilities;
-private:
- /* Names are conventionally the same as in mysqld */
- int check_connection();
- int do_command();
- int dispatch_command(enum enum_server_command command,
- const char *text, uint len);
-};
-
-
-Mysql_connection_thread::Mysql_connection_thread(
- const Mysql_connection_thread_args &args) :
- Mysql_connection_thread_args(args.vio,
- args.thread_registry,
- args.user_map,
- args.connection_id,
- args.instance_map)
- ,thread_info(pthread_self())
+Mysql_connection::Mysql_connection(Thread_registry *thread_registry_arg,
+ User_map *user_map_arg,
+ struct st_vio *vio_arg, ulong
+ connection_id_arg)
+ :vio(vio_arg),
+ connection_id(connection_id_arg),
+ thread_registry(thread_registry_arg),
+ user_map(user_map_arg)
{
- thread_registry.register_thread(&thread_info);
}
@@ -126,83 +76,87 @@ C_MODE_END
This function is complementary to cleanup().
*/
-int Mysql_connection_thread::init()
+bool Mysql_connection::init()
{
/* Allocate buffers for network I/O */
if (my_net_init(&net, vio))
- return 1;
+ return TRUE;
+
net.return_status= &status;
+
/* Initialize random number generator */
{
ulong seed1= (ulong) &rand_st + rand();
- ulong seed2= rand() + (ulong) time(0);
+ ulong seed2= (ulong) rand() + (ulong) time(0);
randominit(&rand_st, seed1, seed2);
}
+
/* Fill scramble - server's random message used for handshake */
create_random_string(scramble, SCRAMBLE_LENGTH, &rand_st);
+
/* We don't support transactions, every query is atomic */
status= SERVER_STATUS_AUTOCOMMIT;
- return 0;
+
+ thread_registry->register_thread(&thread_info);
+
+ return FALSE;
}
-void Mysql_connection_thread::cleanup()
+void Mysql_connection::cleanup()
{
net_end(&net);
+ thread_registry->unregister_thread(&thread_info);
}
-Mysql_connection_thread::~Mysql_connection_thread()
+Mysql_connection::~Mysql_connection()
{
/* vio_delete closes the socket if necessary */
vio_delete(vio);
- thread_registry.unregister_thread(&thread_info);
}
-void Mysql_connection_thread::run()
+void Mysql_connection::main()
{
- log_info("accepted mysql connection %lu", connection_id);
-
- my_thread_init();
+ log_info("Connection %lu: accepted.", (unsigned long) connection_id);
if (check_connection())
{
- my_thread_end();
+ log_info("Connection %lu: failed to authorize the user.",
+ (unsigned long) connection_id);
+
return;
}
- log_info("connection %lu is checked successfully", connection_id);
+ log_info("Connection %lu: the user was authorized successfully.",
+ (unsigned long) connection_id);
vio_keepalive(vio, TRUE);
- while (!net.error && net.vio && !thread_registry.is_shutdown())
+ while (!net.error && net.vio && !thread_registry->is_shutdown())
{
if (do_command())
break;
}
-
- my_thread_end();
}
-int Mysql_connection_thread::check_connection()
+int Mysql_connection::check_connection()
{
ulong pkt_len=0; // to hold client reply length
- /* maximum size of the version string */
- enum { MAX_VERSION_LENGTH= 80 };
/* buffer for the first packet */ /* packet contains: */
- char buff[MAX_VERSION_LENGTH + 1 + // server version, 0-ended
- 4 + // connection id
- SCRAMBLE_LENGTH + 2 + // scramble (in 2 pieces)
- 18]; // server variables: flags,
+ uchar buff[MAX_VERSION_LENGTH + 1 + // server version, 0-ended
+ 4 + // connection id
+ SCRAMBLE_LENGTH + 2 + // scramble (in 2 pieces)
+ 18]; // server variables: flags,
// charset number, status,
- char *pos= buff;
+ uchar *pos= buff;
ulong server_flags;
- memcpy(pos, mysqlmanager_version, mysqlmanager_version_length + 1);
- pos+= mysqlmanager_version_length + 1;
+ memcpy(pos, mysqlmanager_version.str, mysqlmanager_version.length + 1);
+ pos+= mysqlmanager_version.length + 1;
int4store((uchar*) pos, connection_id);
pos+= 4;
@@ -241,7 +195,8 @@ int Mysql_connection_thread::check_connection()
/* write connection message and read reply */
enum { MIN_HANDSHAKE_SIZE= 2 };
- if (net_write_command(&net, protocol_version, "", 0, buff, (uint) (pos - buff)) ||
+ if (net_write_command(&net, protocol_version, (uchar*) "", 0,
+ buff, pos - buff) ||
(pkt_len= my_net_read(&net)) == packet_error ||
pkt_len < MIN_HANDSHAKE_SIZE)
{
@@ -257,25 +212,27 @@ int Mysql_connection_thread::check_connection()
}
client_capabilities|= ((ulong) uint2korr(net.read_pos + 2)) << 16;
- pos= (char*) net.read_pos + 32;
+ pos= net.read_pos + 32;
/* At least one byte for username and one byte for password */
- if (pos >= (char*) net.read_pos + pkt_len + 2)
+ if (pos >= net.read_pos + pkt_len + 2)
{
/*TODO add user and password handling in error messages*/
net_send_error(&net, ER_HANDSHAKE_ERROR);
return 1;
}
- const char *user= pos;
+ const char *user= (char*) pos;
const char *password= strend(user)+1;
ulong password_len= *password++;
+ LEX_STRING user_name= { (char *) user, password - user - 2 };
+
if (password_len != SCRAMBLE_LENGTH)
{
net_send_error(&net, ER_ACCESS_DENIED_ERROR);
return 1;
}
- if (user_map.authenticate(user, (uint) (password - user - 2), password, scramble))
+ if (user_map->authenticate(&user_name, password, scramble))
{
net_send_error(&net, ER_ACCESS_DENIED_ERROR);
return 1;
@@ -285,7 +242,7 @@ int Mysql_connection_thread::check_connection()
}
-int Mysql_connection_thread::do_command()
+int Mysql_connection::do_command()
{
char *packet;
ulong packet_length;
@@ -298,7 +255,7 @@ int Mysql_connection_thread::do_command()
/* Check if we can continue without closing the connection */
if (net.error != 3) // what is 3 - find out
return 1;
- if (thread_registry.is_shutdown())
+ if (thread_registry->is_shutdown())
return 1;
net_send_error(&net, net.last_errno);
net.error= 0;
@@ -306,78 +263,102 @@ int Mysql_connection_thread::do_command()
}
else
{
- if (thread_registry.is_shutdown())
+ if (thread_registry->is_shutdown())
return 1;
packet= (char*) net.read_pos;
enum enum_server_command command= (enum enum_server_command)
(uchar) *packet;
- log_info("connection %lu: packet_length=%lu, command=%d",
- connection_id, packet_length, command);
- return dispatch_command(command, packet + 1, packet_length - 1);
+ log_info("Connection %lu: received packet (length: %lu; command: %d).",
+ (unsigned long) connection_id,
+ (unsigned long) packet_length,
+ (int) command);
+
+ return dispatch_command(command, packet + 1);
}
}
-int Mysql_connection_thread::dispatch_command(enum enum_server_command command,
- const char *packet, uint len)
+int Mysql_connection::dispatch_command(enum enum_server_command command,
+ const char *packet)
{
switch (command) {
case COM_QUIT: // client exit
- log_info("query for connection %lu received quit command", connection_id);
+ log_info("Connection %lu: received QUIT command.",
+ (unsigned long) connection_id);
return 1;
+
case COM_PING:
- log_info("query for connection %lu received ping command", connection_id);
+ log_info("Connection %lu: received PING command.",
+ (unsigned long) connection_id);
net_send_ok(&net, connection_id, NULL);
- break;
+ return 0;
+
case COM_QUERY:
{
- log_info("query for connection %lu : ----\n%s\n-------------------------",
- connection_id,packet);
- if (Command *com= parse_command(&instance_map, packet))
+ log_info("Connection %lu: received QUERY command: '%s'.",
+ (unsigned long) connection_id,
+ (const char *) packet);
+
+ if (Command *com= parse_command(packet))
{
int res= 0;
- log_info("query for connection %lu successefully parsed",connection_id);
+
+ log_info("Connection %lu: query parsed successfully.",
+ (unsigned long) connection_id);
+
res= com->execute(&net, connection_id);
delete com;
if (!res)
- log_info("query for connection %lu executed ok",connection_id);
+ {
+ log_info("Connection %lu: query executed successfully",
+ (unsigned long) connection_id);
+ }
else
{
- log_info("query for connection %lu executed err=%d",connection_id,res);
+ log_info("Connection %lu: can not execute query (error: %d).",
+ (unsigned long) connection_id,
+ (int) res);
+
net_send_error(&net, res);
- return 0;
}
}
else
{
+ log_error("Connection %lu: can not parse query: out ot resources.",
+ (unsigned long) connection_id);
+
net_send_error(&net,ER_OUT_OF_RESOURCES);
- return 0;
}
- break;
+
+ return 0;
}
+
default:
- log_info("query for connection %lu received unknown command",connection_id);
+ log_info("Connection %lu: received unsupported command (%d).",
+ (unsigned long) connection_id,
+ (int) command);
+
net_send_error(&net, ER_UNKNOWN_COM_ERROR);
- break;
+ return 0;
}
- return 0;
+
+ return 0; /* Just to make compiler happy. */
}
-pthread_handler_t mysql_connection(void *arg)
+void Mysql_connection::run()
{
- Mysql_connection_thread_args *args= (Mysql_connection_thread_args *) arg;
- Mysql_connection_thread mysql_connection_thread(*args);
- delete args;
- if (mysql_connection_thread.init())
- log_info("mysql_connection(): error initializing thread");
+ if (init())
+ log_error("Connection %lu: can not init handler.",
+ (unsigned long) connection_id);
else
{
- mysql_connection_thread.run();
- mysql_connection_thread.cleanup();
+ main();
+ cleanup();
}
- return 0;
+
+ delete this;
}
/*
- vim: fdm=marker
+ vim: fdm=marker
*/
diff --git a/server-tools/instance-manager/mysql_connection.h b/server-tools/instance-manager/mysql_connection.h
index 6f5836af4c7..56bbf76e146 100644
--- a/server-tools/instance-manager/mysql_connection.h
+++ b/server-tools/instance-manager/mysql_connection.h
@@ -16,33 +16,59 @@
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_CONNECTION_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_CONNECTION_H
-#include <my_global.h>
-#include <my_pthread.h>
+#include "thread_registry.h"
+#include <mysql_com.h>
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
-pthread_handler_t mysql_connection(void *arg);
-
-class Thread_registry;
-class User_map;
-class Instance_map;
struct st_vio;
+class User_map;
-struct Mysql_connection_thread_args
+/*
+ MySQL connection - handle one connection with mysql command line client
+ See also comments in mysqlmanager.cc to picture general Instance Manager
+ architecture.
+ We use conventional technique to work with classes without exceptions:
+ class acquires all vital resource in init(); Thus if init() succeed,
+ a user must call cleanup(). All other methods are valid only between
+ init() and cleanup().
+*/
+
+class Mysql_connection: public Thread
{
+public:
+ Mysql_connection(Thread_registry *thread_registry_arg,
+ User_map *user_map_arg,
+ struct st_vio *vio_arg,
+ ulong connection_id_arg);
+ virtual ~Mysql_connection();
+
+protected:
+ virtual void run();
+
+private:
struct st_vio *vio;
- Thread_registry &thread_registry;
- const User_map &user_map;
ulong connection_id;
- Instance_map &instance_map;
+ Thread_info thread_info;
+ Thread_registry *thread_registry;
+ User_map *user_map;
+ NET net;
+ struct rand_struct rand_st;
+ char scramble[SCRAMBLE_LENGTH + 1];
+ uint status;
+ ulong client_capabilities;
+private:
+ /* The main loop implementation triad */
+ bool init();
+ void main();
+ void cleanup();
- Mysql_connection_thread_args(struct st_vio *vio_arg,
- Thread_registry &thread_registry_arg,
- const User_map &user_map_arg,
- ulong connection_id_arg,
- Instance_map &instance_map_arg);
+ /* Names are conventionally the same as in mysqld */
+ int check_connection();
+ int do_command();
+ int dispatch_command(enum enum_server_command command, const char *text);
};
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_CONNECTION_H
diff --git a/server-tools/instance-manager/mysql_manager_error.h b/server-tools/instance-manager/mysql_manager_error.h
index 930ad5ed6fa..e50e5d24f6d 100644
--- a/server-tools/instance-manager/mysql_manager_error.h
+++ b/server-tools/instance-manager/mysql_manager_error.h
@@ -28,5 +28,13 @@
#define ER_ACCESS_OPTION_FILE 3008
#define ER_OFFSET_ERROR 3009
#define ER_READ_FILE 3010
+#define ER_DROP_ACTIVE_INSTANCE 3011
+#define ER_CREATE_EXISTING_INSTANCE 3012
+#define ER_INSTANCE_MISCONFIGURED 3013
+#define ER_MALFORMED_INSTANCE_NAME 3014
+#define ER_INSTANCE_IS_ACTIVE 3015
+#define ER_THERE_IS_ACTIVE_INSTACE 3016
+#define ER_INCOMPATIBLE_OPTION 3017
+#define ER_CONF_FILE_DOES_NOT_EXIST 3018
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H */
diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc
index 5a442e2e49a..276d1ca3b49 100644
--- a/server-tools/instance-manager/mysqlmanager.cc
+++ b/server-tools/instance-manager/mysqlmanager.cc
@@ -14,117 +14,145 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
-#include "manager.h"
-
-#include "options.h"
-#include "log.h"
-
+#include <my_dir.h>
#include <my_sys.h>
+
#include <string.h>
-#include <signal.h>
+
#ifndef __WIN__
#include <pwd.h>
#include <grp.h>
-#include <sys/wait.h>
#endif
-#include <sys/types.h>
-#include <sys/stat.h>
+
+#include "angel.h"
+#include "log.h"
+#include "manager.h"
+#include "options.h"
+#include "user_management_commands.h"
+
#ifdef __WIN__
-#include "windowsservice.h"
+#include "IMService.h"
#endif
+
/*
- Few notes about Instance Manager architecture:
- Instance Manager consisits of two processes: the angel process, and the
- instance manager process. Responsibilities of the angel process is to
- monitor the instance manager process, and restart it in case of
- failure/shutdown. The angel process is started only if startup option
- '--run-as-service' is provided.
- The Instance Manager process consists of several
- subsystems (thread sets):
- - the signal handling thread: it's responsibilities are to handle
- user signals and propogate them to the other threads. All other threads
- are accounted in the signal handler thread Thread Registry.
- - the listener: listens all sockets. There is a listening
- socket for each (mysql, http, snmp, rendezvous (?)) subsystem.
- - mysql subsystem: Instance Manager acts like an ordinary MySQL Server,
- but with very restricted command set. Each MySQL client connection is
- handled in a separate thread. All MySQL client connections threads
- constitute mysql subsystem.
- - http subsystem: it is also possible to talk with Instance Manager via
- http. One thread per http connection is used. Threads are pooled.
- - 'snmp' connections (FIXME: I know nothing about it yet)
- - rendezvous threads
+ Instance Manager consists of two processes: the angel process (IM-angel),
+ and the manager process (IM-main). Responsibilities of IM-angel is to
+ monitor IM-main, and restart it in case of failure/shutdown. IM-angel is
+ started only if startup option '--run-as-service' is provided.
+
+ IM-main consists of several subsystems (thread sets):
+
+ - the signal handling thread
+
+ The signal thread handles user signals and propagates them to the
+ other threads. All other threads are accounted in the signal handler
+ thread Thread Registry.
+
+ - the listener
+
+ The listener listens to all sockets. There is a listening socket for
+ each subsystem (TCP/IP, UNIX socket).
+
+ - mysql subsystem
+
+ Instance Manager acts like an ordinary MySQL Server, but with very
+ restricted command set. Each MySQL client connection is handled in a
+ separate thread. All MySQL client connections threads constitute
+ mysql subsystem.
*/
-static void init_environment(char *progname);
+static int main_impl(int argc, char *argv[]);
+
#ifndef __WIN__
-static void daemonize(const char *log_file_name);
-static void angel(const Options &options);
-static struct passwd *check_user(const char *user);
-static int set_user(const char *user, struct passwd *user_info);
-#else
-int HandleServiceOptions(Options options);
+static struct passwd *check_user();
+static bool switch_user();
#endif
-/*
- main, entry point
- - init environment
- - handle options
- - daemonize and run angel process (if necessary)
- - run manager process
-*/
+/************************************************************************/
+/**
+ The entry point.
+*************************************************************************/
int main(int argc, char *argv[])
{
- int return_value= 1;
- init_environment(argv[0]);
- Options options;
+ int return_value;
- if (options.load(argc, argv))
- goto err;
+ puts("\n"
+ "WARNING: This program is deprecated and will be removed in 6.0.\n");
-#ifndef __WIN__
- struct passwd *user_info;
+ /* Initialize. */
- if ((user_info= check_user(options.user)))
- {
- if (set_user(options.user, user_info))
- goto err;
- }
+ MY_INIT(argv[0]);
+ log_init();
+ umask(0117);
+ srand((uint) time(0));
- if (options.run_as_service)
- {
- /* forks, and returns only in child */
- daemonize(options.log_file_name);
- /* forks again, and returns only in child: parent becomes angel */
- angel(options);
- }
-#else
- if (!options.stand_alone)
- {
- if (HandleServiceOptions(options))
- goto err;
- }
- else
-#endif
+ /* Main function. */
- return_value= manager(options);
+ log_info("IM: started.");
-err:
- options.cleanup();
+ return_value= main_impl(argc, argv);
+
+ log_info("IM: finished.");
+
+ /* Cleanup. */
+
+ Options::cleanup();
my_end(0);
+
return return_value;
}
-/******************* Auxilary functions implementation **********************/
+
+/************************************************************************/
+/**
+ Instance Manager main functionality.
+*************************************************************************/
+
+int main_impl(int argc, char *argv[])
+{
+ int rc;
+
+ if ((rc= Options::load(argc, argv)))
+ return rc;
+
+ if (Options::User_management::cmd)
+ return Options::User_management::cmd->execute();
+
+#ifndef __WIN__
+
+ if (switch_user())
+ return 1;
+
+ return Options::Daemon::run_as_service ?
+ Angel::main() :
+ Manager::main();
+
+#else
+
+ return Options::Service::stand_alone ?
+ Manager::main() :
+ IMService::main();
+
+#endif
+}
+
+/**************************************************************************
+ OS-specific functions implementation.
+**************************************************************************/
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
-/* Change to run as another user if started with --user */
-static struct passwd *check_user(const char *user)
+/************************************************************************/
+/**
+ Change to run as another user if started with --user.
+*************************************************************************/
+
+static struct passwd *check_user()
{
+ const char *user= Options::Daemon::user;
struct passwd *user_info;
uid_t user_id= geteuid();
@@ -164,208 +192,41 @@ static struct passwd *check_user(const char *user)
return user_info;
err:
- log_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n", user);
+ log_error("Can not start under user '%s'.",
+ (const char *) user);
return NULL;
}
-static int set_user(const char *user, struct passwd *user_info)
+
+/************************************************************************/
+/**
+ Switch user.
+*************************************************************************/
+
+static bool switch_user()
{
- DBUG_ASSERT(user_info);
+ struct passwd *user_info= check_user();
+
+ if (!user_info)
+ return FALSE;
+
#ifdef HAVE_INITGROUPS
- initgroups((char*) user,user_info->pw_gid);
+ initgroups(Options::Daemon::user, user_info->pw_gid);
#endif
+
if (setgid(user_info->pw_gid) == -1)
{
log_error("setgid() failed");
- return 1;
+ return TRUE;
}
+
if (setuid(user_info->pw_uid) == -1)
{
log_error("setuid() failed");
- return 1;
- }
- return 0;
-}
-#endif
-
-
-/*
- Init environment, common for daemon and non-daemon
-*/
-
-static void init_environment(char *progname)
-{
- MY_INIT(progname);
- log_init();
- umask(0117);
- srand((uint) time(0));
-}
-
-
-#ifndef __WIN__
-/*
- Become a UNIX service
- SYNOPSYS
- daemonize()
-*/
-
-static void daemonize(const char *log_file_name)
-{
- pid_t pid= fork();
- switch (pid) {
- case -1: // parent, fork error
- die("daemonize(): fork failed, %s", strerror(errno));
- case 0: // child, fork ok
- int fd;
- /*
- Become a session leader: setsid must succeed because child is
- guaranteed not to be a process group leader (it belongs to the
- process group of the parent.)
- The goal is not to have a controlling terminal.
- */
- setsid();
- /*
- As we now don't have a controlling terminal we will not receive
- tty-related signals - no need to ignore them.
- */
-
- close(STDIN_FILENO);
-
- fd= open(log_file_name, O_WRONLY | O_CREAT | O_APPEND | O_NOCTTY,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
- if (fd < 0)
- die("daemonize(): failed to open log file %s, %s", log_file_name,
- strerror(errno));
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- if (fd != STDOUT_FILENO && fd != STDERR_FILENO)
- close(fd);
-
- /* TODO: chroot() and/or chdir() here */
- break;
- default:
- /* successfully exit from parent */
- exit(0);
- }
-}
-
-
-enum { CHILD_OK= 0, CHILD_NEED_RESPAWN, CHILD_EXIT_ANGEL };
-
-static volatile sig_atomic_t child_status= CHILD_OK;
-static volatile sig_atomic_t child_exit_code= 0;
-
-/*
- Signal handler for SIGCHLD: reap child, analyze child exit code, and set
- child_status appropriately.
-*/
-
-void reap_child(int __attribute__((unused)) signo)
-{
- /* NOTE: As we have only one child, no need to cycle waitpid(). */
-
- int exit_code;
-
- if (waitpid(0, &exit_code, WNOHANG) > 0)
- {
- child_exit_code= exit_code;
- child_status= exit_code ? CHILD_NEED_RESPAWN : CHILD_EXIT_ANGEL;
+ return TRUE;
}
-}
-
-static volatile sig_atomic_t is_terminated= 0;
-
-/*
- Signal handler for terminate signals - SIGTERM, SIGHUP, SIGINT.
- Set termination status and return.
- (q) do we need to handle SIGQUIT?
-*/
-
-void terminate(int signo)
-{
- is_terminated= signo;
-}
-
-/*
- Fork a child and monitor it.
- User can explicitly kill the angel process with SIGTERM/SIGHUP/SIGINT.
- Angel process will exit silently if mysqlmanager exits normally.
-*/
-
-static void angel(const Options &options)
-{
- /* install signal handlers */
- sigset_t zeromask; // to sigsuspend in parent
- struct sigaction sa_chld, sa_term;
- struct sigaction sa_chld_out, sa_term_out, sa_int_out, sa_hup_out;
-
- sigemptyset(&zeromask);
- sigemptyset(&sa_chld.sa_mask);
- sigemptyset(&sa_term.sa_mask);
-
- sa_chld.sa_handler= reap_child;
- sa_chld.sa_flags= SA_NOCLDSTOP;
- sa_term.sa_handler= terminate;
- sa_term.sa_flags= 0;
-
- /* sigaction can fail only on wrong arguments */
- sigaction(SIGCHLD, &sa_chld, &sa_chld_out);
- sigaction(SIGTERM, &sa_term, &sa_term_out);
- sigaction(SIGINT, &sa_term, &sa_int_out);
- sigaction(SIGHUP, &sa_term, &sa_hup_out);
-
- /* spawn a child */
-spawn:
- pid_t pid= fork();
- switch (pid) {
- case -1:
- die("angel(): fork failed, %s", strerror(errno));
- case 0: // child, success
- /*
- restore default actions for signals to let the manager work with
- signals as he wishes
- */
- sigaction(SIGCHLD, &sa_chld_out, 0);
- sigaction(SIGTERM, &sa_term_out, 0);
- sigaction(SIGINT, &sa_int_out, 0);
- sigaction(SIGHUP, &sa_hup_out, 0);
- /* Here we return to main, and fall into manager */
- break;
- default: // parent, success
- pid= getpid(); /* Get our pid. */
-
- log_info("Angel pid file: '%s'; PID: %d.",
- (const char *) options.angel_pid_file_name,
- (int) pid);
-
- create_pid_file(Options::angel_pid_file_name, pid);
-
- while (child_status == CHILD_OK && is_terminated == 0)
- sigsuspend(&zeromask);
-
- if (is_terminated)
- log_info("angel got signal %d, exiting", is_terminated);
- else if (child_status == CHILD_NEED_RESPAWN)
- {
- child_status= CHILD_OK;
- log_error("angel(): mysqlmanager exited abnormally (exit code: %d):"
- "respawning...",
- (int) child_exit_code);
- sleep(1); /* don't respawn too fast */
- goto spawn;
- }
-
- /* Delete IM-angel pid file. */
- my_delete(Options::angel_pid_file_name, MYF(0));
-
- /*
- mysqlmanager successfully exited, let's silently evaporate
- If we return to main we fall into the manager() function, so let's
- simply exit().
- */
- exit(0);
- }
+ return FALSE;
}
#endif
diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc
index 9eb148c4e3b..d8667e155b5 100644
--- a/server-tools/instance-manager/options.cc
+++ b/server-tools/instance-manager/options.cc
@@ -19,44 +19,88 @@
#include "options.h"
-#include "priv.h"
-#include "portability.h"
+#include <my_global.h>
#include <my_sys.h>
#include <my_getopt.h>
-#include <m_string.h>
#include <mysql_com.h>
+#include "exit_codes.h"
+#include "log.h"
+#include "portability.h"
+#include "priv.h"
+#include "user_management_commands.h"
+
#define QUOTE2(x) #x
#define QUOTE(x) QUOTE2(x)
#ifdef __WIN__
-char Options::install_as_service;
-char Options::remove_service;
-char Options::stand_alone;
-char windows_config_file[FN_REFLEN];
-char default_password_file_name[FN_REFLEN];
-char default_log_file_name[FN_REFLEN];
-const char *Options::config_file= windows_config_file;
-#else
-char Options::run_as_service;
-const char *Options::user= 0; /* No default value */
-const char *default_password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
-const char *default_log_file_name= QUOTE(DEFAULT_LOG_FILE_NAME);
-const char *Options::config_file= QUOTE(DEFAULT_CONFIG_FILE);
-const char *Options::angel_pid_file_name= NULL;
-const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME);
+
+/* Define holders for default values. */
+
+static char win_dflt_config_file_name[FN_REFLEN];
+static char win_dflt_password_file_name[FN_REFLEN];
+static char win_dflt_pid_file_name[FN_REFLEN];
+
+static char win_dflt_mysqld_path[FN_REFLEN];
+
+/* Define and initialize Windows-specific options. */
+
+my_bool Options::Service::install_as_service;
+my_bool Options::Service::remove_service;
+my_bool Options::Service::stand_alone;
+
+const char *Options::Main::config_file= win_dflt_config_file_name;
+const char *Options::Main::password_file_name= win_dflt_password_file_name;
+const char *Options::Main::pid_file_name= win_dflt_pid_file_name;
+
+const char *Options::Main::default_mysqld_path= win_dflt_mysqld_path;
+
+static int setup_windows_defaults();
+
+#else /* UNIX */
+
+/* Define and initialize UNIX-specific options. */
+
+my_bool Options::Daemon::run_as_service= FALSE;
+const char *Options::Daemon::log_file_name= QUOTE(DEFAULT_LOG_FILE_NAME);
+const char *Options::Daemon::user= NULL; /* No default value */
+const char *Options::Daemon::angel_pid_file_name= NULL;
+
+const char *Options::Main::config_file= QUOTE(DEFAULT_CONFIG_FILE);
+const char *
+Options::Main::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
+const char *Options::Main::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME);
+const char *Options::Main::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME);
+
+const char *Options::Main::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH);
+
#endif
-const char *Options::log_file_name= default_log_file_name;
-const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME);
-const char *Options::password_file_name= default_password_file_name;
-const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH);
-const char *Options::bind_address= 0; /* No default value */
-uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL;
-uint Options::port_number= DEFAULT_PORT;
-/* just to declare */
+
+/* Remember if the config file was forced. */
+
+bool Options::Main::is_forced_default_file= FALSE;
+
+/* Define and initialize common options. */
+
+const char *Options::Main::bind_address= NULL; /* No default value */
+uint Options::Main::monitoring_interval= DEFAULT_MONITORING_INTERVAL;
+uint Options::Main::port_number= DEFAULT_PORT;
+my_bool Options::Main::mysqld_safe_compatible= FALSE;
+
+/* Options::User_management */
+
+char *Options::User_management::user_name= NULL;
+char *Options::User_management::password= NULL;
+
+User_management_cmd *Options::User_management::cmd= NULL;
+
+/* Private members. */
+
char **Options::saved_argv= NULL;
-/* Remember if the config file was forced */
-bool Options::is_forced_default_file= 0;
+
+#ifndef DBUG_OFF
+const char *Options::Debug::config_str= "d:t:i:O,im.trace";
+#endif
static const char * const ANGEL_PID_FILE_SUFFIX= ".angel.pid";
static const int ANGEL_PID_FILE_SUFFIX_LEN= (uint) strlen(ANGEL_PID_FILE_SUFFIX);
@@ -67,24 +111,34 @@ static const int ANGEL_PID_FILE_SUFFIX_LEN= (uint) strlen(ANGEL_PID_FILE_SUFFIX)
*/
enum options {
+ OPT_USERNAME= 'u',
+ OPT_PASSWORD= 'p',
OPT_LOG= 256,
OPT_PID_FILE,
OPT_SOCKET,
OPT_PASSWORD_FILE,
OPT_MYSQLD_PATH,
-#ifndef __WIN__
- OPT_RUN_AS_SERVICE,
- OPT_USER,
- OPT_ANGEL_PID_FILE,
-#else
+#ifdef __WIN__
OPT_INSTALL_SERVICE,
OPT_REMOVE_SERVICE,
OPT_STAND_ALONE,
+#else
+ OPT_RUN_AS_SERVICE,
+ OPT_USER,
+ OPT_ANGEL_PID_FILE,
#endif
OPT_MONITORING_INTERVAL,
OPT_PORT,
OPT_WAIT_TIMEOUT,
- OPT_BIND_ADDRESS
+ OPT_BIND_ADDRESS,
+ OPT_PRINT_PASSWORD_LINE,
+ OPT_ADD_USER,
+ OPT_DROP_USER,
+ OPT_EDIT_USER,
+ OPT_CLEAN_PASSWORD_FILE,
+ OPT_CHECK_PASSWORD_FILE,
+ OPT_LIST_USERS,
+ OPT_MYSQLD_SAFE_COMPATIBLE
};
static struct my_option my_long_options[] =
@@ -92,88 +146,160 @@ static struct my_option my_long_options[] =
{ "help", '?', "Display this help and exit.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
- { "log", OPT_LOG, "Path to log file. Used only with --run-as-service.",
- (gptr *) &Options::log_file_name, (gptr *) &Options::log_file_name,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
-
- { "pid-file", OPT_PID_FILE, "Pid file to use.",
- (gptr *) &Options::pid_file_name, (gptr *) &Options::pid_file_name,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "add-user", OPT_ADD_USER,
+ "Add a user to the password file",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
#ifndef __WIN__
{ "angel-pid-file", OPT_ANGEL_PID_FILE, "Pid file for angel process.",
- (gptr *) &Options::angel_pid_file_name,
- (gptr *) &Options::angel_pid_file_name,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
-
- { "socket", OPT_SOCKET, "Socket file to use for connection.",
- (gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name,
+ (uchar* *) &Options::Daemon::angel_pid_file_name,
+ (uchar* *) &Options::Daemon::angel_pid_file_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
#endif
- { "passwd", 'P', "Prepare entry for passwd file and exit.", 0, 0, 0,
- GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
-
{ "bind-address", OPT_BIND_ADDRESS, "Bind address to use for connection.",
- (gptr *) &Options::bind_address, (gptr *) &Options::bind_address,
+ (uchar* *) &Options::Main::bind_address,
+ (uchar* *) &Options::Main::bind_address,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
- { "port", OPT_PORT, "Port number to use for connections",
- (gptr *) &Options::port_number, (gptr *) &Options::port_number,
- 0, GET_UINT, REQUIRED_ARG, DEFAULT_PORT, 0, 0, 0, 0, 0 },
+ { "check-password-file", OPT_CHECK_PASSWORD_FILE,
+ "Check the password file for consistency",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
- { "password-file", OPT_PASSWORD_FILE, "Look for Instance Manager users"
- " and passwords here.",
- (gptr *) &Options::password_file_name,
- (gptr *) &Options::password_file_name,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "clean-password-file", OPT_CLEAN_PASSWORD_FILE,
+ "Clean the password file",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
+
+#ifndef DBUG_OFF
+ {"debug", '#', "Debug log.",
+ (uchar* *) &Options::Debug::config_str,
+ (uchar* *) &Options::Debug::config_str,
+ 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+#endif
{ "default-mysqld-path", OPT_MYSQLD_PATH, "Where to look for MySQL"
" Server binary.",
- (gptr *) &Options::default_mysqld_path,
- (gptr *) &Options::default_mysqld_path,
+ (uchar* *) &Options::Main::default_mysqld_path,
+ (uchar* *) &Options::Main::default_mysqld_path,
0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0 },
+ { "drop-user", OPT_DROP_USER,
+ "Drop existing user from the password file",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
+
+ { "edit-user", OPT_EDIT_USER,
+ "Edit existing user in the password file",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
+
+#ifdef __WIN__
+ { "install", OPT_INSTALL_SERVICE, "Install as system service.",
+ (uchar* *) &Options::Service::install_as_service,
+ (uchar* *) &Options::Service::install_as_service,
+ 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
+#endif
+
+ { "list-users", OPT_LIST_USERS,
+ "Print out a list of registered users",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
+
+#ifndef __WIN__
+ { "log", OPT_LOG, "Path to log file. Used only with --run-as-service.",
+ (uchar* *) &Options::Daemon::log_file_name,
+ (uchar* *) &Options::Daemon::log_file_name,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+#endif
+
{ "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor"
" instances in seconds.",
- (gptr *) &Options::monitoring_interval,
- (gptr *) &Options::monitoring_interval,
+ (uchar* *) &Options::Main::monitoring_interval,
+ (uchar* *) &Options::Main::monitoring_interval,
0, GET_UINT, REQUIRED_ARG, DEFAULT_MONITORING_INTERVAL,
0, 0, 0, 0, 0 },
-#ifdef __WIN__
- { "install", OPT_INSTALL_SERVICE, "Install as system service.",
- (gptr *) &Options::install_as_service, (gptr*) &Options::install_as_service,
+
+ { "mysqld-safe-compatible", OPT_MYSQLD_SAFE_COMPATIBLE,
+ "Start Instance Manager in mysqld_safe compatible manner",
+ (uchar* *) &Options::Main::mysqld_safe_compatible,
+ (uchar* *) &Options::Main::mysqld_safe_compatible,
0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
+
+ { "print-password-line", OPT_PRINT_PASSWORD_LINE,
+ "Print out a user entry as a line for the password file and exit.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
+
+ { "password", OPT_PASSWORD, "Password to update the password file",
+ (uchar* *) &Options::User_management::password,
+ (uchar* *) &Options::User_management::password,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+
+ { "password-file", OPT_PASSWORD_FILE,
+ "Look for Instance Manager users and passwords here.",
+ (uchar* *) &Options::Main::password_file_name,
+ (uchar* *) &Options::Main::password_file_name,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+
+ { "pid-file", OPT_PID_FILE, "Pid file to use.",
+ (uchar* *) &Options::Main::pid_file_name,
+ (uchar* *) &Options::Main::pid_file_name,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+
+ { "port", OPT_PORT, "Port number to use for connections",
+ (uchar* *) &Options::Main::port_number,
+ (uchar* *) &Options::Main::port_number,
+ 0, GET_UINT, REQUIRED_ARG, DEFAULT_PORT, 0, 0, 0, 0, 0 },
+
+#ifdef __WIN__
{ "remove", OPT_REMOVE_SERVICE, "Remove system service.",
- (gptr *)&Options::remove_service, (gptr*) &Options::remove_service,
- 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0},
- { "standalone", OPT_STAND_ALONE, "Run the application in stand alone mode.",
- (gptr *)&Options::stand_alone, (gptr*) &Options::stand_alone,
+ (uchar* *) &Options::Service::remove_service,
+ (uchar* *) &Options::Service::remove_service,
0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0},
#else
{ "run-as-service", OPT_RUN_AS_SERVICE,
- "Daemonize and start angel process.", (gptr *) &Options::run_as_service,
+ "Daemonize and start angel process.",
+ (uchar* *) &Options::Daemon::run_as_service,
0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
+#endif
+#ifndef __WIN__
+ { "socket", OPT_SOCKET, "Socket file to use for connection.",
+ (uchar* *) &Options::Main::socket_file_name,
+ (uchar* *) &Options::Main::socket_file_name,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+#endif
+
+#ifdef __WIN__
+ { "standalone", OPT_STAND_ALONE, "Run the application in stand alone mode.",
+ (uchar* *) &Options::Service::stand_alone,
+ (uchar* *) &Options::Service::stand_alone,
+ 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0},
+#else
{ "user", OPT_USER, "Username to start mysqlmanager",
- (gptr *) &Options::user,
- (gptr *) &Options::user,
+ (uchar* *) &Options::Daemon::user,
+ (uchar* *) &Options::Daemon::user,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
#endif
+
+ { "username", OPT_USERNAME,
+ "Username to update the password file",
+ (uchar* *) &Options::User_management::user_name,
+ (uchar* *) &Options::User_management::user_name,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+
{ "version", 'V', "Output version information and exit.", 0, 0, 0,
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "wait-timeout", OPT_WAIT_TIMEOUT, "The number of seconds IM waits "
"for activity on a connection before closing it.",
- (gptr *) &net_read_timeout, (gptr *) &net_read_timeout, 0, GET_ULONG,
- REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0 },
+ (uchar* *) &net_read_timeout,
+ (uchar* *) &net_read_timeout,
+ 0, GET_ULONG, REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0 },
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }
};
static void version()
{
- printf("%s Ver %s for %s on %s\n", my_progname, mysqlmanager_version,
+ printf("%s Ver %s for %s on %s\n", my_progname,
+ (const char *) mysqlmanager_version.str,
SYSTEM_TYPE, MACHINE_TYPE);
}
@@ -201,54 +327,65 @@ static void usage()
}
-static void passwd()
-{
- char user[1024], *p;
- const char *pw1, *pw2;
- char pw1msg[]= "Enter password: ";
- char pw2msg[]= "Re-type password: ";
- char crypted_pw[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1];
-
- fprintf(stderr, "Creating record for new user.\n");
- fprintf(stderr, "Enter user name: ");
- if (!fgets(user, sizeof(user), stdin))
- {
- fprintf(stderr, "Unable to read user.\n");
- return;
- }
- if ((p= strchr(user, '\n'))) *p= 0;
-
- pw1= get_tty_password(pw1msg);
- pw2= get_tty_password(pw2msg);
-
- if (strcmp(pw1, pw2))
- {
- fprintf(stderr, "Sorry, passwords do not match.\n");
- return;
- }
-
- make_scrambled_password(crypted_pw, pw1);
- printf("%s:%s\n", user, crypted_pw);
-}
-
-
C_MODE_START
static my_bool
get_one_option(int optid,
const struct my_option *opt __attribute__((unused)),
- char *argument __attribute__((unused)))
+ char *argument)
{
switch(optid) {
case 'V':
version();
exit(0);
- case 'P':
- passwd();
- exit(0);
+ case OPT_PRINT_PASSWORD_LINE:
+ case OPT_ADD_USER:
+ case OPT_DROP_USER:
+ case OPT_EDIT_USER:
+ case OPT_CLEAN_PASSWORD_FILE:
+ case OPT_CHECK_PASSWORD_FILE:
+ case OPT_LIST_USERS:
+ if (Options::User_management::cmd)
+ {
+ fprintf(stderr, "Error: only one password-management command "
+ "can be specified at a time.\n");
+ exit(ERR_INVALID_USAGE);
+ }
+
+ switch (optid) {
+ case OPT_PRINT_PASSWORD_LINE:
+ Options::User_management::cmd= new Print_password_line_cmd();
+ break;
+ case OPT_ADD_USER:
+ Options::User_management::cmd= new Add_user_cmd();
+ break;
+ case OPT_DROP_USER:
+ Options::User_management::cmd= new Drop_user_cmd();
+ break;
+ case OPT_EDIT_USER:
+ Options::User_management::cmd= new Edit_user_cmd();
+ break;
+ case OPT_CLEAN_PASSWORD_FILE:
+ Options::User_management::cmd= new Clean_db_cmd();
+ break;
+ case OPT_CHECK_PASSWORD_FILE:
+ Options::User_management::cmd= new Check_db_cmd();
+ break;
+ case OPT_LIST_USERS:
+ Options::User_management::cmd= new List_users_cmd();
+ break;
+ }
+
+ break;
case '?':
usage();
exit(0);
+ case '#':
+#ifndef DBUG_OFF
+ DBUG_SET(argument ? argument : Options::Debug::config_str);
+ DBUG_SET_INITIAL(argument ? argument : Options::Debug::config_str);
+#endif
+ break;
}
return 0;
}
@@ -272,8 +409,8 @@ int Options::load(int argc, char **argv)
{
if (is_prefix(argv[1], "--defaults-file="))
{
- Options::config_file= strchr(argv[1], '=') + 1;
- Options::is_forced_default_file= 1;
+ Main::config_file= strchr(argv[1], '=') + 1;
+ Main::is_forced_default_file= TRUE;
}
if (is_prefix(argv[1], "--defaults-extra-file=") ||
is_prefix(argv[1], "--no-defaults"))
@@ -282,29 +419,44 @@ int Options::load(int argc, char **argv)
fprintf(stderr, "The --defaults-extra-file and --no-defaults options"
" are not supported by\n"
"Instance Manager. Program aborted.\n");
- goto err;
+ return ERR_INVALID_USAGE;
}
}
#ifdef __WIN__
if (setup_windows_defaults())
- goto err;
+ {
+ fprintf(stderr, "Internal error: could not setup default values.\n");
+ return ERR_OUT_OF_MEMORY;
+ }
#endif
+
/* load_defaults will reset saved_argv with a new allocated list */
saved_argv= argv;
/* config-file options are prepended to command-line ones */
- load_defaults(config_file, default_groups, &argc,
- &saved_argv);
- if ((handle_options(&argc, &saved_argv, my_long_options,
- get_one_option)) != 0)
- goto err;
+ log_info("Loading config file '%s'...",
+ (const char *) Main::config_file);
+
+ load_defaults(Main::config_file, default_groups, &argc, &saved_argv);
+
+ if ((handle_options(&argc, &saved_argv, my_long_options, get_one_option)))
+ return ERR_INVALID_USAGE;
+
+ if (!User_management::cmd &&
+ (User_management::user_name || User_management::password))
+ {
+ fprintf(stderr,
+ "--username and/or --password options have been specified, "
+ "but no password-management command has been given.\n");
+ return ERR_INVALID_USAGE;
+ }
#ifndef __WIN__
- if (Options::run_as_service)
+ if (Options::Daemon::run_as_service)
{
- if (Options::angel_pid_file_name == NULL)
+ if (Options::Daemon::angel_pid_file_name == NULL)
{
/*
Calculate angel pid file on the IM pid file basis: replace the
@@ -316,10 +468,11 @@ int Options::load(int argc, char **argv)
char *base_name_ptr;
char *ext_ptr;
- local_angel_pid_file_name= (char *) malloc(strlen(Options::pid_file_name) +
- ANGEL_PID_FILE_SUFFIX_LEN);
+ local_angel_pid_file_name=
+ (char *) malloc(strlen(Options::Main::pid_file_name) +
+ ANGEL_PID_FILE_SUFFIX_LEN);
- strcpy(local_angel_pid_file_name, Options::pid_file_name);
+ strcpy(local_angel_pid_file_name, Options::Main::pid_file_name);
base_name_ptr= strrchr(local_angel_pid_file_name, '/');
@@ -332,54 +485,71 @@ int Options::load(int argc, char **argv)
strcat(local_angel_pid_file_name, ANGEL_PID_FILE_SUFFIX);
- Options::angel_pid_file_name= local_angel_pid_file_name;
+ Options::Daemon::angel_pid_file_name= local_angel_pid_file_name;
}
else
{
- Options::angel_pid_file_name= strdup(Options::angel_pid_file_name);
+ Options::Daemon::angel_pid_file_name=
+ strdup(Options::Daemon::angel_pid_file_name);
}
}
#endif
return 0;
-
-err:
- return 1;
}
void Options::cleanup()
{
- /* free_defaults returns nothing */
- if (Options::saved_argv != NULL)
- free_defaults(Options::saved_argv);
+ if (saved_argv)
+ free_defaults(saved_argv);
+
+ delete User_management::cmd;
#ifndef __WIN__
- if (Options::run_as_service)
- free((void *) Options::angel_pid_file_name);
+ if (Options::Daemon::run_as_service)
+ free((void *) Options::Daemon::angel_pid_file_name);
#endif
}
#ifdef __WIN__
-int Options::setup_windows_defaults()
+static int setup_windows_defaults()
{
- if (!GetModuleFileName(NULL, default_password_file_name,
- sizeof(default_password_file_name)))
- return 1;
- char *filename= strstr(default_password_file_name, ".exe");
- strcpy(filename, ".passwd");
-
- if (!GetModuleFileName(NULL, default_log_file_name,
- sizeof(default_log_file_name)))
+ char module_full_name[FN_REFLEN];
+ char dir_name[FN_REFLEN];
+ char base_name[FN_REFLEN];
+ char im_name[FN_REFLEN];
+ char *base_name_ptr;
+ char *ptr;
+
+ /* Determine dirname and basename. */
+
+ if (!GetModuleFileName(NULL, module_full_name, sizeof (module_full_name)) ||
+ !GetFullPathName(module_full_name, sizeof (dir_name), dir_name,
+ &base_name_ptr))
+ {
return 1;
- filename= strstr(default_log_file_name, ".exe");
- strcpy(filename, ".log");
+ }
+
+ strmake(base_name, base_name_ptr, FN_REFLEN);
+ *base_name_ptr= 0;
+
+ strmake(im_name, base_name, FN_REFLEN);
+ ptr= strrchr(im_name, '.');
+
+ if (!ptr)
+ return 1;
+
+ *ptr= 0;
+
+ /* Initialize the defaults. */
+
+ strxmov(win_dflt_config_file_name, dir_name, DFLT_CONFIG_FILE_NAME, NullS);
+ strxmov(win_dflt_mysqld_path, dir_name, DFLT_MYSQLD_PATH, NullS);
+ strxmov(win_dflt_password_file_name, dir_name, im_name, DFLT_PASSWD_FILE_EXT,
+ NullS);
+ strxmov(win_dflt_pid_file_name, dir_name, im_name, DFLT_PID_FILE_EXT, NullS);
- if (!GetModuleFileName(NULL, windows_config_file,
- sizeof(windows_config_file)))
- return 1;
- char *slash= strrchr(windows_config_file, '\\');
- strcpy(slash, "\\my.ini");
return 0;
}
diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h
index f7186bb4ff4..0202ca271c9 100644
--- a/server-tools/instance-manager/options.h
+++ b/server-tools/instance-manager/options.h
@@ -17,47 +17,89 @@
#define INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H
/*
- Options - all possible options for the instance manager grouped in one
- struct.
+ Options - all possible command-line options for the Instance Manager grouped
+ in one struct.
*/
+
#include <my_global.h>
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
+class User_management_cmd;
+
struct Options
{
-#ifdef __WIN__
- static char install_as_service;
- static char remove_service;
- static char stand_alone;
+ /*
+ NOTE: handle_options() expects value of my_bool type for GET_BOOL
+ accessor (i.e. bool must not be used).
+ */
+
+ struct User_management
+ {
+ static User_management_cmd *cmd;
+
+ static char *user_name;
+ static char *password;
+ };
+
+ struct Main
+ {
+ /* this is not an option parsed by handle_options(). */
+ static bool is_forced_default_file;
+
+ static const char *pid_file_name;
+#ifndef __WIN__
+ static const char *socket_file_name;
+#endif
+ static const char *password_file_name;
+ static const char *default_mysqld_path;
+ static uint monitoring_interval;
+ static uint port_number;
+ static const char *bind_address;
+ static const char *config_file;
+ static my_bool mysqld_safe_compatible;
+ };
+
+#ifndef DBUG_OFF
+ struct Debug
+ {
+ static const char *config_str;
+ };
+#endif
+
+#ifndef __WIN__
+
+ struct Daemon
+ {
+ static my_bool run_as_service;
+ static const char *log_file_name;
+ static const char *user;
+ static const char *angel_pid_file_name;
+ };
+
#else
- static char run_as_service; /* handle_options doesn't support bool */
- static const char *user;
- static const char *angel_pid_file_name;
- static const char *socket_file_name;
+
+ struct Service
+ {
+ static my_bool install_as_service;
+ static my_bool remove_service;
+ static my_bool stand_alone;
+ };
+
#endif
- static bool is_forced_default_file;
- static const char *log_file_name;
- static const char *pid_file_name;
- static const char *password_file_name;
- static const char *default_mysqld_path;
- /* the option which should be passed to process_default_option_files */
- static uint monitoring_interval;
- static uint port_number;
- static const char *bind_address;
- static const char *config_file;
+public:
+ static int load(int argc, char **argv);
+ static void cleanup();
+
+private:
+ Options(); /* Deny instantiation of this class. */
+
+private:
/* argv pointer returned by load_defaults() to be used by free_defaults() */
static char **saved_argv;
-
- int load(int argc, char **argv);
- Options() {}
- void cleanup();
-#ifdef __WIN__
- int setup_windows_defaults();
-#endif
};
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H
diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc
index bbbadf3e91a..cd20e3bc7ab 100644
--- a/server-tools/instance-manager/parse.cc
+++ b/server-tools/instance-manager/parse.cc
@@ -16,12 +16,12 @@
#include "parse.h"
#include "commands.h"
-#include <string.h>
-
enum Token
{
- TOK_ERROR= 0, /* Encodes the "ERROR" word, it doesn't indicate error. */
+ TOK_CREATE= 0,
+ TOK_DROP,
+ TOK_ERROR, /* Encodes the "ERROR" word, it doesn't indicate error. */
TOK_FILES,
TOK_FLUSH,
TOK_GENERAL,
@@ -49,6 +49,8 @@ struct tokens_st
static struct tokens_st tokens[]= {
+ {6, "CREATE"},
+ {4, "DROP"},
{5, "ERROR"},
{5, "FILES"},
{5, "FLUSH"},
@@ -66,13 +68,44 @@ static struct tokens_st tokens[]= {
{5, "UNSET"}
};
+/************************************************************************/
+
+Named_value_arr::Named_value_arr() :
+ initialized(FALSE)
+{
+}
+
+
+bool Named_value_arr::init()
+{
+ if (my_init_dynamic_array(&arr, sizeof(Named_value), 0, 32))
+ return TRUE;
+
+ initialized= TRUE;
+
+ return FALSE;
+}
+
+
+Named_value_arr::~Named_value_arr()
+{
+ if (!initialized)
+ return;
+
+ for (int i= 0; i < get_size(); ++i)
+ get_element(i).free();
+
+ delete_dynamic(&arr);
+}
+
+/************************************************************************/
/*
Returns token no if word corresponds to some token, otherwise returns
TOK_NOT_FOUND
*/
-inline Token find_token(const char *word, uint word_len)
+inline Token find_token(const char *word, size_t word_len)
{
int i= 0;
do
@@ -86,7 +119,7 @@ inline Token find_token(const char *word, uint word_len)
}
-Token get_token(const char **text, uint *word_len)
+Token get_token(const char **text, size_t *word_len)
{
get_word(text, word_len);
if (*word_len)
@@ -95,7 +128,7 @@ Token get_token(const char **text, uint *word_len)
}
-Token shift_token(const char **text, uint *word_len)
+Token shift_token(const char **text, size_t *word_len)
{
Token save= get_token(text, word_len);
(*text)+= *word_len;
@@ -103,52 +136,199 @@ Token shift_token(const char **text, uint *word_len)
}
-int get_text_id(const char **text, uint *word_len, const char **id)
+int get_text_id(const char **text, LEX_STRING *token)
{
- get_word(text, word_len);
- if (*word_len == 0)
+ get_word(text, &token->length);
+ if (token->length == 0)
return 1;
- *id= *text;
+ token->str= (char *) *text;
return 0;
}
-Command *parse_command(Instance_map *map, const char *text)
+static bool parse_long(const LEX_STRING *token, long *value)
+{
+ int err_code;
+ char *end_ptr= token->str + token->length;
+
+ *value= (long)my_strtoll10(token->str, &end_ptr, &err_code);
+
+ return err_code != 0;
+}
+
+
+bool parse_option_value(const char *text, size_t *text_len, char **value)
+{
+ char beginning_quote;
+ const char *text_start_ptr;
+ char *v;
+ bool escape_mode= FALSE;
+
+ if (!*text || (*text != '\'' && *text != '"'))
+ return TRUE; /* syntax error: string expected. */
+
+ beginning_quote= *text;
+
+ ++text; /* skip the beginning quote. */
+
+ text_start_ptr= text;
+
+ if (!(v= Named_value::alloc_str(text)))
+ return TRUE;
+
+ *value= v;
+
+ while (TRUE)
+ {
+ if (!*text)
+ {
+ Named_value::free_str(value);
+ return TRUE; /* syntax error: missing terminating ' character. */
+ }
+
+ if (*text == '\n' || *text == '\r')
+ {
+ Named_value::free_str(value);
+ return TRUE; /* syntax error: option value should be a single line. */
+ }
+
+ if (!escape_mode && *text == beginning_quote)
+ break;
+
+ if (escape_mode)
+ {
+ switch (*text)
+ {
+ case 'b': /* \b -- backspace */
+ if (v > *value)
+ --v;
+ break;
+
+ case 't': /* \t -- tab */
+ *v= '\t';
+ ++v;
+ break;
+
+ case 'n': /* \n -- newline */
+ *v= '\n';
+ ++v;
+ break;
+
+ case 'r': /* \r -- carriage return */
+ *v= '\r';
+ ++v;
+ break;
+
+ case '\\': /* \\ -- back slash */
+ *v= '\\';
+ ++v;
+ break;
+
+ case 's': /* \s -- space */
+ *v= ' ';
+ ++v;
+ break;
+
+ default: /* Unknown escape sequence. Treat as error. */
+ Named_value::free_str(value);
+ return TRUE;
+ }
+
+ escape_mode= FALSE;
+ }
+ else
+ {
+ if (*text == '\\')
+ {
+ escape_mode= TRUE;
+ }
+ else
+ {
+ *v= *text;
+ ++v;
+ }
+ }
+
+ ++text;
+ }
+
+ *v= 0;
+
+ /* "2" below stands for beginning and ending quotes. */
+ *text_len= text - text_start_ptr + 2;
+
+ return FALSE;
+}
+
+
+void skip_spaces(const char **text)
+{
+ while (**text && my_isspace(default_charset_info, **text))
+ ++(*text);
+}
+
+
+Command *parse_command(const char *text)
{
- uint word_len;
- const char *instance_name;
- uint instance_name_len;
- const char *option;
- uint option_len;
- const char *option_value;
- uint option_value_len;
- const char *log_size;
- Command *command;
- bool skip= false;
- const char *tmp;
+ size_t word_len;
+ LEX_STRING instance_name;
+ Command *command= 0;
Token tok1= shift_token(&text, &word_len);
switch (tok1) {
case TOK_START: // fallthrough
case TOK_STOP:
+ case TOK_CREATE:
+ case TOK_DROP:
if (shift_token(&text, &word_len) != TOK_INSTANCE)
goto syntax_error;
get_word(&text, &word_len);
if (word_len == 0)
goto syntax_error;
- instance_name= text;
- instance_name_len= word_len;
+ instance_name.str= (char *) text;
+ instance_name.length= word_len;
text+= word_len;
- /* it should be the end of command */
- get_word(&text, &word_len, NONSPACE);
- if (word_len)
- goto syntax_error;
- if (tok1 == TOK_START)
- command= new Start_instance(map, instance_name, instance_name_len);
+ if (tok1 == TOK_CREATE)
+ {
+ Create_instance *cmd= new Create_instance(&instance_name);
+
+ if (!cmd)
+ return NULL; /* Report ER_OUT_OF_RESOURCES. */
+
+ if (cmd->init(&text))
+ {
+ delete cmd;
+ goto syntax_error;
+ }
+
+ command= cmd;
+ }
else
- command= new Stop_instance(map, instance_name, instance_name_len);
+ {
+ /* it should be the end of command */
+ get_word(&text, &word_len, NONSPACE);
+ if (word_len)
+ goto syntax_error;
+ }
+
+ switch (tok1) {
+ case TOK_START:
+ command= new Start_instance(&instance_name);
+ break;
+ case TOK_STOP:
+ command= new Stop_instance(&instance_name);
+ break;
+ case TOK_CREATE:
+ ; /* command already initialized. */
+ break;
+ case TOK_DROP:
+ command= new Drop_instance(&instance_name);
+ break;
+ default: /* this is impossible, but nevertheless... */
+ DBUG_ASSERT(0);
+ }
break;
case TOK_FLUSH:
if (shift_token(&text, &word_len) != TOK_INSTANCES)
@@ -158,92 +338,72 @@ Command *parse_command(Instance_map *map, const char *text)
if (word_len)
goto syntax_error;
- command= new Flush_instances(map);
+ command= new Flush_instances();
break;
case TOK_UNSET:
- skip= true;
case TOK_SET:
+ {
+ Abstract_option_cmd *cmd;
- if (get_text_id(&text, &instance_name_len, &instance_name))
- goto syntax_error;
- text+= instance_name_len;
-
- /* the next token should be a dot */
- get_word(&text, &word_len);
- if (*text != '.')
- goto syntax_error;
- text++;
+ if (tok1 == TOK_SET)
+ cmd= new Set_option();
+ else
+ cmd= new Unset_option();
- get_word(&text, &option_len, NONSPACE);
- option= text;
- if ((tmp= strchr(text, '=')) != NULL)
- option_len= (uint) (tmp - text);
- text+= option_len;
+ if (!cmd)
+ return NULL; /* Report ER_OUT_OF_RESOURCES. */
- get_word(&text, &word_len);
- if (*text == '=')
- {
- text++; /* skip '=' */
- get_word(&text, &option_value_len, NONSPACE);
- option_value= text;
- text+= option_value_len;
- }
- else
- {
- option_value= "";
- option_value_len= 0;
- }
+ if (cmd->init(&text))
+ {
+ delete cmd;
+ goto syntax_error;
+ }
- /* should be empty */
- get_word(&text, &word_len, NONSPACE);
- if (word_len)
- goto syntax_error;
+ command= cmd;
- if (skip)
- command= new Unset_option(map, instance_name, instance_name_len,
- option, option_len, option_value,
- option_value_len);
- else
- command= new Set_option(map, instance_name, instance_name_len,
- option, option_len, option_value,
- option_value_len);
- break;
+ break;
+ }
case TOK_SHOW:
switch (shift_token(&text, &word_len)) {
case TOK_INSTANCES:
get_word(&text, &word_len, NONSPACE);
if (word_len)
goto syntax_error;
- command= new Show_instances(map);
+ command= new Show_instances();
break;
case TOK_INSTANCE:
switch (Token tok2= shift_token(&text, &word_len)) {
case TOK_OPTIONS:
case TOK_STATUS:
- if (get_text_id(&text, &instance_name_len, &instance_name))
+ if (get_text_id(&text, &instance_name))
goto syntax_error;
- text+= instance_name_len;
+ text+= instance_name.length;
/* check that this is the end of the command */
get_word(&text, &word_len, NONSPACE);
if (word_len)
goto syntax_error;
if (tok2 == TOK_STATUS)
- command= new Show_instance_status(map, instance_name,
- instance_name_len);
+ command= new Show_instance_status(&instance_name);
else
- command= new Show_instance_options(map, instance_name,
- instance_name_len);
+ command= new Show_instance_options(&instance_name);
break;
default:
goto syntax_error;
}
break;
default:
- instance_name= text - word_len;
- instance_name_len= word_len;
- if (instance_name_len)
+ instance_name.str= (char *) text - word_len;
+ instance_name.length= word_len;
+ if (instance_name.length)
{
Log_type log_type;
+
+ long log_size;
+ LEX_STRING log_size_str;
+
+ long log_offset= 0;
+ LEX_STRING log_offset_str= { NULL, 0 };
+
switch (shift_token(&text, &word_len)) {
case TOK_LOG:
switch (Token tok3= shift_token(&text, &word_len)) {
@@ -252,8 +412,7 @@ Command *parse_command(Instance_map *map, const char *text)
/* check that this is the end of the command */
if (word_len)
goto syntax_error;
- command= new Show_instance_log_files(map, instance_name,
- instance_name_len);
+ command= new Show_instance_log_files(&instance_name);
break;
case TOK_ERROR:
case TOK_GENERAL:
@@ -273,12 +432,14 @@ Command *parse_command(Instance_map *map, const char *text)
goto syntax_error;
}
/* get the size of the log we want to retrieve */
- if (get_text_id(&text, &word_len, &log_size))
+ if (get_text_id(&text, &log_size_str))
goto syntax_error;
- text+= word_len;
+ text+= log_size_str.length;
+
/* this parameter is required */
- if (!word_len)
+ if (!log_size_str.length)
goto syntax_error;
+
/* the next token should be comma, or nothing */
get_word(&text, &word_len);
switch (*text) {
@@ -288,23 +449,41 @@ Command *parse_command(Instance_map *map, const char *text)
get_word(&text, &word_len);
if (!word_len)
goto syntax_error;
+ log_offset_str.str= (char *) text;
+ log_offset_str.length= word_len;
text+= word_len;
- command= new Show_instance_log(map, instance_name,
- instance_name_len, log_type,
- log_size, text);
get_word(&text, &word_len, NONSPACE);
/* check that this is the end of the command */
if (word_len)
goto syntax_error;
break;
case '\0':
- command= new Show_instance_log(map, instance_name,
- instance_name_len, log_type,
- log_size, NULL);
break; /* this is ok */
default:
+ goto syntax_error;
+ }
+
+ /* Parse size parameter. */
+
+ if (parse_long(&log_size_str, &log_size))
goto syntax_error;
+
+ if (log_size <= 0)
+ goto syntax_error;
+
+ /* Parse offset parameter (if specified). */
+
+ if (log_offset_str.length)
+ {
+ if (parse_long(&log_offset_str, &log_offset))
+ goto syntax_error;
+
+ if (log_offset <= 0)
+ goto syntax_error;
}
+
+ command= new Show_instance_log(&instance_name,
+ log_type, log_size, log_offset);
break;
default:
goto syntax_error;
@@ -323,5 +502,8 @@ Command *parse_command(Instance_map *map, const char *text)
syntax_error:
command= new Syntax_error();
}
+
+ DBUG_ASSERT(command);
+
return command;
}
diff --git a/server-tools/instance-manager/parse.h b/server-tools/instance-manager/parse.h
index 9f7f7d7933c..9c50ace5948 100644
--- a/server-tools/instance-manager/parse.h
+++ b/server-tools/instance-manager/parse.h
@@ -17,9 +17,9 @@
#include <my_global.h>
#include <my_sys.h>
+#include <m_string.h>
class Command;
-class Instance_map;
enum Log_type
{
@@ -28,10 +28,148 @@ enum Log_type
IM_LOG_SLOW
};
-Command *parse_command(Instance_map *instance_map, const char *text);
+Command *parse_command(const char *text);
+
+bool parse_option_value(const char *text, size_t *text_len, char **value);
+
+void skip_spaces(const char **text);
/* define kinds of the word seek method */
-enum { ALPHANUM= 1, NONSPACE };
+enum enum_seek_method { ALPHANUM= 1, NONSPACE, OPTION_NAME };
+
+/************************************************************************/
+
+class Named_value
+{
+public:
+ /*
+ The purpose of these methods is just to have one method for
+ allocating/deallocating memory for strings for Named_value.
+ */
+
+ static inline char *alloc_str(const LEX_STRING *str);
+ static inline char *alloc_str(const char *str);
+ static inline void free_str(char **str);
+
+public:
+ inline Named_value();
+ inline Named_value(char *name_arg, char *value_arg);
+
+ inline char *get_name();
+ inline char *get_value();
+
+ inline void free();
+
+private:
+ char *name;
+ char *value;
+};
+
+inline char *Named_value::alloc_str(const LEX_STRING *str)
+{
+ return my_strndup(str->str, str->length, MYF(0));
+}
+
+inline char *Named_value::alloc_str(const char *str)
+{
+ return my_strdup(str, MYF(0));
+}
+
+inline void Named_value::free_str(char **str)
+{
+ my_free(*str, MYF(MY_ALLOW_ZERO_PTR));
+ *str= NULL;
+}
+
+inline Named_value::Named_value()
+ :name(NULL), value(NULL)
+{ }
+
+inline Named_value::Named_value(char *name_arg, char *value_arg)
+ :name(name_arg), value(value_arg)
+{ }
+
+inline char *Named_value::get_name()
+{
+ return name;
+}
+
+inline char *Named_value::get_value()
+{
+ return value;
+}
+
+void Named_value::free()
+{
+ free_str(&name);
+ free_str(&value);
+}
+
+/************************************************************************/
+
+class Named_value_arr
+{
+public:
+ Named_value_arr();
+ ~Named_value_arr();
+
+ bool init();
+
+ inline int get_size() const;
+ inline Named_value get_element(int idx) const;
+ inline void remove_element(int idx);
+ inline bool add_element(Named_value *option);
+ inline bool replace_element(int idx, Named_value *option);
+
+private:
+ bool initialized;
+ DYNAMIC_ARRAY arr;
+};
+
+
+inline int Named_value_arr::get_size() const
+{
+ return arr.elements;
+}
+
+
+inline Named_value Named_value_arr::get_element(int idx) const
+{
+ DBUG_ASSERT(0 <= idx && (uint) idx < arr.elements);
+
+ Named_value option;
+ get_dynamic((DYNAMIC_ARRAY *) &arr, (uchar*) &option, idx);
+
+ return option;
+}
+
+
+inline void Named_value_arr::remove_element(int idx)
+{
+ DBUG_ASSERT(0 <= idx && (uint) idx < arr.elements);
+
+ get_element(idx).free();
+
+ delete_dynamic_element(&arr, idx);
+}
+
+
+inline bool Named_value_arr::add_element(Named_value *option)
+{
+ return insert_dynamic(&arr, (uchar*) option);
+}
+
+
+inline bool Named_value_arr::replace_element(int idx, Named_value *option)
+{
+ DBUG_ASSERT(0 <= idx && (uint) idx < arr.elements);
+
+ get_element(idx).free();
+
+ return set_dynamic(&arr, (uchar*) option, idx);
+}
+
+/************************************************************************/
/*
tries to find next word in the text
@@ -39,8 +177,8 @@ enum { ALPHANUM= 1, NONSPACE };
if not found returns pointer to first non-space or to '\0', word_len == 0
*/
-inline void get_word(const char **text, uint *word_len,
- int seek_method= ALPHANUM)
+inline void get_word(const char **text, size_t *word_len,
+ enum_seek_method seek_method= ALPHANUM)
{
const char *word_end;
@@ -50,13 +188,23 @@ inline void get_word(const char **text, uint *word_len,
word_end= *text;
- if (seek_method == ALPHANUM)
+ switch (seek_method) {
+ case ALPHANUM:
while (my_isalnum(default_charset_info, *word_end))
++word_end;
- else
+ break;
+ case NONSPACE:
while (!my_isspace(default_charset_info, *word_end) &&
(*word_end != '\0'))
++word_end;
+ break;
+ case OPTION_NAME:
+ while (my_isalnum(default_charset_info, *word_end) ||
+ *word_end == '-' ||
+ *word_end == '_')
+ ++word_end;
+ break;
+ }
*word_len= (uint) (word_end - *text);
}
diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc
index 377671b4401..3511589acd6 100644
--- a/server-tools/instance-manager/parse_output.cc
+++ b/server-tools/instance-manager/parse_output.cc
@@ -13,15 +13,24 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <my_global.h>
-#include "parse.h"
#include "parse_output.h"
-#include <stdio.h>
+#include <my_global.h>
#include <my_sys.h>
#include <m_string.h>
+
+#include <stdio.h>
+
+#include "parse.h"
#include "portability.h"
+/**************************************************************************
+ Private module implementation.
+**************************************************************************/
+
+namespace { /* no-indent */
+
+/*************************************************************************/
void trim_space(const char **text, uint *word_len)
{
@@ -30,97 +39,369 @@ void trim_space(const char **text, uint *word_len)
start++;
*text= start;
- size_t len= strlen(start);
+ int len= strlen(start);
const char *end= start + len - 1;
while (end > start && my_isspace(&my_charset_latin1, *end))
end--;
- *word_len= (uint) (end - start)+1;
+ *word_len= (end - start)+1;
}
-/*
- Parse output of the given command
+/*************************************************************************/
- SYNOPSYS
- parse_output_and_get_value()
+/**
+ @brief A facade to the internal workings of optaining the output from an
+ executed system process.
+*/
- command the command to execue with popen.
- word the word to look for (usually an option name)
- result the buffer to store the next word (option value)
- input_buffer_len self-explanatory
- flag this equals to GET_LINE if we want to get all the line after
- the matched word and GET_VALUE otherwise.
+class Mysqld_output_parser
+{
+public:
+ Mysqld_output_parser()
+ { }
- DESCRIPTION
+ virtual ~Mysqld_output_parser()
+ { }
- Parse output of the "command". Find the "word" and return the next one
- if flag is GET_VALUE. Return the rest of the parsed string otherwise.
+public:
+ bool parse(const char *command,
+ const char *option_name_str,
+ uint option_name_length,
+ char *option_value_buf,
+ size_t option_value_buf_size,
+ enum_option_type option_type);
- RETURN
- 0 - ok, the word has been found
- 1 - error occured or the word is not found
-*/
+protected:
+ /**
+ @brief Run a process and attach stdout- and stdin-pipes to it.
-int parse_output_and_get_value(const char *command, const char *word,
- char *result, size_t input_buffer_len,
- uint flag)
-{
- FILE *output;
- size_t wordlen;
- /* should be enough to store the string from the output */
- enum { MAX_LINE_LEN= 512 };
- char linebuf[MAX_LINE_LEN];
- int rc= 1;
+ @param command The path to the process to be executed
- wordlen= strlen(word);
+ @return Error status.
+ @retval TRUE An error occurred
+ @retval FALSE Operation was a success
+ */
- /*
- Successful return of popen does not tell us whether the command has been
- executed successfully: if the command was not found, we'll get EOF
- when reading the output buffer below.
+ virtual bool run_command(const char *command)= 0;
+
+
+ /**
+ @brief Read a sequence of bytes from the executed process' stdout pipe.
+
+ The sequence is terminated by either '\0', LF or CRLF tokens. The
+ terminating token is excluded from the result.
+
+ @param line_buffer A pointer to a character buffer
+ @param line_buffer_size The size of the buffer in bytes
+
+ @return Error status.
+ @retval TRUE An error occured
+ @retval FALSE Operation was a success
*/
- if (!(output= popen(command, "r")))
- goto err;
- /*
- We want fully buffered stream. We also want system to
- allocate appropriate buffer.
+ virtual bool read_line(char *line_buffer,
+ uint line_buffer_size)= 0;
+
+
+ /**
+ @brief Release any resources needed after a execution and parsing.
*/
- setvbuf(output, NULL, _IOFBF, 0);
- while (fgets(linebuf, sizeof(linebuf) - 1, output))
+ virtual bool cleanup()= 0;
+};
+
+/*************************************************************************/
+
+bool Mysqld_output_parser::parse(const char *command,
+ const char *option_name_str,
+ uint option_name_length,
+ char *option_value_buf,
+ size_t option_value_buf_size,
+ enum_option_type option_type)
+{
+ /* should be enough to store the string from the output */
+ const int LINE_BUFFER_SIZE= 512;
+ char line_buffer[LINE_BUFFER_SIZE];
+
+ if (run_command(command))
+ return TRUE;
+
+ while (true)
{
+ if (read_line(line_buffer, LINE_BUFFER_SIZE))
+ {
+ cleanup();
+ return TRUE;
+ }
+
uint found_word_len= 0;
- char *linep= linebuf;
+ char *linep= line_buffer;
+
+ line_buffer[sizeof(line_buffer) - 1]= '\0'; /* safety */
+
+ /* Find the word(s) we are looking for in the line. */
+
+ linep= strstr(linep, option_name_str);
+
+ if (!linep)
+ continue;
- linebuf[sizeof(linebuf) - 1]= '\0'; /* safety */
+ linep+= option_name_length;
- /*
- Compare the start of our line with the word(s) we are looking for.
- */
- if (!strncmp(word, linep, wordlen))
+ switch (option_type)
{
- /*
- If we have found our word(s), then move linep past the word(s)
- */
- linep+= wordlen;
- if (flag & GET_VALUE)
+ case GET_VALUE:
+ trim_space((const char**) &linep, &found_word_len);
+
+ if (option_value_buf_size <= found_word_len)
{
- trim_space((const char**) &linep, &found_word_len);
- if (input_buffer_len <= found_word_len)
- goto err;
- strmake(result, linep, found_word_len);
+ cleanup();
+ return TRUE;
}
- else /* currently there are only two options */
- strmake(result, linep, (uint) (input_buffer_len - 1));
- rc= 0;
+
+ strmake(option_value_buf, linep, found_word_len);
+
+ break;
+
+ case GET_LINE:
+ strmake(option_value_buf, linep, option_value_buf_size - 1);
+
break;
}
+
+ cleanup();
+
+ return FALSE;
}
+}
+
+/**************************************************************************
+ Platform-specific implementation: UNIX.
+**************************************************************************/
+
+#ifndef __WIN__
+
+class Mysqld_output_parser_unix : public Mysqld_output_parser
+{
+public:
+ Mysqld_output_parser_unix() :
+ m_stdout(NULL)
+ { }
+
+protected:
+ virtual bool run_command(const char *command);
+
+ virtual bool read_line(char *line_buffer,
+ uint line_buffer_size);
+
+ virtual bool cleanup();
+
+private:
+ FILE *m_stdout;
+};
+
+bool Mysqld_output_parser_unix::run_command(const char *command)
+{
+ if (!(m_stdout= popen(command, "r")))
+ return TRUE;
+
+ /*
+ We want fully buffered stream. We also want system to allocate
+ appropriate buffer.
+ */
- /* we are not interested in the termination status */
- pclose(output);
+ setvbuf(m_stdout, NULL, _IOFBF, 0);
-err:
- return rc;
+ return FALSE;
}
+bool Mysqld_output_parser_unix::read_line(char *line_buffer,
+ uint line_buffer_size)
+{
+ char *retbuff = fgets(line_buffer, line_buffer_size, m_stdout);
+ /* Remove any tailing new line charaters */
+ if (line_buffer[line_buffer_size-1] == LF)
+ line_buffer[line_buffer_size-1]= '\0';
+ return (retbuff == NULL);
+}
+
+bool Mysqld_output_parser_unix::cleanup()
+{
+ if (m_stdout)
+ pclose(m_stdout);
+
+ return FALSE;
+}
+
+#else /* Windows */
+
+/**************************************************************************
+ Platform-specific implementation: Windows.
+**************************************************************************/
+
+class Mysqld_output_parser_win : public Mysqld_output_parser
+{
+public:
+ Mysqld_output_parser_win() :
+ m_internal_buffer(NULL),
+ m_internal_buffer_offset(0),
+ m_internal_buffer_size(0)
+ { }
+
+protected:
+ virtual bool run_command(const char *command);
+ virtual bool read_line(char *line_buffer,
+ uint line_buffer_size);
+ virtual bool cleanup();
+
+private:
+ HANDLE m_h_child_stdout_wr;
+ HANDLE m_h_child_stdout_rd;
+ uint m_internal_buffer_offset;
+ uint m_internal_buffer_size;
+ char *m_internal_buffer;
+};
+
+bool Mysqld_output_parser_win::run_command(const char *command)
+{
+ BOOL op_status;
+
+ SECURITY_ATTRIBUTES sa_attr;
+ sa_attr.nLength= sizeof(SECURITY_ATTRIBUTES);
+ sa_attr.bInheritHandle= TRUE;
+ sa_attr.lpSecurityDescriptor= NULL;
+
+ op_status= CreatePipe(&m_h_child_stdout_rd,
+ &m_h_child_stdout_wr,
+ &sa_attr,
+ 0 /* Use system-default buffer size. */);
+
+ if (!op_status)
+ return TRUE;
+
+ SetHandleInformation(m_h_child_stdout_rd, HANDLE_FLAG_INHERIT, 0);
+
+ STARTUPINFO si_start_info;
+ ZeroMemory(&si_start_info, sizeof(STARTUPINFO));
+ si_start_info.cb= sizeof(STARTUPINFO);
+ si_start_info.hStdError= m_h_child_stdout_wr;
+ si_start_info.hStdOutput= m_h_child_stdout_wr;
+ si_start_info.dwFlags|= STARTF_USESTDHANDLES;
+
+ PROCESS_INFORMATION pi_proc_info;
+
+ op_status= CreateProcess(NULL, /* Application name. */
+ (char*)command, /* Command line. */
+ NULL, /* Process security attributes. */
+ NULL, /* Primary thread security attr.*/
+ TRUE, /* Handles are inherited. */
+ 0, /* Creation flags. */
+ NULL, /* Use parent's environment. */
+ NULL, /* Use parent's curr. directory. */
+ &si_start_info, /* STARTUPINFO pointer. */
+ &pi_proc_info); /* Rec. PROCESS_INFORMATION. */
+
+ if (!op_status)
+ {
+ CloseHandle(m_h_child_stdout_rd);
+ CloseHandle(m_h_child_stdout_wr);
+
+ return TRUE;
+ }
+
+ /* Close unnessary handles. */
+
+ CloseHandle(pi_proc_info.hProcess);
+ CloseHandle(pi_proc_info.hThread);
+
+ return FALSE;
+}
+
+bool Mysqld_output_parser_win::read_line(char *line_buffer,
+ uint line_buffer_size)
+{
+ DWORD dw_read_count= m_internal_buffer_size;
+ bzero(line_buffer,line_buffer_size);
+ char *buff_ptr= line_buffer;
+ char ch;
+
+ while ((unsigned)(buff_ptr - line_buffer) < line_buffer_size)
+ {
+ do
+ {
+ ReadFile(m_h_child_stdout_rd, &ch,
+ 1, &dw_read_count, NULL);
+ } while ((ch == CR || ch == LF) && buff_ptr == line_buffer);
+
+ if (dw_read_count == 0)
+ return TRUE;
+
+ if (ch == CR || ch == LF)
+ break;
+
+ *buff_ptr++ = ch;
+ }
+
+ return FALSE;
+}
+
+bool Mysqld_output_parser_win::cleanup()
+{
+ /* Close all handles. */
+
+ CloseHandle(m_h_child_stdout_wr);
+ CloseHandle(m_h_child_stdout_rd);
+
+ return FALSE;
+}
+#endif
+
+/*************************************************************************/
+
+} /* End of private module implementation. */
+
+/*************************************************************************/
+
+/**
+ @brief Parse output of the given command
+
+ @param command The command to execute.
+ @param option_name_str Option name.
+ @param option_name_length Length of the option name.
+ @param[out] option_value_buf The buffer to store option value.
+ @param option_value_buf_size Size of the option value buffer.
+ @param option_type Type of the option:
+ - GET_LINE if we want to get all the
+ line after the option name;
+ - GET_VALUE otherwise.
+
+ Execute the process by running "command". Find the "option name" and
+ return the next word if "option_type" is GET_VALUE. Return the rest of
+ the parsed string otherwise.
+
+ @note This function has a separate windows implementation.
+
+ @return The error status.
+ @retval FALSE Ok, the option name has been found.
+ @retval TRUE Error occured or the option name is not found.
+*/
+
+bool parse_output_and_get_value(const char *command,
+ const char *option_name_str,
+ uint option_name_length,
+ char *option_value_buf,
+ size_t option_value_buf_size,
+ enum_option_type option_type)
+{
+#ifndef __WIN__
+ Mysqld_output_parser_unix parser;
+#else /* __WIN__ */
+ Mysqld_output_parser_win parser;
+#endif
+
+ return parser.parse(command,
+ option_name_str,
+ option_name_length,
+ option_value_buf,
+ option_value_buf_size,
+ option_type);
+}
diff --git a/server-tools/instance-manager/parse_output.h b/server-tools/instance-manager/parse_output.h
index c236357a200..41618f643a3 100644
--- a/server-tools/instance-manager/parse_output.h
+++ b/server-tools/instance-manager/parse_output.h
@@ -15,11 +15,19 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#define GET_VALUE 1
-#define GET_LINE 2
+#include <my_global.h>
-int parse_output_and_get_value(const char *command, const char *word,
- char *result, size_t input_buffer_len,
- uint flag);
+enum enum_option_type
+{
+ GET_VALUE = 1,
+ GET_LINE
+};
+
+bool parse_output_and_get_value(const char *command,
+ const char *option_name_str,
+ uint option_name_length,
+ char *option_value_buf,
+ size_t option_value_buf_size,
+ enum_option_type option_type);
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_OUTPUT_H */
diff --git a/server-tools/instance-manager/portability.h b/server-tools/instance-manager/portability.h
index 31bebd1b33a..990e6140a9e 100644
--- a/server-tools/instance-manager/portability.h
+++ b/server-tools/instance-manager/portability.h
@@ -34,11 +34,30 @@
/*TODO: fix this */
#define PROTOCOL_VERSION 10
+#define DFLT_CONFIG_FILE_NAME "my.ini"
+#define DFLT_MYSQLD_PATH "mysqld"
+#define DFLT_PASSWD_FILE_EXT ".passwd"
+#define DFLT_PID_FILE_EXT ".pid"
+#define DFLT_SOCKET_FILE_EXT ".sock"
+
typedef int pid_t;
#undef popen
#define popen(A,B) _popen(A,B)
+#define NEWLINE "\r\n"
+#define NEWLINE_LEN 2
+
+const char CR = '\r';
+const char LF = '\n';
+
+#else /* ! __WIN__ */
+
+#define NEWLINE "\n"
+#define NEWLINE_LEN 1
+
+const char LF = '\n';
+
#endif /* __WIN__ */
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PORTABILITY_H */
diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc
index 9e08189887e..74263934924 100644
--- a/server-tools/instance-manager/priv.cc
+++ b/server-tools/instance-manager/priv.cc
@@ -13,38 +13,19 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <my_global.h>
-#include <mysql_com.h>
#include "priv.h"
-#include "portability.h"
-#if defined(__ia64__) || defined(__ia64)
-/*
- We can live with 32K, but reserve 64K. Just to be safe.
- On ia64 we need to reserve double of the size.
-*/
-#define IM_THREAD_STACK_SIZE (128*1024L)
-#else
-#define IM_THREAD_STACK_SIZE (64*1024)
-#endif
-
-
-/* the pid of the manager process (of the signal thread on the LinuxThreads) */
-pid_t manager_pid;
+#include <my_global.h>
+#include <mysql_com.h>
+#include <my_sys.h>
-/*
- This flag is set if mysqlmanager has detected that it is running on the
- system using LinuxThreads
-*/
-bool linuxthreads;
+#include "log.h"
/*
The following string must be less then 80 characters, as
mysql_connection.cc relies on it
*/
-const char mysqlmanager_version[] = "0.2-alpha";
-
-const int mysqlmanager_version_length= sizeof(mysqlmanager_version) - 1;
+const LEX_STRING mysqlmanager_version= { C_STRING_WITH_LEN("1.0-beta") };
const unsigned char protocol_version= PROTOCOL_VERSION;
@@ -64,30 +45,32 @@ unsigned long bytes_sent = 0L, bytes_received = 0L;
unsigned long mysqld_net_retry_count = 10L;
unsigned long open_files_limit;
-/*
- Change the stack size and start a thread. Return an error if either
- pthread_attr_setstacksize or pthread_create fails.
- Arguments are the same as for pthread_create().
-*/
-int set_stacksize_n_create_thread(pthread_t *thread, pthread_attr_t *attr,
- void *(*start_routine)(void *), void *arg)
+
+bool create_pid_file(const char *pid_file_name, int pid)
{
- int rc= 0;
-
-#ifndef __WIN__
-#ifndef PTHREAD_STACK_MIN
-#define PTHREAD_STACK_MIN 32768
-#endif
- /*
- Set stack size to be safe on the platforms with too small
- default thread stack.
- */
- rc= pthread_attr_setstacksize(attr,
- (size_t) (PTHREAD_STACK_MIN +
- IM_THREAD_STACK_SIZE));
-#endif
- if (!rc)
- rc= pthread_create(thread, attr, start_routine, arg);
- return rc;
+ FILE *pid_file;
+
+ if (!(pid_file= my_fopen(pid_file_name, O_WRONLY | O_CREAT | O_BINARY,
+ MYF(0))))
+ {
+ log_error("Can not create pid file '%s': %s (errno: %d)",
+ (const char *) pid_file_name,
+ (const char *) strerror(errno),
+ (int) errno);
+ return TRUE;
+ }
+
+ if (fprintf(pid_file, "%d\n", (int) pid) <= 0)
+ {
+ log_error("Can not write to pid file '%s': %s (errno: %d)",
+ (const char *) pid_file_name,
+ (const char *) strerror(errno),
+ (int) errno);
+ return TRUE;
+ }
+
+ my_fclose(pid_file, MYF(0));
+
+ return FALSE;
}
diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h
index 2e55e0ac8e6..1c2124c0e77 100644
--- a/server-tools/instance-manager/priv.h
+++ b/server-tools/instance-manager/priv.h
@@ -16,13 +16,17 @@
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H
+#include <my_global.h>
+#include <m_string.h>
+#include <my_pthread.h>
+
#include <sys/types.h>
-#ifdef __WIN__
-#include "portability.h"
-#else
+
+#ifndef __WIN__
#include <unistd.h>
#endif
-#include "my_pthread.h"
+
+#include "portability.h"
/* IM-wide platform-independent defines */
#define SERVER_DEFAULT_PORT MYSQL_PORT
@@ -31,19 +35,22 @@
/* three-week timeout should be enough */
#define LONG_TIMEOUT ((ulong) 3600L*24L*21L)
-/* the pid of the manager process (of the signal thread on the LinuxThreads) */
-extern pid_t manager_pid;
+const int MEM_ROOT_BLOCK_SIZE= 512;
+
+/* The maximal length of option name and option value. */
+const int MAX_OPTION_LEN= 1024;
-#ifndef __WIN__
/*
- This flag is set if mysqlmanager has detected that it is running on the
- system using LinuxThreads
+ The maximal length of whole option string:
+ --<option name>=<option value>
*/
-extern bool linuxthreads;
-#endif
+const int MAX_OPTION_STR_LEN= 2 + MAX_OPTION_LEN + 1 + MAX_OPTION_LEN + 1;
-extern const char mysqlmanager_version[];
-extern const int mysqlmanager_version_length;
+const int MAX_VERSION_LENGTH= 160;
+
+const int MAX_INSTANCE_NAME_SIZE= FN_REFLEN;
+
+extern const LEX_STRING mysqlmanager_version;
/* MySQL client-server protocol version: substituted from configure */
extern const unsigned char protocol_version;
@@ -87,8 +94,6 @@ extern unsigned long bytes_sent, bytes_received;
extern unsigned long mysqld_net_retry_count;
extern unsigned long open_files_limit;
-
-int set_stacksize_n_create_thread(pthread_t *thread, pthread_attr_t *attr,
- void *(*start_routine)(void *), void *arg);
+bool create_pid_file(const char *pid_file_name, int pid);
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H
diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc
index faeee4e95e9..8d71fcb8026 100644
--- a/server-tools/instance-manager/protocol.cc
+++ b/server-tools/instance-manager/protocol.cc
@@ -21,7 +21,7 @@
#include <m_string.h>
-static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */
+static uchar eof_buff[1]= { (char) 254 }; /* Marker for end of fields */
static const char ERROR_PACKET_CODE= (char) 255;
@@ -38,7 +38,7 @@ int net_send_ok(struct st_net *net, unsigned long connection_id,
1-9 + message length message to send (isn't stored if no message)
*/
Buffer buff;
- char *pos= buff.buffer;
+ uchar *pos= buff.buffer;
/* check that we have space to hold mandatory fields */
buff.reserve(0, 23);
@@ -53,11 +53,11 @@ int net_send_ok(struct st_net *net, unsigned long connection_id,
int2store(pos, 0);
pos+= 2;
- uint position= (uint) (pos - buff.buffer); /* we might need it for message */
+ size_t position= pos - buff.buffer; /* we might need it for message */
if (message != NULL)
{
- buff.reserve(position, 9 + (uint) strlen(message));
+ buff.reserve(position, 9 + strlen(message));
store_to_protocol_packet(&buff, message, &position);
}
@@ -82,7 +82,8 @@ int net_send_error(struct st_net *net, uint sql_errno)
memcpy(pos, errno_to_sqlstate(sql_errno), SQLSTATE_LENGTH);
pos+= SQLSTATE_LENGTH;
pos= strmake(pos, err, MYSQL_ERRMSG_SIZE - 1) + 1;
- return my_net_write(net, buff, (uint) (pos - buff)) || net_flush(net);
+ return (my_net_write(net, (uchar*) buff, (size_t) (pos - buff)) ||
+ net_flush(net));
}
@@ -98,7 +99,8 @@ int net_send_error_323(struct st_net *net, uint sql_errno)
int2store(pos, sql_errno);
pos+= 2;
pos= strmake(pos, err, MYSQL_ERRMSG_SIZE - 1) + 1;
- return my_net_write(net, buff, (uint) (pos - buff)) || net_flush(net);
+ return (my_net_write(net, (uchar*) buff, (size_t) (pos - buff)) ||
+ net_flush(net));
}
char *net_store_length(char *pkg, uint length)
@@ -115,15 +117,15 @@ char *net_store_length(char *pkg, uint length)
}
-int store_to_protocol_packet(Buffer *buf, const char *string, uint *position,
- uint string_len)
+int store_to_protocol_packet(Buffer *buf, const char *string, size_t *position,
+ size_t string_len)
{
uint currpos;
/* reserve max amount of bytes needed to store length */
if (buf->reserve(*position, 9))
goto err;
- currpos= (uint) (net_store_length(buf->buffer + *position,
+ currpos= (net_store_length(buf->buffer + *position,
(ulonglong) string_len) - buf->buffer);
if (buf->append(currpos, string, string_len))
goto err;
@@ -135,11 +137,12 @@ err:
}
-int store_to_protocol_packet(Buffer *buf, const char *string, uint *position)
+int store_to_protocol_packet(Buffer *buf, const char *string,
+ size_t *position)
{
- uint string_len;
+ size_t string_len;
- string_len= (uint) strlen(string);
+ string_len= strlen(string);
return store_to_protocol_packet(buf, string, position, string_len);
}
@@ -153,16 +156,17 @@ int send_eof(struct st_net *net)
buff[0]=254;
int2store(buff+1, 0);
int2store(buff+3, 0);
- return my_net_write(net, (char*) buff, sizeof buff);
+ return my_net_write(net, buff, sizeof(buff));
}
+
int send_fields(struct st_net *net, LIST *fields)
{
LIST *tmp= fields;
Buffer send_buff;
- char small_buff[4];
- uint position= 0;
- NAME_WITH_LENGTH *field;
+ uchar small_buff[4];
+ size_t position= 0;
+ LEX_STRING *field;
/* send the number of fileds */
net_store_length(small_buff, (uint) list_length(fields));
@@ -172,7 +176,7 @@ int send_fields(struct st_net *net, LIST *fields)
while (tmp)
{
position= 0;
- field= (NAME_WITH_LENGTH *) tmp->data;
+ field= (LEX_STRING *) tmp->data;
store_to_protocol_packet(&send_buff,
(char*) "", &position); /* catalog name */
@@ -183,9 +187,9 @@ int send_fields(struct st_net *net, LIST *fields)
store_to_protocol_packet(&send_buff,
(char*) "", &position); /* table name alias */
store_to_protocol_packet(&send_buff,
- field->name, &position); /* column name */
+ field->str, &position); /* column name */
store_to_protocol_packet(&send_buff,
- field->name, &position); /* column name alias */
+ field->str, &position); /* column name alias */
send_buff.reserve(position, 12);
if (send_buff.is_error())
goto err;
@@ -193,7 +197,7 @@ int send_fields(struct st_net *net, LIST *fields)
int2store(send_buff.buffer + position, 1); /* charsetnr */
int4store(send_buff.buffer + position + 2,
field->length); /* field length */
- send_buff.buffer[position+6]= (char) FIELD_TYPE_STRING; /* type */
+ send_buff.buffer[position+6]= (char) MYSQL_TYPE_STRING; /* type */
int2store(send_buff.buffer + position + 7, 0); /* flags */
send_buff.buffer[position + 9]= (char) 0; /* decimals */
send_buff.buffer[position + 10]= 0;
diff --git a/server-tools/instance-manager/protocol.h b/server-tools/instance-manager/protocol.h
index b8cd4b3d807..b06ae4dfdb2 100644
--- a/server-tools/instance-manager/protocol.h
+++ b/server-tools/instance-manager/protocol.h
@@ -20,11 +20,6 @@
#include <my_list.h>
-typedef struct field {
- char *name;
- uint length;
-} NAME_WITH_LENGTH;
-
/* default field length to be used in various field-realted functions */
enum { DEFAULT_FIELD_LENGTH= 20 };
@@ -41,10 +36,11 @@ int send_fields(struct st_net *net, LIST *fields);
char *net_store_length(char *pkg, uint length);
-int store_to_protocol_packet(Buffer *buf, const char *string, uint *position);
+int store_to_protocol_packet(Buffer *buf, const char *string,
+ size_t *position);
-int store_to_protocol_packet(Buffer *buf, const char *string, uint *position,
- uint string_len);
+int store_to_protocol_packet(Buffer *buf, const char *string, size_t *position,
+ size_t string_len);
int send_eof(struct st_net *net);
diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc
index 5bb4206982e..489caa0aaa8 100644
--- a/server-tools/instance-manager/thread_registry.cc
+++ b/server-tools/instance-manager/thread_registry.cc
@@ -18,31 +18,29 @@
#endif
#include "thread_registry.h"
-
-#include "log.h"
-
-#include <assert.h>
-#include <signal.h>
#include <thr_alarm.h>
-
+#include <signal.h>
+#include "log.h"
#ifndef __WIN__
/* Kick-off signal handler */
enum { THREAD_KICK_OFF_SIGNAL= SIGUSR2 };
-static void handle_signal(int __attribute__((unused)) sig_no)
+extern "C" void handle_signal(int);
+
+void handle_signal(int __attribute__((unused)) sig_no)
{
}
#endif
-/*
- Thread_info initializer methods
-*/
+/* Thread_info initializer methods */
-Thread_info::Thread_info() {}
-Thread_info::Thread_info(pthread_t thread_id_arg) :
- thread_id(thread_id_arg) {}
+void Thread_info::init(bool send_signal_on_shutdown_arg)
+{
+ thread_id= pthread_self();
+ send_signal_on_shutdown= send_signal_on_shutdown_arg;
+}
/*
TODO: think about moving signal information (now it's shutdown_in_progress)
@@ -51,7 +49,7 @@ Thread_info::Thread_info(pthread_t thread_id_arg) :
*/
Thread_registry::Thread_registry() :
- shutdown_in_progress(false)
+ shutdown_in_progress(FALSE)
,sigwait_thread_pid(pthread_self())
,error_status(FALSE)
{
@@ -68,6 +66,12 @@ Thread_registry::~Thread_registry()
/* Check that no one uses the repository. */
pthread_mutex_lock(&LOCK_thread_registry);
+ for (Thread_info *ti= head.next; ti != &head; ti= ti->next)
+ {
+ log_error("Thread_registry: unregistered thread: %lu.",
+ (unsigned long) ti->thread_id);
+ }
+
/* All threads must unregister */
DBUG_ASSERT(head.next == &head);
@@ -83,8 +87,14 @@ Thread_registry::~Thread_registry()
points to the last node.
*/
-void Thread_registry::register_thread(Thread_info *info)
+void Thread_registry::register_thread(Thread_info *info,
+ bool send_signal_on_shutdown)
{
+ info->init(send_signal_on_shutdown);
+
+ DBUG_PRINT("info", ("Thread_registry: registering thread %lu...",
+ (unsigned long) info->thread_id));
+
#ifndef __WIN__
struct sigaction sa;
sa.sa_handler= handle_signal;
@@ -111,11 +121,19 @@ void Thread_registry::register_thread(Thread_info *info)
void Thread_registry::unregister_thread(Thread_info *info)
{
+ DBUG_PRINT("info", ("Thread_registry: unregistering thread %lu...",
+ (unsigned long) info->thread_id));
+
pthread_mutex_lock(&LOCK_thread_registry);
info->prev->next= info->next;
info->next->prev= info->prev;
+
if (head.next == &head)
+ {
+ DBUG_PRINT("info", ("Thread_registry: thread registry is empty!"));
pthread_cond_signal(&COND_thread_registry_is_empty);
+ }
+
pthread_mutex_unlock(&LOCK_thread_registry);
}
@@ -180,13 +198,8 @@ int Thread_registry::cond_timedwait(Thread_info *info, pthread_cond_t *cond,
void Thread_registry::deliver_shutdown()
{
- Thread_info *info;
- struct timespec shutdown_time;
- int error;
- set_timespec(shutdown_time, 1);
-
pthread_mutex_lock(&LOCK_thread_registry);
- shutdown_in_progress= true;
+ shutdown_in_progress= TRUE;
#ifndef __WIN__
/* to stop reading from the network we need to flush alarm queue */
@@ -198,29 +211,14 @@ void Thread_registry::deliver_shutdown()
process_alarm(THR_SERVER_ALARM);
#endif
- for (info= head.next; info != &head; info= info->next)
- {
- pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL);
- /*
- sic: race condition here, the thread may not yet fall into
- pthread_cond_wait.
- */
- if (info->current_cond)
- pthread_cond_signal(info->current_cond);
- }
/*
- The common practice is to test predicate before pthread_cond_wait.
- I don't do that here because the predicate is practically always false
- before wait - is_shutdown's been just set, and the lock's still not
- released - the only case when the predicate is false is when no other
- threads exist.
+ sic: race condition here, the thread may not yet fall into
+ pthread_cond_wait.
*/
- while (((error= pthread_cond_timedwait(&COND_thread_registry_is_empty,
- &LOCK_thread_registry,
- &shutdown_time)) != ETIMEDOUT &&
- error != ETIME) &&
- head.next != &head)
- ;
+
+ interrupt_threads();
+
+ wait_for_threads_to_unregister();
/*
If previous signals did not reach some threads, they must be sleeping
@@ -229,12 +227,31 @@ void Thread_registry::deliver_shutdown()
so this time everybody should be informed (presumably each worker can
get CPU during shutdown_time.)
*/
- for (info= head.next; info != &head; info= info->next)
+
+ interrupt_threads();
+
+ /* Get the last chance to threads to stop. */
+
+ wait_for_threads_to_unregister();
+
+#ifndef DBUG_OFF
+ /*
+ Print out threads, that didn't stopped. Thread_registry destructor will
+ probably abort the program if there is still any alive thread.
+ */
+
+ if (head.next != &head)
{
- pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL);
- if (info->current_cond)
- pthread_cond_signal(info->current_cond);
+ DBUG_PRINT("info", ("Thread_registry: non-stopped threads:"));
+
+ for (Thread_info *info= head.next; info != &head; info= info->next)
+ DBUG_PRINT("info", (" - %lu", (unsigned long) info->thread_id));
}
+ else
+ {
+ DBUG_PRINT("info", ("Thread_registry: all threads stopped."));
+ }
+#endif // DBUG_OFF
pthread_mutex_unlock(&LOCK_thread_registry);
}
@@ -246,6 +263,142 @@ void Thread_registry::request_shutdown()
}
+void Thread_registry::interrupt_threads()
+{
+ for (Thread_info *info= head.next; info != &head; info= info->next)
+ {
+ if (!info->send_signal_on_shutdown)
+ continue;
+
+ pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL);
+ if (info->current_cond)
+ pthread_cond_signal(info->current_cond);
+ }
+}
+
+
+void Thread_registry::wait_for_threads_to_unregister()
+{
+ struct timespec shutdown_time;
+
+ set_timespec(shutdown_time, 1);
+
+ DBUG_PRINT("info", ("Thread_registry: joining threads..."));
+
+ while (true)
+ {
+ if (head.next == &head)
+ {
+ DBUG_PRINT("info", ("Thread_registry: emptied."));
+ return;
+ }
+
+ int error= pthread_cond_timedwait(&COND_thread_registry_is_empty,
+ &LOCK_thread_registry,
+ &shutdown_time);
+
+ if (error == ETIMEDOUT || error == ETIME)
+ {
+ DBUG_PRINT("info", ("Thread_registry: threads shutdown timed out."));
+ return;
+ }
+ }
+}
+
+
+/*********************************************************************
+ class Thread
+*********************************************************************/
+
+#if defined(__ia64__) || defined(__ia64)
+/*
+ We can live with 32K, but reserve 64K. Just to be safe.
+ On ia64 we need to reserve double of the size.
+*/
+#define IM_THREAD_STACK_SIZE (128*1024L)
+#else
+#define IM_THREAD_STACK_SIZE (64*1024)
+#endif
+
+/*
+ Change the stack size and start a thread. Return an error if either
+ pthread_attr_setstacksize or pthread_create fails.
+ Arguments are the same as for pthread_create().
+*/
+
+static
+int set_stacksize_and_create_thread(pthread_t *thread, pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg)
+{
+ int rc= 0;
+
+#ifndef __WIN__
+#ifndef PTHREAD_STACK_MIN
+#define PTHREAD_STACK_MIN 32768
+#endif
+ /*
+ Set stack size to be safe on the platforms with too small
+ default thread stack.
+ */
+ rc= pthread_attr_setstacksize(attr,
+ (size_t) (PTHREAD_STACK_MIN +
+ IM_THREAD_STACK_SIZE));
+#endif
+ if (!rc)
+ rc= pthread_create(thread, attr, start_routine, arg);
+ return rc;
+}
+
+
+Thread::~Thread()
+{
+}
+
+
+void *Thread::thread_func(void *arg)
+{
+ Thread *thread= (Thread *) arg;
+ my_thread_init();
+
+ thread->run();
+
+ my_thread_end();
+ return NULL;
+}
+
+
+bool Thread::start(enum_thread_type thread_type)
+{
+ pthread_attr_t attr;
+ int rc;
+
+ pthread_attr_init(&attr);
+
+ if (thread_type == DETACHED)
+ {
+ detached = TRUE;
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ }
+ else
+ {
+ detached = FALSE;
+ }
+
+ rc= set_stacksize_and_create_thread(&id, &attr, Thread::thread_func, this);
+ pthread_attr_destroy(&attr);
+
+ return rc != 0;
+}
+
+
+bool Thread::join()
+{
+ DBUG_ASSERT(!detached);
+
+ return pthread_join(id, NULL) != 0;
+}
+
+
int Thread_registry::get_error_status()
{
int ret_error_status;
diff --git a/server-tools/instance-manager/thread_registry.h b/server-tools/instance-manager/thread_registry.h
index f6de49cd9ad..d04c8442e44 100644
--- a/server-tools/instance-manager/thread_registry.h
+++ b/server-tools/instance-manager/thread_registry.h
@@ -57,7 +57,7 @@
#pragma interface
#endif
-/*
+/**
Thread_info - repository entry for each worker thread
All entries comprise double-linked list like:
0 -- entry -- entry -- entry - 0
@@ -67,17 +67,63 @@
class Thread_info
{
public:
- Thread_info();
- Thread_info(pthread_t thread_id_arg);
+ Thread_info() {}
friend class Thread_registry;
private:
+ void init(bool send_signal_on_shutdown);
+private:
pthread_cond_t *current_cond;
Thread_info *prev, *next;
pthread_t thread_id;
+ bool send_signal_on_shutdown;
};
-/*
+/**
+ A base class for a detached thread.
+*/
+
+class Thread
+{
+public:
+ enum enum_thread_type
+ {
+ DETACHED,
+ JOINABLE
+ };
+public:
+ Thread()
+ { }
+
+public:
+ inline bool is_detached() const;
+
+ bool start(enum_thread_type thread_type = JOINABLE);
+ bool join();
+
+protected:
+ virtual void run()= 0;
+ virtual ~Thread();
+
+private:
+ pthread_t id;
+ bool detached;
+
+private:
+ static void *thread_func(void *arg);
+
+private:
+ Thread(const Thread & /* rhs */); /* not implemented */
+ Thread &operator=(const Thread & /* rhs */); /* not implemented */
+};
+
+inline bool Thread::is_detached() const
+{
+ return detached;
+}
+
+
+/**
Thread_registry - contains handles for each worker thread to deliver
signal information to workers.
*/
@@ -88,7 +134,7 @@ public:
Thread_registry();
~Thread_registry();
- void register_thread(Thread_info *info);
+ void register_thread(Thread_info *info, bool send_signal_on_shutdown= TRUE);
void unregister_thread(Thread_info *info);
void deliver_shutdown();
void request_shutdown();
@@ -101,12 +147,20 @@ public:
void set_error_status();
private:
+ void interrupt_threads();
+ void wait_for_threads_to_unregister();
+
+private:
Thread_info head;
bool shutdown_in_progress;
pthread_mutex_t LOCK_thread_registry;
pthread_cond_t COND_thread_registry_is_empty;
pthread_t sigwait_thread_pid;
bool error_status;
+
+private:
+ Thread_registry(const Thread_registry &);
+ Thread_registry &operator =(const Thread_registry &);
};
diff --git a/server-tools/instance-manager/user_management_commands.cc b/server-tools/instance-manager/user_management_commands.cc
new file mode 100644
index 00000000000..2eb0ae30aa5
--- /dev/null
+++ b/server-tools/instance-manager/user_management_commands.cc
@@ -0,0 +1,421 @@
+/* Copyright (C) 2006 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
+#pragma implementation
+#endif
+
+#include "user_management_commands.h"
+
+#include "exit_codes.h"
+#include "options.h"
+#include "user_map.h"
+
+/*************************************************************************
+ Module-specific (internal) functions.
+*************************************************************************/
+
+/*
+ The function returns user name. The user name is retrieved from command-line
+ options (if specified) or from console.
+
+ NOTE
+ This function must not be used in user-management command implementations.
+ Use get_user_name() instead.
+
+ SYNOPSIS
+ get_user_name_impl()
+
+ RETURN
+ NULL on error
+ valid pointer on success
+*/
+
+static char *get_user_name_impl()
+{
+ static char user_name_buf[1024];
+ char *ptr;
+
+ if (Options::User_management::user_name)
+ return Options::User_management::user_name;
+
+ printf("Enter user name: ");
+ fflush(stdout);
+
+ if (!fgets(user_name_buf, sizeof (user_name_buf), stdin))
+ return NULL;
+
+ if ((ptr= strchr(user_name_buf, '\n')))
+ *ptr= 0;
+
+ if ((ptr= strchr(user_name_buf, '\r')))
+ *ptr= 0;
+
+ return user_name_buf;
+}
+
+
+/*
+ The function is intended to provide user name for user-management
+ operations. It also checks that length of the specified user name is correct
+ (not empty, not exceeds USERNAME_LENGTH). Report to stderr if something is
+ wrong.
+
+ SYNOPSIS
+ get_user_name()
+ user_name [OUT] on success contains user name
+
+ RETURN
+ TRUE on error
+ FALSE on success
+*/
+
+static bool get_user_name(LEX_STRING *user_name)
+{
+ char *user_name_str= get_user_name_impl();
+
+ if (!user_name_str)
+ {
+ fprintf(stderr, "Error: unable to read user name from stdin.\n");
+ return TRUE;
+ }
+
+ user_name->str= user_name_str;
+ user_name->length= strlen(user_name->str);
+
+ if (user_name->length == 0)
+ {
+ fprintf(stderr, "Error: user name can not be empty.\n");
+ return TRUE;
+ }
+
+ if (user_name->length > USERNAME_LENGTH)
+ {
+ fprintf(stderr, "Error: user name must not exceed %d characters.\n",
+ (int) USERNAME_LENGTH);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/*
+ The function is intended to provide password for user-management operations.
+ The password is retrieved from command-line options (if specified) or from
+ console.
+
+ SYNOPSIS
+ get_password()
+
+ RETURN
+ NULL on error
+ valid pointer on success
+*/
+
+static const char *get_password()
+{
+ if (Options::User_management::password)
+ return Options::User_management::password;
+
+ const char *passwd1= get_tty_password("Enter password: ");
+ const char *passwd2= get_tty_password("Re-type password: ");
+
+ if (strcmp(passwd1, passwd2))
+ {
+ fprintf(stderr, "Error: passwords do not match.\n");
+ return 0;
+ }
+
+ return passwd1;
+}
+
+
+/*
+ Load password file into user map.
+
+ SYNOPSIS
+ load_password_file()
+ user_map target user map
+
+ RETURN
+ See exit_codes.h for possible values.
+*/
+
+static int load_password_file(User_map *user_map)
+{
+ int err_code;
+ const char *err_msg;
+
+ if (user_map->init())
+ {
+ fprintf(stderr, "Error: can not initialize user map.\n");
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ if ((err_code= user_map->load(Options::Main::password_file_name, &err_msg)))
+ fprintf(stderr, "Error: %s.\n", (const char *) err_msg);
+
+ return err_code;
+}
+
+
+/*
+ Save user map into password file.
+
+ SYNOPSIS
+ save_password_file()
+ user_map user map
+
+ RETURN
+ See exit_codes.h for possible values.
+*/
+
+static int save_password_file(User_map *user_map)
+{
+ int err_code;
+ const char *err_msg;
+
+ if ((err_code= user_map->save(Options::Main::password_file_name, &err_msg)))
+ fprintf(stderr, "Error: %s.\n", (const char *) err_msg);
+
+ return err_code;
+}
+
+/*************************************************************************
+ Print_password_line_cmd
+*************************************************************************/
+
+int Print_password_line_cmd::execute()
+{
+ LEX_STRING user_name;
+ const char *password;
+
+ printf("Creating record for new user.\n");
+
+ if (get_user_name(&user_name))
+ return ERR_CAN_NOT_READ_USER_NAME;
+
+ if (!(password= get_password()))
+ return ERR_CAN_NOT_READ_PASSWORD;
+
+ {
+ User user(&user_name, password);
+
+ printf("%s:%s\n",
+ (const char *) user.user,
+ (const char *) user.scrambled_password);
+ }
+
+ return ERR_OK;
+}
+
+
+/*************************************************************************
+ Add_user_cmd
+*************************************************************************/
+
+int Add_user_cmd::execute()
+{
+ LEX_STRING user_name;
+ const char *password;
+
+ User_map user_map;
+ User *new_user;
+
+ int err_code;
+
+ if (get_user_name(&user_name))
+ return ERR_CAN_NOT_READ_USER_NAME;
+
+ /* Load the password file. */
+
+ if ((err_code= load_password_file(&user_map)) != ERR_OK)
+ return err_code;
+
+ /* Check that the user does not exist. */
+
+ if (user_map.find_user(&user_name))
+ {
+ fprintf(stderr, "Error: user '%s' already exists.\n",
+ (const char *) user_name.str);
+ return ERR_USER_ALREADY_EXISTS;
+ }
+
+ /* Add the user. */
+
+ if (!(password= get_password()))
+ return ERR_CAN_NOT_READ_PASSWORD;
+
+ if (!(new_user= new User(&user_name, password)))
+ return ERR_OUT_OF_MEMORY;
+
+ if (user_map.add_user(new_user))
+ {
+ delete new_user;
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ /* Save the password file. */
+
+ return save_password_file(&user_map);
+}
+
+
+/*************************************************************************
+ Drop_user_cmd
+*************************************************************************/
+
+int Drop_user_cmd::execute()
+{
+ LEX_STRING user_name;
+
+ User_map user_map;
+ User *user;
+
+ int err_code;
+
+ if (get_user_name(&user_name))
+ return ERR_CAN_NOT_READ_USER_NAME;
+
+ /* Load the password file. */
+
+ if ((err_code= load_password_file(&user_map)) != ERR_OK)
+ return err_code;
+
+ /* Find the user. */
+
+ user= user_map.find_user(&user_name);
+
+ if (!user)
+ {
+ fprintf(stderr, "Error: user '%s' does not exist.\n",
+ (const char *) user_name.str);
+ return ERR_USER_NOT_FOUND;
+ }
+
+ /* Remove the user (ignore possible errors). */
+
+ user_map.remove_user(user);
+
+ /* Save the password file. */
+
+ return save_password_file(&user_map);
+}
+
+
+/*************************************************************************
+ Edit_user_cmd
+*************************************************************************/
+
+int Edit_user_cmd::execute()
+{
+ LEX_STRING user_name;
+ const char *password;
+
+ User_map user_map;
+ User *user;
+
+ int err_code;
+
+ if (get_user_name(&user_name))
+ return ERR_CAN_NOT_READ_USER_NAME;
+
+ /* Load the password file. */
+
+ if ((err_code= load_password_file(&user_map)) != ERR_OK)
+ return err_code;
+
+ /* Find the user. */
+
+ user= user_map.find_user(&user_name);
+
+ if (!user)
+ {
+ fprintf(stderr, "Error: user '%s' does not exist.\n",
+ (const char *) user_name.str);
+ return ERR_USER_NOT_FOUND;
+ }
+
+ /* Modify user's password. */
+
+ if (!(password= get_password()))
+ return ERR_CAN_NOT_READ_PASSWORD;
+
+ user->set_password(password);
+
+ /* Save the password file. */
+
+ return save_password_file(&user_map);
+}
+
+
+/*************************************************************************
+ Clean_db_cmd
+*************************************************************************/
+
+int Clean_db_cmd::execute()
+{
+ User_map user_map;
+
+ if (user_map.init())
+ {
+ fprintf(stderr, "Error: can not initialize user map.\n");
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ return save_password_file(&user_map);
+}
+
+
+/*************************************************************************
+ Check_db_cmd
+*************************************************************************/
+
+int Check_db_cmd::execute()
+{
+ User_map user_map;
+
+ return load_password_file(&user_map);
+}
+
+
+/*************************************************************************
+ List_users_cmd
+*************************************************************************/
+
+int List_users_cmd::execute()
+{
+ User_map user_map;
+
+ int err_code;
+
+ /* Load the password file. */
+
+ if ((err_code= load_password_file(&user_map)))
+ return err_code;
+
+ /* Print out registered users. */
+
+ {
+ User_map::Iterator it(&user_map);
+ User *user;
+
+ while ((user= it.next()))
+ fprintf(stderr, "%s\n", (const char *) user->user);
+ }
+
+ return ERR_OK;
+}
diff --git a/server-tools/instance-manager/user_management_commands.h b/server-tools/instance-manager/user_management_commands.h
new file mode 100644
index 00000000000..c925e6ae363
--- /dev/null
+++ b/server-tools/instance-manager/user_management_commands.h
@@ -0,0 +1,167 @@
+#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MANAGEMENT_CMD_H
+#define INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MANAGEMENT_CMD_H
+
+/*
+ Copyright (C) 2006 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
+*/
+
+/*
+ This header contains declarations of classes inteded to support
+ user-management commands (such as add user, get list of users, etc).
+
+ The general idea is to have one interface (pure abstract class) for such a
+ command. Each concrete user-management command is implemented in concrete
+ class, derived from the common interface.
+*/
+
+#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
+#pragma interface
+#endif
+
+/*************************************************************************
+ User_management_cmd -- base class for User-management commands.
+*************************************************************************/
+
+class User_management_cmd
+{
+public:
+ User_management_cmd()
+ { }
+
+ virtual ~User_management_cmd()
+ { }
+
+public:
+ /*
+ Executes user-management command.
+
+ SYNOPSIS
+ execute()
+
+ RETURN
+ See exit_codes.h for possible values.
+ */
+
+ virtual int execute() = 0;
+};
+
+
+/*************************************************************************
+ Print_password_line_cmd: support for --print-password-line command-line
+ option.
+*************************************************************************/
+
+class Print_password_line_cmd: public User_management_cmd
+{
+public:
+ Print_password_line_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ Add_user_cmd: support for --add-user command-line option.
+*************************************************************************/
+
+class Add_user_cmd: public User_management_cmd
+{
+public:
+ Add_user_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ Drop_user_cmd: support for --drop-user command-line option.
+*************************************************************************/
+
+class Drop_user_cmd: public User_management_cmd
+{
+public:
+ Drop_user_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ Edit_user_cmd: support for --edit-user command-line option.
+*************************************************************************/
+
+class Edit_user_cmd: public User_management_cmd
+{
+public:
+ Edit_user_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ Clean_db_cmd: support for --clean-db command-line option.
+*************************************************************************/
+
+class Clean_db_cmd: public User_management_cmd
+{
+public:
+ Clean_db_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ Check_db_cmd: support for --check-db command-line option.
+*************************************************************************/
+
+class Check_db_cmd: public User_management_cmd
+{
+public:
+ Check_db_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ List_users_cmd: support for --list-users command-line option.
+*************************************************************************/
+
+class List_users_cmd: public User_management_cmd
+{
+public:
+ List_users_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MANAGEMENT_CMD_H
diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc
index 7871cad7814..49c35c16ca9 100644
--- a/server-tools/instance-manager/user_map.cc
+++ b/server-tools/instance-manager/user_map.cc
@@ -18,33 +18,32 @@
#endif
#include "user_map.h"
-
-#include <mysql_com.h>
-#include <m_string.h>
-
+#include "exit_codes.h"
#include "log.h"
+#include "portability.h"
-struct User
+User::User(const LEX_STRING *user_name_arg, const char *password)
{
- char user[USERNAME_LENGTH + 1];
- uint8 user_length;
- uint8 salt[SCRAMBLE_LENGTH];
- User() {}
- int init(const char *line);
-};
-
+ user_length= (uint8) (strmake(user, user_name_arg->str,
+ USERNAME_LENGTH + 1) - user);
+ set_password(password);
+}
int User::init(const char *line)
{
const char *name_begin, *name_end, *password;
- int line_ending_len= 1;
+ int password_length;
if (line[0] == '\'' || line[0] == '"')
{
name_begin= line + 1;
name_end= strchr(name_begin, line[0]);
if (name_end == 0 || name_end[1] != ':')
- goto err;
+ {
+ log_error("Invalid format (unmatched quote) of user line (%s).",
+ (const char *) line);
+ return 1;
+ }
password= name_end + 2;
}
else
@@ -52,44 +51,58 @@ int User::init(const char *line)
name_begin= line;
name_end= strchr(name_begin, ':');
if (name_end == 0)
- goto err;
+ {
+ log_error("Invalid format (no delimiter) of user line (%s).",
+ (const char *) line);
+ return 1;
+ }
password= name_end + 1;
}
- user_length= (uint) (name_end - name_begin);
+
+ user_length= (uint8) (name_end - name_begin);
if (user_length > USERNAME_LENGTH)
- goto err;
-
- /*
- assume that newline characater is present
- we support reading password files that end in \n or \r\n on
- either platform.
- */
- if (password[strlen(password)-2] == '\r')
- line_ending_len= 2;
- if (strlen(password) != (uint) (SCRAMBLED_PASSWORD_CHAR_LENGTH +
- line_ending_len))
- goto err;
+ {
+ log_error("User name is too long (%d). Max length: %d. "
+ "User line: '%s'.",
+ (int) user_length,
+ (int) USERNAME_LENGTH,
+ (const char *) line);
+ return 1;
+ }
+
+ password_length= (int) strlen(password);
+ if (password_length > SCRAMBLED_PASSWORD_CHAR_LENGTH)
+ {
+ log_error("Password is too long (%d). Max length: %d."
+ "User line: '%s'.",
+ (int) password_length,
+ (int) SCRAMBLED_PASSWORD_CHAR_LENGTH,
+ (const char *) line);
+ return 1;
+ }
memcpy(user, name_begin, user_length);
user[user_length]= 0;
+
+ memcpy(scrambled_password, password, password_length);
+ scrambled_password[password_length]= 0;
+
get_salt_from_password(salt, password);
- log_info("loaded user %s", user);
+
+ log_info("Loaded user '%s'.", (const char *) user);
return 0;
-err:
- log_error("error parsing user and password at line %s", line);
- return 1;
}
C_MODE_START
-static byte* get_user_key(const byte* u, uint* len,
- my_bool __attribute__((unused)) t)
+static uchar* get_user_key(const uchar* u, size_t* len,
+ my_bool __attribute__((unused)) t)
{
const User *user= (const User *) u;
*len= user->user_length;
- return (byte *) user->user;
+ return (uchar *) user->user;
}
static void delete_user(void *u)
@@ -101,30 +114,70 @@ static void delete_user(void *u)
C_MODE_END
+void User_map::Iterator::reset()
+{
+ cur_idx= 0;
+}
+
+
+User *User_map::Iterator::next()
+{
+ if (cur_idx < user_map->hash.records)
+ return (User *) hash_element(&user_map->hash, cur_idx++);
+
+ return NULL;
+}
+
+
int User_map::init()
{
enum { START_HASH_SIZE= 16 };
if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
- get_user_key, delete_user, 0))
+ get_user_key, delete_user, 0))
return 1;
+
+ initialized= TRUE;
+
return 0;
}
+User_map::User_map()
+ :initialized(FALSE)
+{
+}
+
+
User_map::~User_map()
{
- hash_free(&hash);
+ if (initialized)
+ hash_free(&hash);
}
/*
- Load all users from the password file. Must be called once right after
- construction.
- In case of failure, puts error message to the log file and returns 1
+ Load password database.
+
+ SYNOPSIS
+ load()
+ password_file_name [IN] password file path
+ err_msg [OUT] error message
+
+ DESCRIPTION
+ Load all users from the password file. Must be called once right after
+ construction. In case of failure, puts error message to the log file and
+ returns specific error code.
+
+ RETURN
+ 0 on success
+ !0 on error
*/
-int User_map::load(const char *password_file_name)
+int User_map::load(const char *password_file_name, const char **err_msg)
{
+ static const int ERR_MSG_BUF_SIZE = 255;
+ static char err_msg_buf[ERR_MSG_BUF_SIZE];
+
FILE *file;
char line[USERNAME_LENGTH + SCRAMBLED_PASSWORD_CHAR_LENGTH +
2 + /* for possible quotes */
@@ -132,35 +185,173 @@ int User_map::load(const char *password_file_name)
2 + /* for newline */
1]; /* for trailing zero */
User *user;
- int rc= 1;
+
+ if (my_access(password_file_name, F_OK) != 0)
+ {
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "password file (%s) does not exist",
+ (const char *) password_file_name);
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_PASSWORD_FILE_DOES_NOT_EXIST;
+ }
if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0)
{
- /* Probably the password file wasn't specified. Try to leave without it */
- log_info("[WARNING] can't open password file %s: errno=%d, %s", password_file_name,
- errno, strerror(errno));
- return 0;
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "can not open password file (%s): %s",
+ (const char *) password_file_name,
+ (const char *) strerror(errno));
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_IO_ERROR;
}
+ log_info("Loading the password database...");
+
while (fgets(line, sizeof(line), file))
{
+ char *user_line= line;
+
+ /*
+ We need to skip EOL-symbols also from the beginning of the line, because
+ if the previous line was ended by \n\r sequence, we get \r in our line.
+ */
+
+ while (user_line[0] == '\r' || user_line[0] == '\n')
+ ++user_line;
+
+ /* Skip EOL-symbols in the end of the line. */
+
+ {
+ char *ptr;
+
+ if ((ptr= strchr(user_line, '\n')))
+ *ptr= 0;
+
+ if ((ptr= strchr(user_line, '\r')))
+ *ptr= 0;
+ }
+
/* skip comments and empty lines */
- if (line[0] == '#' || line[0] == '\n' &&
- (line[1] == '\0' || line[1] == '\r'))
+ if (!user_line[0] || user_line[0] == '#')
continue;
+
if ((user= new User) == 0)
- goto done;
- if (user->init(line) || my_hash_insert(&hash, (byte *) user))
- goto err_init_user;
+ {
+ my_fclose(file, MYF(0));
+
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "out of memory while parsing password file (%s)",
+ (const char *) password_file_name);
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ if (user->init(user_line))
+ {
+ delete user;
+ my_fclose(file, MYF(0));
+
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "password file (%s) corrupted",
+ (const char *) password_file_name);
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_PASSWORD_FILE_CORRUPTED;
+ }
+
+ if (my_hash_insert(&hash, (uchar *) user))
+ {
+ delete user;
+ my_fclose(file, MYF(0));
+
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "out of memory while parsing password file (%s)",
+ (const char *) password_file_name);
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_OUT_OF_MEMORY;
+ }
}
- if (feof(file))
- rc= 0;
- goto done;
-err_init_user:
- delete user;
-done:
+
+ log_info("The password database loaded successfully.");
+
my_fclose(file, MYF(0));
- return rc;
+
+ if (err_msg)
+ *err_msg= NULL;
+
+ return ERR_OK;
+}
+
+
+int User_map::save(const char *password_file_name, const char **err_msg)
+{
+ static const int ERR_MSG_BUF_SIZE = 255;
+ static char err_msg_buf[ERR_MSG_BUF_SIZE];
+
+ FILE *file;
+
+ if ((file= my_fopen(password_file_name, O_WRONLY | O_TRUNC | O_BINARY,
+ MYF(0))) == 0)
+ {
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "can not open password file (%s) for writing: %s",
+ (const char *) password_file_name,
+ (const char *) strerror(errno));
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_IO_ERROR;
+ }
+
+ {
+ User_map::Iterator it(this);
+ User *user;
+
+ while ((user= it.next()))
+ {
+ if (fprintf(file, "%s:%s\n", (const char *) user->user,
+ (const char *) user->scrambled_password) < 0)
+ {
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "can not write to password file (%s): %s",
+ (const char *) password_file_name,
+ (const char *) strerror(errno));
+ *err_msg= err_msg_buf;
+ }
+
+ my_fclose(file, MYF(0));
+
+ return ERR_IO_ERROR;
+ }
+ }
+ }
+
+ my_fclose(file, MYF(0));
+
+ return ERR_OK;
}
@@ -172,13 +363,33 @@ done:
2 - user not found
*/
-int User_map::authenticate(const char *user_name, uint length,
+int User_map::authenticate(const LEX_STRING *user_name,
const char *scrambled_password,
const char *scramble) const
{
- const User *user= (const User *) hash_search((HASH *) &hash,
- (byte *) user_name, length);
- if (user)
- return check_scramble(scrambled_password, scramble, user->salt);
- return 2;
+ const User *user= find_user(user_name);
+ return user ? check_scramble(scrambled_password, scramble, user->salt) : 2;
+}
+
+
+User *User_map::find_user(const LEX_STRING *user_name)
+{
+ return (User*) hash_search(&hash, (uchar*) user_name->str, user_name->length);
+}
+
+const User *User_map::find_user(const LEX_STRING *user_name) const
+{
+ return const_cast<User_map *> (this)->find_user(user_name);
+}
+
+
+bool User_map::add_user(User *user)
+{
+ return my_hash_insert(&hash, (uchar*) user) == 0 ? FALSE : TRUE;
+}
+
+
+bool User_map::remove_user(User *user)
+{
+ return hash_delete(&hash, (uchar*) user) == 0 ? FALSE : TRUE;
}
diff --git a/server-tools/instance-manager/user_map.h b/server-tools/instance-manager/user_map.h
index ca9bc861bda..6168f2b04fa 100644
--- a/server-tools/instance-manager/user_map.h
+++ b/server-tools/instance-manager/user_map.h
@@ -17,14 +17,35 @@
#define INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H
#include <my_global.h>
-
#include <my_sys.h>
+#include <mysql_com.h>
+#include <m_string.h>
#include <hash.h>
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
+struct User
+{
+ User()
+ {}
+
+ User(const LEX_STRING *user_name_arg, const char *password);
+
+ int init(const char *line);
+
+ inline void set_password(const char *password)
+ {
+ make_scrambled_password(scrambled_password, password);
+ }
+
+ char user[USERNAME_LENGTH + 1];
+ char scrambled_password[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1];
+ uint8 user_length;
+ uint8 salt[SCRAMBLE_LENGTH];
+};
+
/*
User_map -- all users and passwords
*/
@@ -32,16 +53,51 @@
class User_map
{
public:
- User_map() {}
+ /* User_map iterator */
+
+ class Iterator
+ {
+ public:
+ Iterator(User_map *user_map_arg) :
+ user_map(user_map_arg), cur_idx(0)
+ { }
+
+ public:
+ void reset();
+
+ User *next();
+
+ private:
+ User_map *user_map;
+ uint cur_idx;
+ };
+
+public:
+ User_map();
~User_map();
int init();
- int load(const char *password_file_name);
- int authenticate(const char *user_name, uint length,
+ int load(const char *password_file_name, const char **err_msg);
+ int save(const char *password_file_name, const char **err_msg);
+ int authenticate(const LEX_STRING *user_name,
const char *scrambled_password,
const char *scramble) const;
+
+ const User *find_user(const LEX_STRING *user_name) const;
+ User *find_user(const LEX_STRING *user_name);
+
+ bool add_user(User *user);
+ bool remove_user(User *user);
+
+private:
+ User_map(const User_map &);
+ User_map &operator =(const User_map &);
+
private:
HASH hash;
+ bool initialized;
+
+ friend class Iterator;
};
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H