Patch for IM in scope of working on BUG#24415: Instance manager test

im_daemon_life_cycle fails randomly.

1. Move IM-angel functionality into a separate file, create Angel class.
2. Be more verbose;
3. Fix typo in FLUSH INSTANCES implementation;
4. Polishing.
parent 3febb266
--------------------------------------------------------------------
server_id = 1
server_id = 2
--------------------------------------------------------------------
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
SHOW INSTANCES;
instance_name state
mysqld1 starting
mysqld1 XXXXX
mysqld2 offline
UNSET mysqld1.server_id;
ERROR HY000: The instance is active. Stop the instance first
......@@ -86,7 +82,7 @@ UNSET mysqld2.aaa, mysqld3.bbb, mysqld2.ccc, mysqld3.ddd;
--------------------------------------------------------------------
--------------------------------------------------------------------
SET mysqld2.aaa, mysqld3.bbb, mysqld.ccc = 0010;
ERROR HY000: Bad instance name. Check that the instance with such a name exists
ERROR HY000: Unknown instance name
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
......@@ -98,7 +94,7 @@ ERROR HY000: The instance is active. Stop the instance first
--------------------------------------------------------------------
--------------------------------------------------------------------
UNSET mysqld2.server_id, mysqld3.server_id, mysqld.ccc;
ERROR HY000: Bad instance name. Check that the instance with such a name exists
ERROR HY000: Unknown instance name
--------------------------------------------------------------------
server_id = 1
server_id=2
......
......@@ -26,7 +26,7 @@
--echo
--echo --> Printing out line for 'testuser'...
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --print-password-line --username=testuser --password=abc | tail -1
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --print-password-line --username=testuser --password=abc | tail -2 | head -1
--echo
--echo --> Listing users...
......@@ -45,7 +45,7 @@
--echo
--echo --> Printing out line for 'testuser'...
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --print-password-line --username=testuser --password=xyz | tail -1
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --print-password-line --username=testuser --password=xyz | tail -2 | head -1
--echo
--echo --> Listing users...
......
......@@ -15,17 +15,19 @@
#include <windows.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)
......@@ -60,50 +62,63 @@ void IMService::Log(const char *msg)
log_info(msg);
}
int HandleServiceOptions()
int IMService::main()
{
int ret_val= 0;
IMService winService;
if (Options::Service::install_as_service)
{
if (winService.IsInstalled())
{
log_info("Service is already installed.");
else if (winService.Install())
return 1;
}
if (winService.Install(IM_SVC_USERNAME, IM_SVC_PASSWORD))
{
log_info("Service installed successfully.");
return 0;
}
else
{
log_error("Service failed to install.");
ret_val= 1;
return 1;
}
}
else if (Options::Service::remove_service)
if (Options::Service::remove_service)
{
if (! winService.IsInstalled())
if (!winService.IsInstalled())
{
log_info("Service is not installed.");
else if (winService.Remove())
return 1;
}
if (winService.Remove())
{
log_info("Service removed successfully.");
return 0;
}
else
{
log_error("Service failed to remove.");
ret_val= 1;
return 1;
}
}
else
log_info("Initializing Instance Manager service...");
if (!winService.Init())
{
log_info("Initializing Instance Manager service...");
log_error("Service failed to initialize.");
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.");
ret_val= 1;
}
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 ret_val;
return 0;
}
......@@ -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
{
public:
static int main();
private:
IMService(void);
~IMService(void);
......@@ -27,5 +30,3 @@ protected:
void Stop();
void Run(DWORD argc, LPTSTR *argv);
};
extern int HandleServiceOptions();
......@@ -80,7 +80,9 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
portability.h \
exit_codes.h \
user_management_commands.h \
user_management_commands.cc
user_management_commands.cc \
angel.h \
angel.cc
mysqlmanager_LDADD= @CLIENT_EXTRA_LDFLAGS@ \
liboptions.la \
......
......@@ -14,19 +14,29 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#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),
serviceName(p_serviceName),
displayName(p_displayName),
inited(FALSE),
dwAcceptedControls(SERVICE_ACCEPT_STOP),
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,7 +45,7 @@ WindowsService::~WindowsService(void)
{
}
BOOL WindowsService::Install()
BOOL WindowsService::Install(const char *username, const char *password)
{
bool ret_val= FALSE;
SC_HANDLE newService;
......@@ -70,7 +80,7 @@ BOOL WindowsService::Install()
BOOL WindowsService::Init()
{
assert(serviceName != NULL);
DBUG_ASSERT(serviceName != NULL);
if (inited)
return TRUE;
......@@ -207,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);
......@@ -215,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);
}
......@@ -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,7 +28,7 @@ protected:
bool debugging;
public:
WindowsService(void);
WindowsService(const char *p_serviceName, const char *p_displayName);
~WindowsService(void);
BOOL Install();
......
/* 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 <signal.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
/*
Include my_global.h right after system includes so that we can change
system defines if needed.
*/
#include "my_global.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 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 status, 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 child_exit_status;
if (waitpid(0, &child_exit_status, WNOHANG) > 0)
{
child_status= WIFSIGNALED(child_exit_status) ?
CHILD_NEED_RESPAWN :
CHILD_EXIT_ANGEL;
}
}
/************************************************************************/
/**
SIGTERM, SIGHUP, SIGINT handler.
Set termination status and return.
*************************************************************************/
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. */
DBUG_ASSERT(!sigaction(SIGCHLD, &sa_chld, &sa_chld_orig));
DBUG_ASSERT(!sigaction(SIGTERM, &sa_term, &sa_term_orig));
DBUG_ASSERT(!sigaction(SIGINT, &sa_term, &sa_int_orig));
DBUG_ASSERT(!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. */
DBUG_ASSERT(!sigaction(SIGCHLD, &sa_chld_orig, NULL));
DBUG_ASSERT(!sigaction(SIGTERM, &sa_term_orig, NULL));
DBUG_ASSERT(!sigaction(SIGINT, &sa_int_orig, NULL));
DBUG_ASSERT(!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.");
log_info("Angel: sleeping 1 second...");
sleep(1); /* don't respawn too fast */
log_info("Angel: respawning Manager...");
continue;
}
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()
{
int ret_status;
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__
/* 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__
......@@ -210,8 +210,10 @@ int Show_instances::write_data(st_net *net)
int Flush_instances::execute(st_net *net, ulong connection_id)
{
if (Manager::flush_instances())
return ER_OUT_OF_RESOURCES;
int err_status= Manager::flush_instances();
if (err_status)
return err_status;
return net_send_ok(net, connection_id, NULL) ? ER_OUT_OF_RESOURCES : 0;
}
......
......@@ -29,6 +29,8 @@
#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"
......@@ -205,14 +207,16 @@ 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("Can not determine thread model.");
log_error("Manager: can not determine thread model.");
return 1;
}
log_info("Detected threads model: %s.",
log_info("Manager: detected threads model: %s.",
(const char *) (linux_threads ? "LINUX threads" : "POSIX threads"));
#endif // __WIN__
......@@ -250,7 +254,7 @@ int Manager::main()
if (instance_map.init())
{
log_error("Can not initialize instance list: out of memory.");
log_error("Manager: can not initialize instance list: out of memory.");
return 1;
}
......@@ -258,7 +262,7 @@ int Manager::main()
if (user_map.init())
{
log_error("Can not initialize user list: out of memory.");
log_error("Manager: can not initialize user list: out of memory.");
return 1;
}
......@@ -277,20 +281,19 @@ int Manager::main()
}
else
{
log_error("%s.", (const char *) err_msg);
log_error("Manager: %s.", (const char *) err_msg);
return 1;
}
}
/* Write Instance Manager pid file. */
log_info("IM pid file: '%s'; PID: %d.",
(const char *) Options::Main::pid_file_name,
(int) manager_pid);
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.
......@@ -326,7 +329,7 @@ int Manager::main()
if (guardian.start(Thread::DETACHED))
{
log_error("Can not start Guardian thread.");
log_error("Manager: can not start Guardian thread.");
goto err;
}
......@@ -334,7 +337,7 @@ int Manager::main()
if (Manager::flush_instances())
{
log_error("Can not init instances repository.");
log_error("Manager: can not init instances repository.");
stop_all_threads();
goto err;
}
......@@ -343,7 +346,7 @@ int Manager::main()
if (listener.start(Thread::DETACHED))
{
log_error("Can not start Listener thread.");
log_error("Manager: can not start Listener thread.");
stop_all_threads();
goto err;
}
......@@ -366,7 +369,7 @@ int Manager::main()
if ((status= my_sigwait(&mask, &signo)) != 0)
{
log_error("sigwait() failed");
log_error("Manager: sigwait() failed");
stop_all_threads();
goto err;
}
......@@ -426,7 +429,6 @@ err:
#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 rc;
}
......@@ -460,34 +462,41 @@ err:
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
*/
bool Manager::flush_instances()
int Manager::flush_instances()
{
p_instance_map->lock();
if (p_instance_map->is_there_active_instance())
{
p_instance_map->unlock();
return TRUE;
return ER_THERE_IS_ACTIVE_INSTACE;
}
if (p_instance_map->reset())
{
p_instance_map->unlock();
return TRUE;
return ER_OUT_OF_RESOURCES;
}
if (p_instance_map->load())
{
p_instance_map->unlock();
return TRUE; /* Don't init guardian if we failed to load instances. */
/* Don't init guardian if we failed to load instances. */
return ER_OUT_OF_RESOURCES;
}
get_guardian()->init(); /* TODO: check error status. */
get_guardian()->init();
get_guardian()->ping();
p_instance_map->unlock();
return FALSE;
return 0;
}
......@@ -32,7 +32,7 @@ class Manager
public:
static int main();
static bool flush_instances();
static int flush_instances();
public:
/**
......
......@@ -47,7 +47,7 @@ unsigned long open_files_limit;
int create_pid_file(const char *pid_file_name, int pid)
bool create_pid_file(const char *pid_file_name, int pid)
{
FILE *pid_file;
......@@ -58,7 +58,7 @@ int create_pid_file(const char *pid_file_name, int pid)
(const char *) pid_file_name,
(const char *) strerror(errno),
(int) errno);
return 1;
return TRUE;
}
if (fprintf(pid_file, "%d\n", (int) pid) <= 0)
......@@ -67,10 +67,10 @@ int create_pid_file(const char *pid_file_name, int pid)
(const char *) pid_file_name,
(const char *) strerror(errno),
(int) errno);
return 1;
return TRUE;
}
my_fclose(pid_file, MYF(0));
return 0;
return FALSE;
}
......@@ -94,6 +94,6 @@ extern unsigned long bytes_sent, bytes_received;
extern unsigned long mysqld_net_retry_count;
extern unsigned long open_files_limit;
int create_pid_file(const char *pid_file_name, int pid);
bool create_pid_file(const char *pid_file_name, int pid);
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment