Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mariadb
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
aabeeccf
Commit
aabeeccf
authored
Oct 06, 2009
by
Alexander Nozdrin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Backport WL#4085: Merge revno:2476.1105.1 from 6.0.
parent
ca8706ff
Changes
55
Hide whitespace changes
Inline
Side-by-side
Showing
55 changed files
with
0 additions
and
13103 deletions
+0
-13103
server-tools/CMakeLists.txt
server-tools/CMakeLists.txt
+0
-33
server-tools/Makefile.am
server-tools/Makefile.am
+0
-20
server-tools/instance-manager/CMakeLists.txt
server-tools/instance-manager/CMakeLists.txt
+0
-38
server-tools/instance-manager/IMService.cpp
server-tools/instance-manager/IMService.cpp
+0
-124
server-tools/instance-manager/IMService.h
server-tools/instance-manager/IMService.h
+0
-32
server-tools/instance-manager/Makefile.am
server-tools/instance-manager/Makefile.am
+0
-103
server-tools/instance-manager/README
server-tools/instance-manager/README
+0
-11
server-tools/instance-manager/WindowsService.cpp
server-tools/instance-manager/WindowsService.cpp
+0
-231
server-tools/instance-manager/WindowsService.h
server-tools/instance-manager/WindowsService.h
+0
-56
server-tools/instance-manager/angel.cc
server-tools/instance-manager/angel.cc
+0
-407
server-tools/instance-manager/angel.h
server-tools/instance-manager/angel.h
+0
-34
server-tools/instance-manager/buffer.cc
server-tools/instance-manager/buffer.cc
+0
-110
server-tools/instance-manager/buffer.h
server-tools/instance-manager/buffer.h
+0
-65
server-tools/instance-manager/command.cc
server-tools/instance-manager/command.cc
+0
-30
server-tools/instance-manager/command.h
server-tools/instance-manager/command.h
+0
-60
server-tools/instance-manager/commands.cc
server-tools/instance-manager/commands.cc
+0
-1752
server-tools/instance-manager/commands.h
server-tools/instance-manager/commands.h
+0
-393
server-tools/instance-manager/exit_codes.h
server-tools/instance-manager/exit_codes.h
+0
-40
server-tools/instance-manager/guardian.cc
server-tools/instance-manager/guardian.cc
+0
-496
server-tools/instance-manager/guardian.h
server-tools/instance-manager/guardian.h
+0
-110
server-tools/instance-manager/instance.cc
server-tools/instance-manager/instance.cc
+0
-944
server-tools/instance-manager/instance.h
server-tools/instance-manager/instance.h
+0
-273
server-tools/instance-manager/instance_map.cc
server-tools/instance-manager/instance_map.cc
+0
-649
server-tools/instance-manager/instance_map.h
server-tools/instance-manager/instance_map.h
+0
-102
server-tools/instance-manager/instance_options.cc
server-tools/instance-manager/instance_options.cc
+0
-753
server-tools/instance-manager/instance_options.h
server-tools/instance-manager/instance_options.h
+0
-126
server-tools/instance-manager/listener.cc
server-tools/instance-manager/listener.cc
+0
-337
server-tools/instance-manager/listener.h
server-tools/instance-manager/listener.h
+0
-61
server-tools/instance-manager/log.cc
server-tools/instance-manager/log.cc
+0
-196
server-tools/instance-manager/log.h
server-tools/instance-manager/log.h
+0
-59
server-tools/instance-manager/manager.cc
server-tools/instance-manager/manager.cc
+0
-526
server-tools/instance-manager/manager.h
server-tools/instance-manager/manager.h
+0
-71
server-tools/instance-manager/messages.cc
server-tools/instance-manager/messages.cc
+0
-104
server-tools/instance-manager/messages.h
server-tools/instance-manager/messages.h
+0
-23
server-tools/instance-manager/mysql_connection.cc
server-tools/instance-manager/mysql_connection.cc
+0
-376
server-tools/instance-manager/mysql_connection.h
server-tools/instance-manager/mysql_connection.h
+0
-74
server-tools/instance-manager/mysql_manager_error.h
server-tools/instance-manager/mysql_manager_error.h
+0
-40
server-tools/instance-manager/mysqlmanager.cc
server-tools/instance-manager/mysqlmanager.cc
+0
-232
server-tools/instance-manager/options.cc
server-tools/instance-manager/options.cc
+0
-558
server-tools/instance-manager/options.h
server-tools/instance-manager/options.h
+0
-108
server-tools/instance-manager/parse.cc
server-tools/instance-manager/parse.cc
+0
-509
server-tools/instance-manager/parse.h
server-tools/instance-manager/parse.h
+0
-212
server-tools/instance-manager/parse_output.cc
server-tools/instance-manager/parse_output.cc
+0
-407
server-tools/instance-manager/parse_output.h
server-tools/instance-manager/parse_output.h
+0
-33
server-tools/instance-manager/portability.h
server-tools/instance-manager/portability.h
+0
-65
server-tools/instance-manager/priv.cc
server-tools/instance-manager/priv.cc
+0
-76
server-tools/instance-manager/priv.h
server-tools/instance-manager/priv.h
+0
-99
server-tools/instance-manager/protocol.cc
server-tools/instance-manager/protocol.cc
+0
-217
server-tools/instance-manager/protocol.h
server-tools/instance-manager/protocol.h
+0
-47
server-tools/instance-manager/thread_registry.cc
server-tools/instance-manager/thread_registry.cc
+0
-419
server-tools/instance-manager/thread_registry.h
server-tools/instance-manager/thread_registry.h
+0
-176
server-tools/instance-manager/user_management_commands.cc
server-tools/instance-manager/user_management_commands.cc
+0
-421
server-tools/instance-manager/user_management_commands.h
server-tools/instance-manager/user_management_commands.h
+0
-167
server-tools/instance-manager/user_map.cc
server-tools/instance-manager/user_map.cc
+0
-395
server-tools/instance-manager/user_map.h
server-tools/instance-manager/user_map.h
+0
-103
No files found.
server-tools/CMakeLists.txt
deleted
100644 → 0
View file @
ca8706ff
# 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
SET
(
CMAKE_CXX_FLAGS_DEBUG
"
${
CMAKE_CXX_FLAGS_DEBUG
}
-DSAFEMALLOC -DSAFE_MUTEX"
)
SET
(
CMAKE_C_FLAGS_DEBUG
"
${
CMAKE_C_FLAGS_DEBUG
}
-DSAFEMALLOC -DSAFE_MUTEX"
)
ADD_DEFINITIONS
(
-DMYSQL_SERVER -DMYSQL_INSTANCE_MANAGER
)
INCLUDE_DIRECTORIES
(
${
PROJECT_SOURCE_DIR
}
/include
${
PROJECT_SOURCE_DIR
}
/sql
${
PROJECT_SOURCE_DIR
}
/extra/yassl/include
)
ADD_EXECUTABLE
(
mysqlmanager buffer.cc command.cc commands.cc guardian.cc instance.cc instance_map.cc
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
)
ADD_DEPENDENCIES
(
mysqlmanager GenError
)
TARGET_LINK_LIBRARIES
(
mysqlmanager dbug mysys strings taocrypt vio yassl zlib wsock32
)
server-tools/Makefile.am
deleted
100644 → 0
View file @
ca8706ff
# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
SUBDIRS
=
.
instance-manager
DIST_SUBDIRS
=
.
instance-manager
# Don't update the files from bitkeeper
%
::
SCCS/s.%
server-tools/instance-manager/CMakeLists.txt
deleted
100755 → 0
View file @
ca8706ff
# 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
INCLUDE
(
"
${
PROJECT_SOURCE_DIR
}
/win/mysql_manifest.cmake"
)
SET
(
CMAKE_CXX_FLAGS_DEBUG
"
${
CMAKE_CXX_FLAGS_DEBUG
}
-DSAFEMALLOC -DSAFE_MUTEX"
)
SET
(
CMAKE_C_FLAGS_DEBUG
"
${
CMAKE_C_FLAGS_DEBUG
}
-DSAFEMALLOC -DSAFE_MUTEX"
)
ADD_DEFINITIONS
(
-DMYSQL_SERVER -DMYSQL_INSTANCE_MANAGER
)
INCLUDE_DIRECTORIES
(
${
PROJECT_SOURCE_DIR
}
/include
${
PROJECT_SOURCE_DIR
}
/sql
${
PROJECT_SOURCE_DIR
}
/extra/yassl/include
)
ADD_EXECUTABLE
(
mysqlmanager buffer.cc command.cc commands.cc guardian.cc instance.cc instance_map.cc
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
)
ADD_DEPENDENCIES
(
mysqlmanager GenError
)
TARGET_LINK_LIBRARIES
(
mysqlmanager debug dbug mysys strings taocrypt vio yassl zlib wsock32
)
IF
(
EMBED_MANIFESTS
)
MYSQL_EMBED_MANIFEST
(
"mysqlmanager"
"asInvoker"
)
ENDIF
(
EMBED_MANIFESTS
)
server-tools/instance-manager/IMService.cpp
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2005 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 */
#include <winsock2.h>
#include <signal.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"
)
{
}
IMService
::~
IMService
(
void
)
{
}
void
IMService
::
Stop
()
{
ReportStatus
(
SERVICE_STOP_PENDING
);
/* stop the IM work */
raise
(
SIGTERM
);
}
void
IMService
::
Run
(
DWORD
argc
,
LPTSTR
*
argv
)
{
/* report to the SCM that we're about to start */
ReportStatus
((
DWORD
)
SERVICE_START_PENDING
);
Options
::
load
(
argc
,
argv
);
/* init goes here */
ReportStatus
((
DWORD
)
SERVICE_RUNNING
);
/* wait for main loop to terminate */
(
void
)
Manager
::
main
();
Options
::
cleanup
();
}
void
IMService
::
Log
(
const
char
*
msg
)
{
log_info
(
msg
);
}
int
IMService
::
main
()
{
IMService
winService
;
if
(
Options
::
Service
::
install_as_service
)
{
if
(
winService
.
IsInstalled
())
{
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_error
(
"Service failed to install."
);
return
1
;
}
}
if
(
Options
::
Service
::
remove_service
)
{
if
(
!
winService
.
IsInstalled
())
{
log_info
(
"Service is not installed."
);
return
1
;
}
if
(
winService
.
Remove
())
{
log_info
(
"Service removed successfully."
);
return
0
;
}
else
{
log_error
(
"Service failed to remove."
);
return
1
;
}
}
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
;
}
server-tools/instance-manager/IMService.h
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2005 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 */
#pragma once
#include "WindowsService.h"
class
IMService
:
public
WindowsService
{
public:
static
int
main
();
private:
IMService
(
void
);
~
IMService
(
void
);
protected:
void
Log
(
const
char
*
msg
);
void
Stop
();
void
Run
(
DWORD
argc
,
LPTSTR
*
argv
);
};
server-tools/instance-manager/Makefile.am
deleted
100644 → 0
View file @
ca8706ff
# Copyright (C) 2004 MySQL AB
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
INCLUDES
=
@ZLIB_INCLUDES@
-I
$(top_srcdir)
/include
\
@openssl_includes@
-I
$(top_builddir)
/include
DEFS
=
-DMYSQL_INSTANCE_MANAGER
-DMYSQL_SERVER
# 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)
# See automake/autoconf docs for details
noinst_LTLIBRARIES
=
liboptions.la
noinst_LIBRARIES
=
libnet.a
liboptions_la_CXXFLAGS
=
$(CXXFLAGS)
\
-DDEFAULT_PID_FILE_NAME
=
"
$(localstatedir)
/mysqlmanager.pid"
\
-DDEFAULT_LOG_FILE_NAME
=
"
$(localstatedir)
/mysqlmanager.log"
\
-DDEFAULT_SOCKET_FILE_NAME
=
"/tmp/mysqlmanager.sock"
\
-DDEFAULT_PASSWORD_FILE_NAME
=
"/etc/mysqlmanager.passwd"
\
-DDEFAULT_MYSQLD_PATH
=
"
$(libexecdir)
/mysqld
$(EXEEXT)
"
\
-DDEFAULT_CONFIG_FILE
=
"my.cnf"
\
-DPROTOCOL_VERSION
=
@PROTOCOL_VERSION@
liboptions_la_SOURCES
=
options.h options.cc priv.h priv.cc
liboptions_la_LIBADD
=
$(top_builddir)
/libmysql/get_password.lo
# MySQL sometimes uses symlinks to reuse code
# All symlinked files are grouped in libnet.a
nodist_libnet_a_SOURCES
=
net_serv.cc client_settings.h
libnet_a_LIBADD
=
$(top_builddir)
/sql/password.
$(OBJEXT)
\
$(top_builddir)
/sql/pack.
$(OBJEXT)
\
$(top_builddir)
/sql/sql_state.
$(OBJEXT)
\
$(top_builddir)
/sql/mini_client_errors.
$(OBJEXT)
\
$(top_builddir)
/sql/client.
$(OBJEXT)
CLEANFILES
=
net_serv.cc client_settings.h
net_serv.cc
:
rm
-f
net_serv.cc
@
LN_CP_F@
$(top_srcdir)
/sql/net_serv.cc net_serv.cc
client_settings.h
:
rm
-f
client_settings.h
@
LN_CP_F@
$(top_srcdir)
/sql/client_settings.h 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
\
listener.h listener.cc protocol.h protocol.cc
\
mysql_connection.h mysql_connection.cc
\
user_map.h user_map.cc
\
messages.h messages.cc
\
commands.h commands.cc
\
instance.h instance.cc
\
instance_map.h instance_map.cc
\
instance_options.h instance_options.cc
\
buffer.h buffer.cc parse.cc parse.h
\
guardian.cc guardian.h
\
parse_output.cc parse_output.h
\
mysql_manager_error.h
\
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
\
$(top_builddir)
/strings/libmystrings.a
\
$(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
# Don't update the files from bitkeeper
%
::
SCCS/s.%
server-tools/instance-manager/README
deleted
100644 → 0
View file @
ca8706ff
Instance Manager - manage MySQL instances locally and remotely.
File description:
mysqlmanager.cc - entry point to the manager, main,
options.{h,cc} - handle startup options
manager.{h,cc} - manager process
mysql_connection.{h,cc} - handle one connection with mysql client.
See also instance manager architecture description in mysqlmanager.cc.
server-tools/instance-manager/WindowsService.cpp
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2005 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 */
#include "my_global.h"
#include <windows.h>
#include "WindowsService.h"
static
WindowsService
*
gService
;
WindowsService
::
WindowsService
(
const
char
*
p_serviceName
,
const
char
*
p_displayName
)
:
statusCheckpoint
(
0
),
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
;
}
WindowsService
::~
WindowsService
(
void
)
{
}
BOOL
WindowsService
::
Install
(
const
char
*
username
,
const
char
*
password
)
{
bool
ret_val
=
FALSE
;
SC_HANDLE
newService
;
SC_HANDLE
scm
;
if
(
IsInstalled
())
return
TRUE
;
// determine the name of the currently executing file
char
szFilePath
[
_MAX_PATH
];
GetModuleFileName
(
NULL
,
szFilePath
,
sizeof
(
szFilePath
));
// open a connection to the SCM
if
(
!
(
scm
=
OpenSCManager
(
0
,
0
,
SC_MANAGER_CREATE_SERVICE
)))
return
FALSE
;
newService
=
CreateService
(
scm
,
serviceName
,
displayName
,
SERVICE_ALL_ACCESS
,
SERVICE_WIN32_OWN_PROCESS
,
SERVICE_AUTO_START
,
SERVICE_ERROR_NORMAL
,
szFilePath
,
NULL
,
NULL
,
NULL
,
username
,
password
);
if
(
newService
)
{
CloseServiceHandle
(
newService
);
ret_val
=
TRUE
;
}
CloseServiceHandle
(
scm
);
return
ret_val
;
}
BOOL
WindowsService
::
Init
()
{
DBUG_ASSERT
(
serviceName
!=
NULL
);
if
(
inited
)
return
TRUE
;
SERVICE_TABLE_ENTRY
stb
[]
=
{
{
(
LPSTR
)
serviceName
,
(
LPSERVICE_MAIN_FUNCTION
)
ServiceMain
},
{
NULL
,
NULL
}
};
inited
=
TRUE
;
return
StartServiceCtrlDispatcher
(
stb
);
//register with the Service Manager
}
BOOL
WindowsService
::
Remove
()
{
bool
ret_val
=
FALSE
;
if
(
!
IsInstalled
())
return
TRUE
;
// open a connection to the SCM
SC_HANDLE
scm
=
OpenSCManager
(
0
,
0
,
SC_MANAGER_CREATE_SERVICE
);
if
(
!
scm
)
return
FALSE
;
SC_HANDLE
service
=
OpenService
(
scm
,
serviceName
,
DELETE
);
if
(
service
)
{
if
(
DeleteService
(
service
))
ret_val
=
TRUE
;
DWORD
dw
=
::
GetLastError
();
CloseServiceHandle
(
service
);
}
CloseServiceHandle
(
scm
);
return
ret_val
;
}
BOOL
WindowsService
::
IsInstalled
()
{
BOOL
ret_val
=
FALSE
;
SC_HANDLE
scm
=
::
OpenSCManager
(
NULL
,
NULL
,
SC_MANAGER_CONNECT
);
SC_HANDLE
serv_handle
=
::
OpenService
(
scm
,
serviceName
,
SERVICE_QUERY_STATUS
);
ret_val
=
serv_handle
!=
NULL
;
::
CloseServiceHandle
(
serv_handle
);
::
CloseServiceHandle
(
scm
);
return
ret_val
;
}
void
WindowsService
::
SetAcceptedControls
(
DWORD
acceptedControls
)
{
dwAcceptedControls
=
acceptedControls
;
}
BOOL
WindowsService
::
ReportStatus
(
DWORD
currentState
,
DWORD
waitHint
,
DWORD
dwError
)
{
if
(
debugging
)
return
TRUE
;
if
(
currentState
==
SERVICE_START_PENDING
)
status
.
dwControlsAccepted
=
0
;
else
status
.
dwControlsAccepted
=
dwAcceptedControls
;
status
.
dwCurrentState
=
currentState
;
status
.
dwWin32ExitCode
=
dwError
!=
0
?
ERROR_SERVICE_SPECIFIC_ERROR
:
NO_ERROR
;
status
.
dwWaitHint
=
waitHint
;
status
.
dwServiceSpecificExitCode
=
dwError
;
if
(
currentState
==
SERVICE_RUNNING
||
currentState
==
SERVICE_STOPPED
)
{
status
.
dwCheckPoint
=
0
;
statusCheckpoint
=
0
;
}
else
status
.
dwCheckPoint
=
++
statusCheckpoint
;
// Report the status of the service to the service control manager.
BOOL
result
=
SetServiceStatus
(
statusHandle
,
&
status
);
if
(
!
result
)
Log
(
"ReportStatus failed"
);
return
result
;
}
void
WindowsService
::
RegisterAndRun
(
DWORD
argc
,
LPTSTR
*
argv
)
{
statusHandle
=
::
RegisterServiceCtrlHandler
(
serviceName
,
ControlHandler
);
if
(
statusHandle
&&
ReportStatus
(
SERVICE_START_PENDING
))
Run
(
argc
,
argv
);
ReportStatus
(
SERVICE_STOPPED
);
}
void
WindowsService
::
HandleControlCode
(
DWORD
opcode
)
{
// Handle the requested control code.
switch
(
opcode
)
{
case
SERVICE_CONTROL_STOP
:
// Stop the service.
status
.
dwCurrentState
=
SERVICE_STOP_PENDING
;
Stop
();
break
;
case
SERVICE_CONTROL_PAUSE
:
status
.
dwCurrentState
=
SERVICE_PAUSE_PENDING
;
Pause
();
break
;
case
SERVICE_CONTROL_CONTINUE
:
status
.
dwCurrentState
=
SERVICE_CONTINUE_PENDING
;
Continue
();
break
;
case
SERVICE_CONTROL_SHUTDOWN
:
Shutdown
();
break
;
case
SERVICE_CONTROL_INTERROGATE
:
ReportStatus
(
status
.
dwCurrentState
);
break
;
default:
// invalid control code
break
;
}
}
void
WINAPI
WindowsService
::
ServiceMain
(
DWORD
argc
,
LPTSTR
*
argv
)
{
DBUG_ASSERT
(
gService
!=
NULL
);
// register our service control handler:
gService
->
RegisterAndRun
(
argc
,
argv
);
}
void
WINAPI
WindowsService
::
ControlHandler
(
DWORD
opcode
)
{
DBUG_ASSERT
(
gService
!=
NULL
);
return
gService
->
HandleControlCode
(
opcode
);
}
server-tools/instance-manager/WindowsService.h
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2005 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 */
#pragma once
class
WindowsService
{
protected:
bool
inited
;
const
char
*
serviceName
;
const
char
*
displayName
;
SERVICE_STATUS_HANDLE
statusHandle
;
DWORD
statusCheckpoint
;
SERVICE_STATUS
status
;
DWORD
dwAcceptedControls
;
bool
debugging
;
public:
WindowsService
(
const
char
*
p_serviceName
,
const
char
*
p_displayName
);
~
WindowsService
(
void
);
BOOL
Install
(
const
char
*
username
,
const
char
*
password
);
BOOL
Remove
();
BOOL
Init
();
BOOL
IsInstalled
();
void
SetAcceptedControls
(
DWORD
acceptedControls
);
void
Debug
(
bool
debugFlag
)
{
debugging
=
debugFlag
;
}
public:
static
void
WINAPI
ServiceMain
(
DWORD
argc
,
LPTSTR
*
argv
);
static
void
WINAPI
ControlHandler
(
DWORD
CtrlType
);
protected:
virtual
void
Run
(
DWORD
argc
,
LPTSTR
*
argv
)
=
0
;
virtual
void
Stop
()
{}
virtual
void
Shutdown
()
{}
virtual
void
Pause
()
{}
virtual
void
Continue
()
{}
virtual
void
Log
(
const
char
*
msg
)
{}
BOOL
ReportStatus
(
DWORD
currentStatus
,
DWORD
waitHint
=
3000
,
DWORD
dwError
=
0
);
void
HandleControlCode
(
DWORD
opcode
);
void
RegisterAndRun
(
DWORD
argc
,
LPTSTR
*
argv
);
};
server-tools/instance-manager/angel.cc
deleted
100644 → 0
View file @
ca8706ff
/* 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__
server-tools/instance-manager/angel.h
deleted
100644 → 0
View file @
ca8706ff
/* 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__
server-tools/instance-manager/buffer.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "buffer.h"
#include <m_string.h>
const
uint
Buffer
::
BUFFER_INITIAL_SIZE
=
4096
;
const
uint
Buffer
::
MAX_BUFFER_SIZE
=
16777216
;
/*
Puts the given string to the buffer.
SYNOPSIS
append()
position start position in the buffer
string string to be put in the buffer
len_arg the length of the string. This way we can avoid some
strlens.
DESCRIPTION
The method puts a string into the buffer, starting from position .
In the case when the buffer is too small it reallocs the buffer. The
total size of the buffer is restricted with 16.
RETURN
0 - ok
1 - got an error in reserve()
*/
int
Buffer
::
append
(
size_t
position
,
const
char
*
string
,
size_t
len_arg
)
{
if
(
reserve
(
position
,
len_arg
))
return
1
;
strnmov
((
char
*
)
buffer
+
position
,
string
,
len_arg
);
return
0
;
}
/*
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.
SYNOPSIS
reserve()
position the number starting byte on the buffer to store a buffer
len_arg the length of the string.
DESCRIPTION
The method checks whether it is possible to put a string of the "len_arg"
length into the buffer, starting from "position" byte. In the case when the
buffer is too small it reallocs the buffer. The total size of the buffer is
restricted with 16 Mb.
RETURN
0 - ok
1 - realloc error or we have come to the 16Mb barrier
*/
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
=
(
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
=
(
size_t
)
(
buffer_size
*
1.5
);
}
return
0
;
err:
error
=
1
;
return
1
;
}
int
Buffer
::
get_size
()
{
return
(
uint
)
buffer_size
;
}
int
Buffer
::
is_error
()
{
return
error
;
}
server-tools/instance-manager/buffer.h
deleted
100644 → 0
View file @
ca8706ff
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
#include <my_sys.h>
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
/*
This class is a simple implementation of the buffer of varying size.
It is used to store MySQL client-server protocol packets. This is why
the maximum buffer size if 16Mb. (See internals manual section
7. MySQL Client/Server Protocol)
*/
class
Buffer
{
private:
static
const
uint
BUFFER_INITIAL_SIZE
;
/* maximum buffer size is 16Mb */
static
const
uint
MAX_BUFFER_SIZE
;
size_t
buffer_size
;
/* Error flag. Triggered if we get an error of some kind */
int
error
;
public:
Buffer
(
size_t
buffer_size_arg
=
BUFFER_INITIAL_SIZE
)
:
buffer_size
(
buffer_size_arg
),
error
(
0
)
{
/*
As append() will invokes realloc() anyway, it's ok if malloc returns 0
*/
if
(
!
(
buffer
=
(
uchar
*
)
my_malloc
(
buffer_size
,
MYF
(
0
))))
buffer_size
=
0
;
}
~
Buffer
()
{
my_free
(
buffer
,
MYF
(
0
));
}
public:
uchar
*
buffer
;
int
get_size
();
int
is_error
();
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 */
server-tools/instance-manager/command.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "manager.h"
#include "command.h"
Command
::
Command
()
:
guardian
(
Manager
::
get_guardian
()),
instance_map
(
Manager
::
get_instance_map
())
{}
Command
::~
Command
()
{}
server-tools/instance-manager/command.h
deleted
100644 → 0
View file @
ca8706ff
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
/* 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
*/
class
Command
{
public:
Command
();
virtual
~
Command
();
/*
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
;
};
#endif
/* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H */
server-tools/instance-manager/commands.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "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 "options.h"
#include "priv.h"
#include "protocol.h"
/**************************************************************************
{{{ Static functions.
**************************************************************************/
/**
modify_defaults_to_im_error -- a map of error codes of
mysys::modify_defaults_file() into Instance Manager error codes.
*/
static
const
int
modify_defaults_to_im_error
[]
=
{
0
,
ER_OUT_OF_RESOURCES
,
ER_ACCESS_OPTION_FILE
};
/**
Parse version number from the version string.
SYNOPSIS
parse_version_number()
version_str
version
version_size
DESCRIPTION
TODO
TODO: Move this function to Instance_options and parse version number
only once.
NOTE: This function is used only in SHOW INSTANCE STATUS statement at the
moment.
*/
static
int
parse_version_number
(
const
char
*
version_str
,
char
*
version
,
uint
version_size
)
{
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'
;
return
0
;
}
/**************************************************************************
}}}
**************************************************************************/
/**************************************************************************
Implementation of Instance_name.
**************************************************************************/
Instance_name
::
Instance_name
(
const
LEX_STRING
*
name
)
{
str
.
str
=
str_buffer
;
str
.
length
=
name
->
length
;
if
(
str
.
length
>
MAX_INSTANCE_NAME_SIZE
-
1
)
str
.
length
=
MAX_INSTANCE_NAME_SIZE
-
1
;
strmake
(
str
.
str
,
name
->
str
,
str
.
length
);
}
/**************************************************************************
Implementation of Show_instances.
**************************************************************************/
/**
Implementation of SHOW INSTANCES statement.
Possible error codes:
ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
int
Show_instances
::
execute
(
st_net
*
net
,
ulong
/* connection_id */
)
{
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
;
name_field
.
str
=
(
char
*
)
"instance_name"
;
name_field
.
length
=
DEFAULT_FIELD_LENGTH
;
name
.
data
=
&
name_field
;
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
);
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
()))
{
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
();
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
))
{
err_status
=
TRUE
;
}
instance
->
unlock
();
if
(
err_status
)
break
;
}
instance_map
->
unlock
();
return
err_status
?
ER_OUT_OF_RESOURCES
:
0
;
}
/**************************************************************************
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
(
st_net
*
net
,
ulong
connection_id
)
{
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
;
}
/**************************************************************************
Implementation of Instance_cmd.
**************************************************************************/
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
;
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
;
}
/**************************************************************************
Implementation of Show_instance_status.
**************************************************************************/
Show_instance_status
::
Show_instance_status
(
const
LEX_STRING
*
instance_name_arg
)
:
Abstract_instance_cmd
(
instance_name_arg
)
{
}
/**
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
::
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_status
::
write_header
(
st_net
*
net
)
{
LIST
name
,
state
,
version
,
version_number
,
mysqld_compatible
;
LIST
*
field_list
;
LEX_STRING
name_field
,
state_field
,
version_field
,
version_number_field
,
mysqld_compatible_field
;
/* Create list of the fileds to be passed to send_fields(). */
name_field
.
str
=
(
char
*
)
"instance_name"
;
name_field
.
length
=
DEFAULT_FIELD_LENGTH
;
name
.
data
=
&
name_field
;
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
;
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
);
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
)
{
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
(
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
;
}
/**************************************************************************
Implementation of Show_instance_options.
**************************************************************************/
Show_instance_options
::
Show_instance_options
(
const
LEX_STRING
*
instance_name_arg
)
:
Abstract_instance_cmd
(
instance_name_arg
)
{
}
/**
Implementation of SHOW INSTANCE OPTIONS 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_options
::
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_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
)
{
LIST
name
,
option
;
LIST
*
field_list
;
LEX_STRING
name_field
,
option_field
;
/* Create list of the fileds to be passed to send_fields(). */
name_field
.
str
=
(
char
*
)
"option_name"
;
name_field
.
length
=
DEFAULT_FIELD_LENGTH
;
name
.
data
=
&
name_field
;
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
);
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
))
{
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
(
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
))
{
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
)
{
}
/**
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
==
'='
)
{
++
(
*
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_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
);
}
}
/**
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
);
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 Drop_instance.
**************************************************************************/
Drop_instance
::
Drop_instance
(
const
LEX_STRING
*
instance_name_arg
)
:
Instance_cmd
(
instance_name_arg
)
{
}
/**
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
)
{
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
)
{
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
);
instance
->
unlock
();
instance_map
->
unlock
();
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.
**************************************************************************/
Show_instance_log
::
Show_instance_log
(
const
LEX_STRING
*
instance_name_arg
,
Log_type
log_type_arg
,
uint
size_arg
,
uint
offset_arg
)
:
Abstract_instance_cmd
(
instance_name_arg
),
log_type
(
log_type_arg
),
size
(
size_arg
),
offset
(
offset_arg
)
{
}
/**
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
((
err_code
=
write_header
(
net
))
||
(
err_code
=
write_data
(
net
,
instance
)))
return
err_code
;
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
;
return
0
;
}
int
Show_instance_log
::
check_params
(
Instance
*
instance
)
{
const
char
*
logpath
=
instance
->
options
.
logs
[
log_type
];
/* Cannot read negative number of bytes. */
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
;
return
0
;
}
int
Show_instance_log
::
write_header
(
st_net
*
net
)
{
LIST
name
;
LIST
*
field_list
;
LEX_STRING
name_field
;
/* 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
);
return
send_fields
(
net
,
field_list
)
?
ER_OUT_OF_RESOURCES
:
0
;
}
int
Show_instance_log
::
write_data
(
st_net
*
net
,
Instance
*
instance
)
{
Buffer
send_buff
;
/* buffer for packets */
size_t
pos
=
0
;
const
char
*
logpath
=
instance
->
options
.
logs
[
log_type
];
File
fd
;
size_t
buff_size
;
size_t
read_len
;
MY_STAT
file_stat
;
Buffer
read_buff
;
if
((
fd
=
my_open
(
logpath
,
O_RDONLY
|
O_BINARY
,
MYF
(
MY_WME
)))
<=
0
)
return
ER_OPEN_LOGFILE
;
/* my_fstat doesn't use the flag parameter */
if
(
my_fstat
(
fd
,
&
file_stat
,
MYF
(
0
)))
{
close
(
fd
);
return
ER_OUT_OF_RESOURCES
;
}
/* calculate buffer size */
buff_size
=
(
size
-
offset
);
read_buff
.
reserve
(
0
,
buff_size
);
/* 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
;
}
close
(
fd
);
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
;
}
return
0
;
}
/**************************************************************************
Implementation of Show_instance_log_files.
**************************************************************************/
Show_instance_log_files
::
Show_instance_log_files
(
const
LEX_STRING
*
instance_name_arg
)
:
Abstract_instance_cmd
(
instance_name_arg
)
{
}
/**
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
;
if
((
err_code
=
write_header
(
net
))
||
(
err_code
=
write_data
(
net
,
instance
)))
return
err_code
;
return
0
;
}
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
;
return
0
;
}
int
Show_instance_log_files
::
write_header
(
st_net
*
net
)
{
LIST
name
,
path
,
size
;
LIST
*
field_list
;
LEX_STRING
name_field
,
path_field
,
size_field
;
/* Create list of the fileds to be passed to send_fields(). */
name_field
.
str
=
(
char
*
)
"Logfile"
;
name_field
.
length
=
DEFAULT_FIELD_LENGTH
;
name
.
data
=
&
name_field
;
path_field
.
str
=
(
char
*
)
"Path"
;
path_field
.
length
=
DEFAULT_FIELD_LENGTH
;
path
.
data
=
&
path_field
;
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
);
return
send_fields
(
net
,
field_list
)
?
ER_OUT_OF_RESOURCES
:
0
;
}
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
;
/*
Save some more space for the log file names. In fact all
we need is strlen("GENERAL_LOG") + 1
*/
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
))
{
int10_to_str
(
file_stat
.
st_size
,
buff
,
10
);
log_path
=
log_files
->
value
;
log_size
=
buff
;
}
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
;
}
/**************************************************************************
Implementation of Abstract_option_cmd.
**************************************************************************/
/**
Instance_options_list -- a data class representing a list of options for
some instance.
*/
class
Instance_options_list
{
public:
Instance_options_list
(
const
LEX_STRING
*
instance_name_arg
);
public:
bool
init
();
const
LEX_STRING
*
get_instance_name
()
const
{
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
)
{
}
bool
Instance_options_list
::
init
()
{
return
options
.
init
();
}
/**************************************************************************/
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
0 Success
ER_OUT_OF_RESOURCES Not enough resources to complete the operation
ER_ACCESS_OPTION_FILE Cannot access the option file
*/
int
Abstract_option_cmd
::
correct_file
(
Instance
*
instance
,
Named_value
*
option
,
bool
skip
)
{
int
err_code
=
modify_defaults_file
(
Options
::
Main
::
config_file
,
option
->
get_name
(),
option
->
get_value
(),
instance
->
get_name
()
->
str
,
skip
);
DBUG_ASSERT
(
err_code
>=
0
&&
err_code
<=
2
);
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
];
}
/**
Lock Instance Map and call execute_impl().
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
Abstract_option_cmd
::
execute
(
st_net
*
net
,
ulong
connection_id
)
{
int
err_code
;
instance_map
->
lock
();
err_code
=
execute_impl
(
net
,
connection_id
);
instance_map
->
unlock
();
return
err_code
;
}
Instance_options_list
*
Abstract_option_cmd
::
get_instance_options_list
(
const
LEX_STRING
*
instance_name
)
{
Instance_options_list
*
lst
=
(
Instance_options_list
*
)
hash_search
(
&
instance_options_map
,
(
uchar
*
)
instance_name
->
str
,
instance_name
->
length
);
if
(
!
lst
)
{
lst
=
new
Instance_options_list
(
instance_name
);
if
(
!
lst
)
return
NULL
;
if
(
lst
->
init
()
||
my_hash_insert
(
&
instance_options_map
,
(
uchar
*
)
lst
))
{
delete
lst
;
return
NULL
;
}
}
return
lst
;
}
/**
Skeleton implementation of option-management command.
MT-NOTE: Instance Map is locked before calling this operation.
*/
int
Abstract_option_cmd
::
execute_impl
(
st_net
*
net
,
ulong
connection_id
)
{
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 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. */
}
{
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
)
{
/* 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
;
}
/**************************************************************************
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
)
{
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
;
/* 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
(
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
(
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
;
/* Update the internal cache. */
instance
->
options
.
unset_option
(
option
->
get_name
());
return
0
;
}
/**************************************************************************
Implementation of Syntax_error.
**************************************************************************/
int
Syntax_error
::
execute
(
st_net
*
/* net */
,
ulong
/* connection_id */
)
{
return
ER_SYNTAX_ERROR
;
}
server-tools/instance-manager/commands.h
deleted
100644 → 0
View file @
ca8706ff
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#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 INSTANCES
*/
class
Show_instances
:
public
Command
{
public:
Show_instances
()
{
}
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 internal cache.
Grammar: FLUSH INSTANCES
*/
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:
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.
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 INSTANCE STATUS <instance_name>
*/
class
Show_instance_status
:
public
Abstract_instance_cmd
{
public:
Show_instance_status
(
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
);
private:
int
write_header
(
st_net
*
net
);
int
write_data
(
st_net
*
net
,
Instance
*
instance
);
};
/**
Print options of chosen instance.
Grammar: SHOW INSTANCE OPTIONS <instance_name>
*/
class
Show_instance_options
:
public
Abstract_instance_cmd
{
public:
Show_instance_options
(
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
);
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
Abstract_instance_cmd
{
public:
Start_instance
(
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
);
};
/**
Stop an instance.
Grammar: STOP INSTANCE <instance_name>
*/
class
Stop_instance
:
public
Abstract_instance_cmd
{
public:
Stop_instance
(
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
);
};
/**
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]
*/
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
);
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
;
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
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
);
private:
int
write_header
(
st_net
*
net
);
int
write_data
(
st_net
*
net
,
Instance
*
instance
);
};
/**
Abstract class for option-management commands.
*/
class
Instance_options_list
;
class
Abstract_option_cmd
:
public
Command
{
public:
~
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][, ...]
*/
class
Set_option
:
public
Abstract_option_cmd
{
public:
Set_option
()
{
}
protected:
virtual
bool
parse_args
(
const
char
**
text
);
virtual
int
process_option
(
Instance
*
instance
,
Named_value
*
option
);
};
/**
Remove option of the instance.
Grammar: UNSET instance_name.option[, ...]
*/
class
Unset_option
:
public
Abstract_option_cmd
{
public:
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 */
server-tools/instance-manager/exit_codes.h
deleted
100644 → 0
View file @
ca8706ff
#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
server-tools/instance-manager/guardian.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "guardian.h"
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include "instance.h"
#include "instance_map.h"
#include "log.h"
#include "mysql_manager_error.h"
#include "options.h"
/*************************************************************************
{{{ Constructor & destructor.
*************************************************************************/
/**
Guardian constructor.
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
::
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
);
}
Guardian
::~
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
);
}
/*************************************************************************
}}}
*************************************************************************/
/**
Send request to stop Guardian.
SYNOPSIS
request_shutdown()
*/
void
Guardian
::
request_shutdown
()
{
stop_instances
();
lock
();
shutdown_requested
=
TRUE
;
unlock
();
ping
();
}
/**
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
)
{
int
restart_retry
=
100
;
time_t
current_time
=
time
(
NULL
);
if
(
instance
->
get_state
()
==
Instance
::
STOPPING
)
{
/* This brach is executed during shutdown. */
/* This returns TRUE if and only if an instance was stopped for sure. */
if
(
instance
->
is_crashed
())
{
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_mysqld_running
())
{
/* The instance can be contacted on it's port */
/* If STARTING also check that pidfile has been created */
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
(
instance
->
get_state
()
!=
Instance
::
STARTED
)
{
/* clear status fields */
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
(
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
)
{
if
(
instance
->
is_crashed
())
{
instance
->
start_mysqld
();
log_info
(
"Guardian: starting '%s'..."
,
(
const
char
*
)
instance
->
options
.
instance_name
.
str
);
}
}
else
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_mysqld
();
instance
->
last_checked
=
current_time
;
log_info
(
"Guardian: restarting '%s'..."
,
(
const
char
*
)
instance
->
options
.
instance_name
.
str
);
}
}
else
{
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
);
}
return
;
case
Instance
:
:
CRASHED_AND_ABANDONED
:
return
;
/* do nothing */
default:
DBUG_ASSERT
(
0
);
}
}
}
/**
Main function of Guardian thread.
SYNOPSIS
run()
DESCRIPTION
Check for all guarded instances and restart them if needed.
*/
void
Guardian
::
run
()
{
struct
timespec
timeout
;
log_info
(
"Guardian: started."
);
thread_registry
->
register_thread
(
&
thread_info
);
/* Loop, until all instances were shut down at the end. */
while
(
true
)
{
Instance_map
::
Iterator
instances_it
(
instance_map
);
Instance
*
instance
;
bool
all_instances_stopped
=
TRUE
;
instance_map
->
lock
();
while
((
instance
=
instances_it
.
next
()))
{
instance
->
lock
();
if
(
!
instance
->
is_guarded
()
||
instance
->
get_state
()
==
Instance
::
STOPPED
)
{
instance
->
unlock
();
continue
;
}
process_instance
(
instance
);
if
(
instance
->
get_state
()
!=
Instance
::
STOPPED
)
all_instances_stopped
=
FALSE
;
instance
->
unlock
();
}
instance_map
->
unlock
();
lock
();
if
(
shutdown_requested
&&
all_instances_stopped
)
{
log_info
(
"Guardian: all guarded mysqlds stopped."
);
stopped
=
TRUE
;
unlock
();
break
;
}
set_timespec
(
timeout
,
Options
::
Main
::
monitoring_interval
);
thread_registry
->
cond_timedwait
(
&
thread_info
,
&
COND_guardian
,
&
LOCK_guardian
,
&
timeout
);
unlock
();
}
log_info
(
"Guardian: stopped."
);
/* Now, when the Guardian is stopped we can stop the IM. */
thread_registry
->
unregister_thread
(
&
thread_info
);
thread_registry
->
request_shutdown
();
log_info
(
"Guardian: finished."
);
}
/**
Return the value of stopped flag.
*/
bool
Guardian
::
is_stopped
()
{
int
var
;
lock
();
var
=
stopped
;
unlock
();
return
var
;
}
/**
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.
*/
void
Guardian
::
ping
()
{
pthread_cond_signal
(
&
COND_guardian
);
}
/**
Prepare list of instances.
SYNOPSIS
init()
MT-NOTE: Instance Map must be locked before calling the operation.
*/
void
Guardian
::
init
()
{
Instance
*
instance
;
Instance_map
::
Iterator
iterator
(
instance_map
);
while
((
instance
=
iterator
.
next
()))
{
instance
->
lock
();
instance
->
reset_stat
();
instance
->
set_state
(
Instance
::
NOT_STARTED
);
instance
->
unlock
();
}
}
/**
An internal method which is called at shutdown to unregister instances and
attempt to stop them if requested.
SYNOPSIS
stop_instances()
DESCRIPTION
Loops through the guarded_instances list and prepares them for shutdown.
For each instance we issue a stop command and change the state
accordingly.
NOTE
Guardian object should be locked by the caller.
*/
void
Guardian
::
stop_instances
()
{
static
const
int
NUM_STOP_ATTEMPTS
=
100
;
Instance_map
::
Iterator
instances_it
(
instance_map
);
Instance
*
instance
;
instance_map
->
lock
();
while
((
instance
=
instances_it
.
next
()))
{
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
(
instance
->
is_mysqld_running
()
||
instance
->
get_state
()
==
Instance
::
STARTED
)
{
instance
->
set_state
(
Instance
::
STOPPING
);
instance
->
last_checked
=
time
(
NULL
);
}
else
{
/* 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
();
}
instance_map
->
unlock
();
}
/**
Lock Guardian.
*/
void
Guardian
::
lock
()
{
pthread_mutex_lock
(
&
LOCK_guardian
);
}
/**
Unlock Guardian.
*/
void
Guardian
::
unlock
()
{
pthread_mutex_unlock
(
&
LOCK_guardian
);
}
server-tools/instance-manager/guardian.h
deleted
100644 → 0
View file @
ca8706ff
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#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
class
Instance
;
class
Instance_map
;
class
Thread_registry
;
/**
The guardian thread is responsible for monitoring and restarting of guarded
instances.
*/
class
Guardian
:
public
Thread
{
public:
Guardian
(
Thread_registry
*
thread_registry_arg
,
Instance_map
*
instance_map_arg
);
~
Guardian
();
void
init
();
public:
void
request_shutdown
();
bool
is_stopped
();
void
lock
();
void
unlock
();
void
ping
();
protected:
virtual
void
run
();
private:
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
;
/*
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 */
server-tools/instance-manager/instance.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include <my_global.h>
#include <mysql.h>
#include <signal.h>
#ifndef __WIN__
#include <sys/wait.h>
#endif
#include "manager.h"
#include "guardian.h"
#include "instance.h"
#include "log.h"
#include "mysql_manager_error.h"
#include "portability.h"
#include "priv.h"
#include "thread_registry.h"
#include "instance_map.h"
/*************************************************************************
{{{ Platform-specific functions.
*************************************************************************/
#ifndef __WIN__
typedef
pid_t
My_process_info
;
#else
typedef
PROCESS_INFORMATION
My_process_info
;
#endif
/*
Wait for an instance
SYNOPSIS
wait_process()
pi Pointer to the process information structure
(platform-dependent).
RETURN
0 - Success
1 - Error
*/
#ifndef __WIN__
static
int
wait_process
(
My_process_info
*
pi
)
{
/*
Here we wait for the child created. This process differs for systems
running LinuxThreads and POSIX Threads compliant systems. This is because
according to POSIX we could wait() for a child in any thread of the
process. While LinuxThreads require that wait() is called by the thread,
which created the child.
On the other hand we could not expect mysqld to return the pid, we
got in from fork(), to wait4() fucntion when running on LinuxThreads.
This is because MySQL shutdown thread is not the one, which was created
by our fork() call.
So basically we have two options: whether the wait() call returns only in
the creator thread, but we cannot use waitpid() since we have no idea
which pid we should wait for (in fact it should be the pid of shutdown
thread, but we don't know this one). Or we could use waitpid(), but
couldn't use wait(), because it could return in any wait() in the program.
*/
if
(
Manager
::
is_linux_threads
())
wait
(
NULL
);
/* LinuxThreads were detected */
else
waitpid
(
*
pi
,
NULL
,
0
);
return
0
;
}
#else
static
int
wait_process
(
My_process_info
*
pi
)
{
/* Wait until child process exits. */
WaitForSingleObject
(
pi
->
hProcess
,
INFINITE
);
DWORD
exitcode
;
::
GetExitCodeProcess
(
pi
->
hProcess
,
&
exitcode
);
/* Close process and thread handles. */
CloseHandle
(
pi
->
hProcess
);
CloseHandle
(
pi
->
hThread
);
/*
GetExitCodeProces returns zero on failure. We should revert this value
to report an error.
*/
return
(
!
exitcode
);
}
#endif
/*
Launch an instance
SYNOPSIS
start_process()
instance_options Pointer to the options of the instance to be
launched.
pi Pointer to the process information structure
(platform-dependent).
RETURN
FALSE - Success
TRUE - Cannot create an instance
*/
#ifndef __WIN__
static
bool
start_process
(
Instance_options
*
instance_options
,
My_process_info
*
pi
)
{
#ifndef __QNX__
*
pi
=
fork
();
#else
/*
On QNX one cannot use fork() in multithreaded environment and we
should use spawn() or one of it's siblings instead.
Here we use spawnv(), which is a combination of fork() and execv()
in one call. It returns the pid of newly created process (>0) or -1
*/
*
pi
=
spawnv
(
P_NOWAIT
,
instance_options
->
mysqld_path
,
instance_options
->
argv
);
#endif
switch
(
*
pi
)
{
case
0
:
/* never happens on QNX */
execv
(
instance_options
->
mysqld_path
.
str
,
instance_options
->
argv
);
/* exec never returns */
exit
(
1
);
case
-
1
:
log_error
(
"Instance '%s': can not start mysqld: fork() failed."
,
(
const
char
*
)
instance_options
->
instance_name
.
str
);
return
TRUE
;
}
return
FALSE
;
}
#else
static
bool
start_process
(
Instance_options
*
instance_options
,
My_process_info
*
pi
)
{
STARTUPINFO
si
;
ZeroMemory
(
&
si
,
sizeof
(
STARTUPINFO
));
si
.
cb
=
sizeof
(
STARTUPINFO
);
ZeroMemory
(
pi
,
sizeof
(
PROCESS_INFORMATION
));
int
cmdlen
=
0
;
for
(
int
i
=
0
;
instance_options
->
argv
[
i
]
!=
0
;
i
++
)
cmdlen
+=
(
uint
)
strlen
(
instance_options
->
argv
[
i
])
+
3
;
cmdlen
++
;
/* make room for the null */
char
*
cmdline
=
new
char
[
cmdlen
];
if
(
cmdline
==
NULL
)
return
TRUE
;
cmdline
[
0
]
=
0
;
for
(
int
i
=
0
;
instance_options
->
argv
[
i
]
!=
0
;
i
++
)
{
strcat
(
cmdline
,
"
\"
"
);
strcat
(
cmdline
,
instance_options
->
argv
[
i
]);
strcat
(
cmdline
,
"
\"
"
);
}
/* Start the child process */
BOOL
result
=
CreateProcess
(
NULL
,
/* Put it all in cmdline */
cmdline
,
/* Command line */
NULL
,
/* Process handle not inheritable */
NULL
,
/* Thread handle not inheritable */
FALSE
,
/* Set handle inheritance to FALSE */
0
,
/* No creation flags */
NULL
,
/* Use parent's environment block */
NULL
,
/* Use parent's starting directory */
&
si
,
/* Pointer to STARTUPINFO structure */
pi
);
/* Pointer to PROCESS_INFORMATION structure */
delete
cmdline
;
return
!
result
;
}
#endif
#ifdef __WIN__
BOOL
SafeTerminateProcess
(
HANDLE
hProcess
,
UINT
uExitCode
)
{
DWORD
dwTID
,
dwCode
,
dwErr
=
0
;
HANDLE
hProcessDup
=
INVALID_HANDLE_VALUE
;
HANDLE
hRT
=
NULL
;
HINSTANCE
hKernel
=
GetModuleHandle
(
"Kernel32"
);
BOOL
bSuccess
=
FALSE
;
BOOL
bDup
=
DuplicateHandle
(
GetCurrentProcess
(),
hProcess
,
GetCurrentProcess
(),
&
hProcessDup
,
PROCESS_ALL_ACCESS
,
FALSE
,
0
);
// Detect the special case where the process is
// already dead...
if
(
GetExitCodeProcess
((
bDup
)
?
hProcessDup
:
hProcess
,
&
dwCode
)
&&
(
dwCode
==
STILL_ACTIVE
))
{
FARPROC
pfnExitProc
;
pfnExitProc
=
GetProcAddress
(
hKernel
,
"ExitProcess"
);
hRT
=
CreateRemoteThread
((
bDup
)
?
hProcessDup
:
hProcess
,
NULL
,
0
,
(
LPTHREAD_START_ROUTINE
)
pfnExitProc
,
(
PVOID
)
uExitCode
,
0
,
&
dwTID
);
if
(
hRT
==
NULL
)
dwErr
=
GetLastError
();
}
else
dwErr
=
ERROR_PROCESS_ABORTED
;
if
(
hRT
)
{
// Must wait process to terminate to
// guarantee that it has exited...
WaitForSingleObject
((
bDup
)
?
hProcessDup
:
hProcess
,
INFINITE
);
CloseHandle
(
hRT
);
bSuccess
=
TRUE
;
}
if
(
bDup
)
CloseHandle
(
hProcessDup
);
if
(
!
bSuccess
)
SetLastError
(
dwErr
);
return
bSuccess
;
}
int
kill
(
pid_t
pid
,
int
signum
)
{
HANDLE
processhandle
=
::
OpenProcess
(
PROCESS_ALL_ACCESS
,
FALSE
,
pid
);
if
(
signum
==
SIGTERM
)
::
SafeTerminateProcess
(
processhandle
,
0
);
else
::
TerminateProcess
(
processhandle
,
-
1
);
return
0
;
}
#endif
/*************************************************************************
}}}
*************************************************************************/
/*************************************************************************
{{{ Static constants.
*************************************************************************/
const
LEX_STRING
Instance
::
DFLT_INSTANCE_NAME
=
{
C_STRING_WITH_LEN
(
"mysqld"
)
};
/*************************************************************************
}}}
*************************************************************************/
/*************************************************************************
{{{ Instance Monitor thread.
*************************************************************************/
/**
Proxy thread is a simple way to avoid all pitfalls of the threads
implementation in the OS (e.g. LinuxThreads). With such a thread we
don't have to process SIGCHLD, which is a tricky business if we want
to do it in a portable way.
Instance Monitor Thread forks a child process, execs mysqld and waits for
the child to die.
Instance Monitor assumes that the monitoring instance will not be dropped.
This is guaranteed by having flag monitoring_thread_active and
Instance::is_active() operation.
*/
class
Instance_monitor
:
public
Thread
{
public:
Instance_monitor
(
Instance
*
instance_arg
)
:
instance
(
instance_arg
)
{}
protected:
virtual
void
run
();
void
start_and_monitor_instance
();
private:
Instance
*
instance
;
};
void
Instance_monitor
::
run
()
{
start_and_monitor_instance
();
delete
this
;
}
void
Instance_monitor
::
start_and_monitor_instance
()
{
Thread_registry
*
thread_registry
=
Manager
::
get_thread_registry
();
Guardian
*
guardian
=
Manager
::
get_guardian
();
My_process_info
mysqld_process_info
;
Thread_info
monitor_thread_info
;
log_info
(
"Instance '%s': Monitor: started."
,
(
const
char
*
)
instance
->
get_name
()
->
str
);
/*
For guarded instance register the thread in Thread_registry to wait for
the thread to stop on shutdown (nonguarded instances are not stopped on
shutdown, so the thread will no finish).
*/
if
(
instance
->
is_guarded
())
{
thread_registry
->
register_thread
(
&
monitor_thread_info
,
FALSE
);
}
/* Starting mysqld. */
log_info
(
"Instance '%s': Monitor: starting mysqld..."
,
(
const
char
*
)
instance
->
get_name
()
->
str
);
if
(
start_process
(
&
instance
->
options
,
&
mysqld_process_info
))
{
instance
->
lock
();
instance
->
monitoring_thread_active
=
FALSE
;
instance
->
unlock
();
return
;
}
/* Waiting for mysqld to die. */
log_info
(
"Instance '%s': Monitor: waiting for mysqld to stop..."
,
(
const
char
*
)
instance
->
get_name
()
->
str
);
wait_process
(
&
mysqld_process_info
);
/* Don't check for return value. */
log_info
(
"Instance '%s': Monitor: mysqld stopped."
,
(
const
char
*
)
instance
->
get_name
()
->
str
);
/* Update instance status. */
instance
->
lock
();
if
(
instance
->
is_guarded
())
thread_registry
->
unregister_thread
(
&
monitor_thread_info
);
instance
->
crashed
=
TRUE
;
instance
->
monitoring_thread_active
=
FALSE
;
log_info
(
"Instance '%s': Monitor: finished."
,
(
const
char
*
)
instance
->
get_name
()
->
str
);
instance
->
unlock
();
/* Wake up guardian. */
guardian
->
ping
();
}
/**************************************************************************
}}}
**************************************************************************/
/**************************************************************************
{{{ Static operations.
**************************************************************************/
/**
The operation is intended to check whether string is a well-formed
instance name or not.
SYNOPSIS
is_name_valid()
name string to check
RETURN
TRUE string is a valid instance name
FALSE string is not a valid instance name
TODO: Move to Instance_name class: Instance_name::is_valid().
*/
bool
Instance
::
is_name_valid
(
const
LEX_STRING
*
name
)
{
const
char
*
name_suffix
=
name
->
str
+
DFLT_INSTANCE_NAME
.
length
;
if
(
strncmp
(
name
->
str
,
Instance
::
DFLT_INSTANCE_NAME
.
str
,
Instance
::
DFLT_INSTANCE_NAME
.
length
)
!=
0
)
return
FALSE
;
return
*
name_suffix
==
0
||
my_isdigit
(
default_charset_info
,
*
name_suffix
);
}
/**
The operation is intended to check if the given instance name is
mysqld-compatible or not.
SYNOPSIS
is_mysqld_compatible_name()
name name to check
RETURN
TRUE name is mysqld-compatible
FALSE otherwise
TODO: Move to Instance_name class: Instance_name::is_mysqld_compatible().
*/
bool
Instance
::
is_mysqld_compatible_name
(
const
LEX_STRING
*
name
)
{
return
strcmp
(
name
->
str
,
DFLT_INSTANCE_NAME
.
str
)
==
0
;
}
/**
Return client state name. Must not be used outside the class.
Use Instance::get_state_name() instead.
*/
const
char
*
Instance
::
get_instance_state_name
(
enum_instance_state
state
)
{
switch
(
state
)
{
case
STOPPED
:
return
"offline"
;
case
NOT_STARTED
:
return
"not started"
;
case
STARTING
:
return
"starting"
;
case
STARTED
:
return
"online"
;
case
JUST_CRASHED
:
return
"failed"
;
case
CRASHED
:
return
"crashed"
;
case
CRASHED_AND_ABANDONED
:
return
"abandoned"
;
case
STOPPING
:
return
"stopping"
;
}
return
NULL
;
/* just to ignore compiler warning. */
}
/**************************************************************************
}}}
**************************************************************************/
/**************************************************************************
{{{ Initialization & deinitialization.
**************************************************************************/
Instance
::
Instance
()
:
monitoring_thread_active
(
FALSE
),
crashed
(
FALSE
),
configured
(
FALSE
),
/* mysqld_compatible is initialized in init() */
state
(
NOT_STARTED
),
restart_counter
(
0
),
crash_moment
(
0
),
last_checked
(
0
)
{
pthread_mutex_init
(
&
LOCK_instance
,
0
);
}
Instance
::~
Instance
()
{
log_info
(
"Instance '%s': destroying..."
,
(
const
char
*
)
get_name
()
->
str
);
pthread_mutex_destroy
(
&
LOCK_instance
);
}
/**
Initialize instance options.
SYNOPSIS
init()
name_arg name of the instance
RETURN:
FALSE - ok
TRUE - error
*/
bool
Instance
::
init
(
const
LEX_STRING
*
name_arg
)
{
mysqld_compatible
=
is_mysqld_compatible_name
(
name_arg
);
return
options
.
init
(
name_arg
);
}
/**
@brief Complete instance options initialization.
@return Error status.
@retval FALSE ok
@retval TRUE error
*/
bool
Instance
::
complete_initialization
()
{
configured
=
!
options
.
complete_initialization
();
return
!
configured
;
}
/**************************************************************************
}}}
**************************************************************************/
/**************************************************************************
{{{ Instance: public interface implementation.
**************************************************************************/
/**
Determine if there is some activity with the instance.
SYNOPSIS
is_active()
DESCRIPTION
An instance is active if one of the following conditions is true:
- Instance-monitoring thread is running;
- Instance is guarded and its state is other than STOPPED;
- Corresponding mysqld-server accepts connections.
MT-NOTE: instance must be locked before calling the operation.
RETURN
TRUE - instance is active
FALSE - otherwise.
*/
bool
Instance
::
is_active
()
{
if
(
monitoring_thread_active
)
return
TRUE
;
if
(
is_guarded
()
&&
get_state
()
!=
STOPPED
)
return
TRUE
;
return
is_mysqld_running
();
}
/**
Determine if mysqld is accepting connections.
SYNOPSIS
is_mysqld_running()
DESCRIPTION
Try to connect to mysqld with fake login/password to check whether it is
accepting connections or not.
MT-NOTE: instance must be locked before calling the operation.
RETURN
TRUE - mysqld is alive and accept connections
FALSE - otherwise.
*/
bool
Instance
::
is_mysqld_running
()
{
MYSQL
mysql
;
uint
port
=
options
.
get_mysqld_port
();
/* 0 if not specified. */
const
char
*
socket
=
NULL
;
static
const
char
*
password
=
"check_connection"
;
static
const
char
*
username
=
"MySQL_Instance_Manager"
;
static
const
char
*
access_denied_message
=
"Access denied for user"
;
bool
return_val
;
if
(
options
.
mysqld_socket
)
socket
=
options
.
mysqld_socket
;
/* no port was specified => instance falled back to default value */
if
(
!
port
&&
!
options
.
mysqld_socket
)
port
=
SERVER_DEFAULT_PORT
;
mysql_init
(
&
mysql
);
/* try to connect to a server with a fake username/password pair */
if
(
mysql_real_connect
(
&
mysql
,
LOCAL_HOST
,
username
,
password
,
NullS
,
port
,
socket
,
0
))
{
/*
We have successfully connected to the server using fake
username/password. Write a warning to the logfile.
*/
log_error
(
"Instance '%s': was able to log into mysqld."
,
(
const
char
*
)
get_name
()
->
str
);
return_val
=
TRUE
;
/* server is alive */
}
else
return_val
=
test
(
!
strncmp
(
access_denied_message
,
mysql_error
(
&
mysql
),
sizeof
(
access_denied_message
)
-
1
));
mysql_close
(
&
mysql
);
return
return_val
;
}
/**
@brief Start mysqld.
Reset flags and start Instance Monitor thread, which will start mysqld.
@note Instance must be locked before calling the operation.
@return Error status code
@retval FALSE Ok
@retval TRUE Could not start instance
*/
bool
Instance
::
start_mysqld
()
{
Instance_monitor
*
instance_monitor
;
if
(
!
configured
)
return
TRUE
;
/*
Prepare instance to start Instance Monitor thread.
NOTE: It's important to set these actions here in order to avoid
race conditions -- these actions must be done under acquired lock on
Instance.
*/
crashed
=
FALSE
;
monitoring_thread_active
=
TRUE
;
remove_pid
();
/* Create and start the Instance Monitor thread. */
instance_monitor
=
new
Instance_monitor
(
this
);
if
(
instance_monitor
==
NULL
||
instance_monitor
->
start
(
Thread
::
DETACHED
))
{
delete
instance_monitor
;
monitoring_thread_active
=
FALSE
;
log_error
(
"Instance '%s': can not create instance monitor thread."
,
(
const
char
*
)
get_name
()
->
str
);
return
TRUE
;
}
++
restart_counter
;
/* The Instance Monitor thread will delete itself when it's finished. */
return
FALSE
;
}
/**
Stop mysqld.
SYNOPSIS
stop_mysqld()
DESCRIPTION
Try to stop mysqld gracefully. Otherwise kill it with SIGKILL.
MT-NOTE: instance must be locked before calling the operation.
RETURN
FALSE - ok
TRUE - could not stop the instance
*/
bool
Instance
::
stop_mysqld
()
{
log_info
(
"Instance '%s': stopping mysqld..."
,
(
const
char
*
)
get_name
()
->
str
);
kill_mysqld
(
SIGTERM
);
if
(
!
wait_for_stop
())
{
log_info
(
"Instance '%s': mysqld stopped gracefully."
,
(
const
char
*
)
get_name
()
->
str
);
return
FALSE
;
}
log_info
(
"Instance '%s': mysqld failed to stop gracefully within %d seconds."
,
(
const
char
*
)
get_name
()
->
str
,
(
int
)
options
.
get_shutdown_delay
());
log_info
(
"Instance'%s': killing mysqld..."
,
(
const
char
*
)
get_name
()
->
str
);
kill_mysqld
(
SIGKILL
);
if
(
!
wait_for_stop
())
{
log_info
(
"Instance '%s': mysqld has been killed."
,
(
const
char
*
)
get_name
()
->
str
);
return
FALSE
;
}
log_info
(
"Instance '%s': can not kill mysqld within %d seconds."
,
(
const
char
*
)
get_name
()
->
str
,
(
int
)
options
.
get_shutdown_delay
());
return
TRUE
;
}
/**
Send signal to mysqld.
SYNOPSIS
kill_mysqld()
DESCRIPTION
Load pid from the pid file and send the given signal to that process.
If the signal is SIGKILL, remove the pid file after sending the signal.
MT-NOTE: instance must be locked before calling the operation.
TODO
This too low-level and OS-specific operation for public interface.
Also, it has some implicit behaviour for SIGKILL signal. Probably, we
should have the following public operations instead:
- start_mysqld() -- as is;
- stop_mysqld -- request mysqld to shutdown gracefully (send SIGTERM);
don't wait for complete shutdown;
- wait_for_stop() (or join_mysqld()) -- wait for mysqld to stop within
time interval;
- kill_mysqld() -- request to terminate mysqld; don't wait for
completion.
These operations should also be used in Guardian to manage instances.
*/
bool
Instance
::
kill_mysqld
(
int
signum
)
{
pid_t
mysqld_pid
=
options
.
load_pid
();
if
(
mysqld_pid
==
0
)
{
log_info
(
"Instance '%s': no pid file to send a signal (%d)."
,
(
const
char
*
)
get_name
()
->
str
,
(
int
)
signum
);
return
TRUE
;
}
log_info
(
"Instance '%s': sending %d to %d..."
,
(
const
char
*
)
get_name
()
->
str
,
(
int
)
signum
,
(
int
)
mysqld_pid
);
if
(
kill
(
mysqld_pid
,
signum
))
{
log_info
(
"Instance '%s': kill() failed."
,
(
const
char
*
)
get_name
()
->
str
);
return
TRUE
;
}
/* Kill suceeded */
if
(
signum
==
SIGKILL
)
/* really killed instance with SIGKILL */
{
log_error
(
"Instance '%s': killed."
,
(
const
char
*
)
options
.
instance_name
.
str
);
/* After sucessful hard kill the pidfile need to be removed */
options
.
unlink_pidfile
();
}
return
FALSE
;
}
/**
Lock instance.
*/
void
Instance
::
lock
()
{
pthread_mutex_lock
(
&
LOCK_instance
);
}
/**
Unlock instance.
*/
void
Instance
::
unlock
()
{
pthread_mutex_unlock
(
&
LOCK_instance
);
}
/**
Return instance state name.
SYNOPSIS
get_state_name()
DESCRIPTION
The operation returns user-friendly state name. The operation can be
used both for guarded and non-guarded instances.
MT-NOTE: instance must be locked before calling the operation.
TODO: Replace with the static get_state_name(state_code) function.
*/
const
char
*
Instance
::
get_state_name
()
{
if
(
!
is_configured
())
return
"misconfigured"
;
if
(
is_guarded
())
{
/* The instance is managed by Guardian: we can report precise state. */
return
get_instance_state_name
(
get_state
());
}
/* The instance is not managed by Guardian: we can report status only. */
return
is_active
()
?
"online"
:
"offline"
;
}
/**
Reset statistics.
SYNOPSIS
reset_stat()
DESCRIPTION
The operation resets statistics used for guarding the instance.
MT-NOTE: instance must be locked before calling the operation.
TODO: Make private.
*/
void
Instance
::
reset_stat
()
{
restart_counter
=
0
;
crash_moment
=
0
;
last_checked
=
0
;
}
/**************************************************************************
}}}
**************************************************************************/
/**************************************************************************
{{{ Instance: implementation of private operations.
**************************************************************************/
/**
Remove pid file.
*/
void
Instance
::
remove_pid
()
{
int
mysqld_pid
=
options
.
load_pid
();
if
(
mysqld_pid
==
0
)
return
;
if
(
options
.
unlink_pidfile
())
{
log_error
(
"Instance '%s': can not unlink pid file."
,
(
const
char
*
)
options
.
instance_name
.
str
);
}
}
/**
Wait for mysqld to stop within shutdown interval.
*/
bool
Instance
::
wait_for_stop
()
{
int
start_time
=
(
int
)
time
(
NULL
);
int
finish_time
=
start_time
+
options
.
get_shutdown_delay
();
log_info
(
"Instance '%s': waiting for mysqld to stop "
"(timeout: %d seconds)..."
,
(
const
char
*
)
get_name
()
->
str
,
(
int
)
options
.
get_shutdown_delay
());
while
(
true
)
{
if
(
options
.
load_pid
()
==
0
&&
!
is_mysqld_running
())
return
FALSE
;
if
(
time
(
NULL
)
>=
finish_time
)
return
TRUE
;
/* Sleep for 0.3 sec and check again. */
my_sleep
(
300000
);
}
}
/**************************************************************************
}}}
**************************************************************************/
server-tools/instance-manager/instance.h
deleted
100644 → 0
View file @
ca8706ff
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#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:
/* 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
();
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:
/**
This attributes contains instance options.
TODO: Make private.
*/
Instance_options
options
;
private:
/**
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
;
/*
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.
*/
bool
mysqld_compatible
;
/*
Mutex protecting the instance.
*/
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
();
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 */
server-tools/instance-manager/instance_map.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "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 "priv.h"
C_MODE_START
/**
HASH-routines: get key of instance for storing in hash.
*/
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
.
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 function.
SYNOPSIS
process_option()
ctx Handler context. Here it is an instance_map structure.
group_name The name of the group the option belongs to.
option The very option to be processed. It is already
prepared to be used in argv (has -- prefix)
DESCRIPTION
This handler checks whether a group is an instance group and adds
an option to the appropriate instance class. If this is the first
occurence of an instance name, we'll also create the instance
with such name and add it to the instance map.
RETURN
0 - ok
1 - error occured
*/
static
int
process_option
(
void
*
ctx
,
const
char
*
group
,
const
char
*
option
)
{
Instance_map
*
map
=
(
Instance_map
*
)
ctx
;
LEX_STRING
group_str
;
group_str
.
str
=
(
char
*
)
group
;
group_str
.
length
=
strlen
(
group
);
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
Instance_map::process_one_option()
group group name
option option string (e.g. "--name=value")
DESCRIPTION
This is an auxiliary function and should not be used externally.
It is used only by flush_instances(), which pass it to
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
LEX_STRING
*
group
,
const
char
*
option
)
{
Instance
*
instance
=
NULL
;
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
))
{
delete
instance
;
return
1
;
}
if
(
instance
->
is_mysqld_compatible
())
log_info
(
"Warning: instance name '%s' is mysqld-compatible."
,
(
const
char
*
)
group
->
str
);
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 constructor.
*/
Instance_map
::
Instance_map
()
{
pthread_mutex_init
(
&
LOCK_instance_map
,
0
);
}
/**
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
()
{
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
);
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
);
}
/**
Check if there is an active instance or not.
*/
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
::
add_instance
(
Instance
*
instance
)
{
return
my_hash_insert
(
&
hash
,
(
uchar
*
)
instance
);
}
/**
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
);
}
/**
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
=
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
;
}
/**
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
)
{
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. */
for
(
uint
i
=
0
;
i
<
hash
.
records
;
++
i
)
{
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. */
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
:
/*
Continue if the instance has been added to the config file
successfully, or the config file just does not exist.
*/
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
;
}
return
FALSE
;
}
/**
Load options from config files and create appropriate instance
structures.
*/
int
Instance_map
::
load
()
{
int
argc
=
1
;
/* this is a dummy variable for search_option_files() */
uint
args_used
=
0
;
const
char
*
argv_options
[
3
];
char
**
argv
=
(
char
**
)
&
argv_options
;
char
defaults_file_arg
[
FN_REFLEN
];
/* the name of the program may be orbitrary here in fact */
argv_options
[
0
]
=
"mysqlmanager"
;
/*
If the option file was forced by the user when starting
the IM with --defaults-file=xxxx, make sure it is also
passed as --defaults-file, not only as Options::config_file.
This is important for option files given with relative path:
e.g. --defaults-file=my.cnf.
Otherwise my_search_option_files will treat "my.cnf" as a group
name and start looking for files named "my.cnf.cnf" in all
default dirs. Which is not what we want.
*/
if
(
Options
::
Main
::
is_forced_default_file
)
{
snprintf
(
defaults_file_arg
,
FN_REFLEN
,
"--defaults-file=%s"
,
Options
::
Main
::
config_file
);
argv_options
[
1
]
=
defaults_file_arg
;
argv_options
[
2
]
=
'\0'
;
argc
=
2
;
}
else
argv_options
[
1
]
=
'\0'
;
/*
If the routine failed, we'll simply fallback to defaults in
complete_initialization().
*/
if
(
my_search_option_files
(
Options
::
Main
::
config_file
,
&
argc
,
(
char
***
)
&
argv
,
&
args_used
,
process_option
,
(
void
*
)
this
,
Options
::
default_directories
))
log_info
(
"Falling back to compiled-in defaults."
);
return
complete_initialization
();
}
/*************************************************************************
{{{ Instance_map::Iterator implementation.
*************************************************************************/
void
Instance_map
::
Iterator
::
go_to_first
()
{
current_instance
=
0
;
}
Instance
*
Instance_map
::
Iterator
::
next
()
{
if
(
current_instance
<
instance_map
->
hash
.
records
)
return
(
Instance
*
)
hash_element
(
&
instance_map
->
hash
,
current_instance
++
);
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
;
}
server-tools/instance-manager/instance_map.h
deleted
100644 → 0
View file @
ca8706ff
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.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
*/
class
Iterator
{
private:
uint
current_instance
;
Instance_map
*
instance_map
;
public:
Iterator
(
Instance_map
*
instance_map_arg
)
:
current_instance
(
0
),
instance_map
(
instance_map_arg
)
{}
void
go_to_first
();
Instance
*
next
();
};
public:
Instance
*
find
(
const
LEX_STRING
*
name
);
bool
is_there_active_instance
();
void
lock
();
void
unlock
();
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:
Instance_map
();
~
Instance_map
();
private:
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 */
server-tools/instance-manager/instance_options.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "instance_options.h"
#include <my_global.h>
#include <my_sys.h>
#include <m_string.h>
#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
bool
create_mysqld_command
(
Buffer
*
buf
,
const
LEX_STRING
*
mysqld_path
,
const
LEX_STRING
*
option
)
{
int
position
=
0
;
if
(
buf
->
get_size
())
/* malloc succeeded */
{
#ifdef __WIN__
buf
->
append
(
position
++
,
"
\"
"
,
1
);
#endif
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
->
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
}
static
char
*
find_last_path_separator
(
char
*
path
,
uint
length
)
{
while
(
length
)
{
if
(
is_path_separator
(
path
[
length
]))
return
path
+
length
;
length
--
;
}
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
SYNOPSIS
get_default_option()
result buffer to put found value
result_len buffer size
option_name the name of the option, prefixed with "--"
DESCRIPTION
Get compile-in value of requested option from server
RETURN
0 - ok
1 - error occured
*/
int
Instance_options
::
get_default_option
(
char
*
result
,
size_t
result_len
,
const
char
*
option_name
)
{
int
rc
=
1
;
LEX_STRING
verbose_option
=
{
C_STRING_WITH_LEN
(
" --no-defaults --verbose --help"
)
};
/* reserve space for the path + option + final '\0' */
Buffer
cmd
(
mysqld_path
.
length
+
verbose_option
.
length
+
1
);
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
((
char
*
)
cmd
.
buffer
,
option_name
+
2
,
strlen
(
option_name
+
2
),
result
,
result_len
,
GET_VALUE
);
err:
return
rc
;
}
/*
Fill mysqld_version option (used at initialization stage)
SYNOPSIS
fill_instance_version()
DESCRIPTION
Get mysqld version string from "mysqld --version" output.
RETURN
FALSE - ok
TRUE - error occured
*/
bool
Instance_options
::
fill_instance_version
()
{
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
,
&
version_option
))
{
log_error
(
"Failed to get version of '%s': out of memory."
,
(
const
char
*
)
mysqld_path
.
str
);
return
TRUE
;
}
bzero
(
result
,
MAX_VERSION_LENGTH
);
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'
);
{
char
*
start
;
/* trim leading whitespaces */
start
=
result
;
while
(
my_isspace
(
default_charset_info
,
*
start
))
++
start
;
mysqld_version
=
strdup_root
(
&
alloc
,
start
);
}
return
FALSE
;
}
/*
Fill mysqld_real_path
SYNOPSIS
fill_mysqld_real_path()
DESCRIPTION
Get the real path to mysqld from "mysqld --help" output.
Will print the realpath of mysqld between "Usage: " and "[OPTIONS]"
This is needed if the mysqld_path variable is pointing at a
script(for example libtool) or a symlink.
RETURN
FALSE - ok
TRUE - error occured
*/
bool
Instance_options
::
fill_mysqld_real_path
()
{
char
result
[
FN_REFLEN
];
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
,
&
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
);
if
(
parse_output_and_get_value
((
char
*
)
cmd
.
buffer
,
STRING_WITH_LEN
(
"Usage: "
),
result
,
FN_REFLEN
,
GET_LINE
))
{
log_error
(
"Failed to get real path of '%s': unexpected output."
,
(
const
char
*
)
mysqld_path
.
str
);
return
TRUE
;
}
DBUG_ASSERT
(
*
result
!=
'\0'
);
{
char
*
options_str
;
/* chop the path of at [OPTIONS] */
if
((
options_str
=
strstr
(
result
,
"[OPTIONS]"
)))
*
options_str
=
'\0'
;
mysqld_real_path
.
str
=
strdup_root
(
&
alloc
,
result
);
mysqld_real_path
.
length
=
strlen
(
mysqld_real_path
.
str
);
}
return
FALSE
;
}
/*
Fill various log options
SYNOPSIS
fill_log_options()
DESCRIPTION
Compute paths to enabled log files. If the path is not specified in the
instance explicitly (I.e. log=/home/user/mysql.log), we try to guess the
file name and placement.
RETURN
FALSE - ok
TRUE - error occured
*/
bool
Instance_options
::
fill_log_options
()
{
Buffer
buff
;
enum
{
MAX_LOG_OPTION_LENGTH
=
256
};
char
datadir
[
MAX_LOG_OPTION_LENGTH
];
char
hostname
[
MAX_LOG_OPTION_LENGTH
];
uint
hostname_length
;
struct
log_files_st
{
const
char
*
name
;
uint
length
;
char
**
value
;
const
char
*
default_suffix
;
}
logs_st
[]
=
{
{
"--log-error"
,
11
,
&
(
logs
[
IM_LOG_ERROR
]),
".err"
},
{
"--log"
,
5
,
&
(
logs
[
IM_LOG_GENERAL
]),
".log"
},
{
"--log-slow-queries"
,
18
,
&
(
logs
[
IM_LOG_SLOW
]),
"-slow.log"
},
{
NULL
,
0
,
NULL
,
NULL
}
};
struct
log_files_st
*
log_files
;
/* compute hostname and datadir for the instance */
if
(
mysqld_datadir
==
NULL
)
{
if
(
get_default_option
(
datadir
,
MAX_LOG_OPTION_LENGTH
,
"--datadir"
))
return
TRUE
;
}
else
{
/* below is safe, as --datadir always has a value */
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
=
strlen
(
hostname
);
for
(
log_files
=
logs_st
;
log_files
->
name
;
log_files
++
)
{
for
(
int
i
=
0
;
(
argv
[
i
]
!=
0
);
i
++
)
{
if
(
!
strncmp
(
argv
[
i
],
log_files
->
name
,
log_files
->
length
))
{
/*
This is really log_files->name option if and only if it is followed
by '=', '\0' or space character. This way we can distinguish such
options as '--log' and '--log-bin'. This is checked in the following
two statements.
*/
if
(
argv
[
i
][
log_files
->
length
]
==
'\0'
||
my_isspace
(
default_charset_info
,
argv
[
i
][
log_files
->
length
]))
{
char
full_name
[
MAX_LOG_OPTION_LENGTH
];
fn_format
(
full_name
,
hostname
,
datadir
,
""
,
MY_UNPACK_FILENAME
|
MY_SAFE_PATH
);
if
((
MAX_LOG_OPTION_LENGTH
-
strlen
(
full_name
))
<=
strlen
(
log_files
->
default_suffix
))
return
TRUE
;
strmov
(
full_name
+
strlen
(
full_name
),
log_files
->
default_suffix
);
/*
If there were specified two identical logfiles options,
we would loose some memory in MEM_ROOT here. However
this situation is not typical.
*/
*
(
log_files
->
value
)
=
strdup_root
(
&
alloc
,
full_name
);
}
if
(
argv
[
i
][
log_files
->
length
]
==
'='
)
{
char
full_name
[
MAX_LOG_OPTION_LENGTH
];
fn_format
(
full_name
,
argv
[
i
]
+
log_files
->
length
+
1
,
datadir
,
""
,
MY_UNPACK_FILENAME
|
MY_SAFE_PATH
);
if
(
!
(
*
(
log_files
->
value
)
=
strdup_root
(
&
alloc
,
full_name
)))
return
TRUE
;
}
}
}
}
return
FALSE
;
}
/*
Get the full pid file name with path
SYNOPSIS
get_pid_filaname()
result buffer to sotre the pidfile value
IMPLEMENTATION
Get the data directory, then get the pid filename
(which is always set for an instance), then load the
full path with my_load_path(). It takes into account
whether it is already an absolute path or it should be
prefixed with the datadir and so on.
RETURN
0 - ok
1 - error occured
*/
int
Instance_options
::
get_pid_filename
(
char
*
result
)
{
char
datadir
[
MAX_PATH_LEN
];
if
(
mysqld_datadir
==
NULL
)
{
/* we might get an error here if we have wrong path to the mysqld binary */
if
(
get_default_option
(
datadir
,
sizeof
(
datadir
),
"--datadir"
))
return
1
;
}
else
strxnmov
(
datadir
,
MAX_PATH_LEN
-
1
,
mysqld_datadir
,
"/"
,
NullS
);
/* get the full path to the pidfile */
my_load_path
(
result
,
mysqld_pid_file
,
datadir
);
return
0
;
}
int
Instance_options
::
unlink_pidfile
()
{
return
unlink
(
pid_file_with_path
);
}
pid_t
Instance_options
::
load_pid
()
{
FILE
*
pid_file_stream
;
/* get the pid */
if
((
pid_file_stream
=
my_fopen
(
pid_file_with_path
,
O_RDONLY
|
O_BINARY
,
MYF
(
0
)))
!=
NULL
)
{
pid_t
pid
;
if
(
fscanf
(
pid_file_stream
,
"%i"
,
&
pid
)
!=
1
)
pid
=
-
1
;
my_fclose
(
pid_file_stream
,
MYF
(
0
));
return
pid
;
}
return
0
;
}
bool
Instance_options
::
complete_initialization
()
{
int
arg_idx
;
const
char
*
tmp
;
char
*
end
;
char
bin_name_firstchar
;
if
(
!
mysqld_path
.
str
)
{
/*
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
;
}
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
);
/* put back the first character of the binary name*/
end
[
1
]
=
bin_name_firstchar
;
}
if
(
mysqld_port
)
mysqld_port_val
=
atoi
(
mysqld_port
);
if
(
shutdown_delay
)
shutdown_delay_val
=
atoi
(
shutdown_delay
);
if
(
!
(
tmp
=
strdup_root
(
&
alloc
,
"--no-defaults"
)))
return
TRUE
;
if
(
!
mysqld_pid_file
)
{
char
pidfilename
[
MAX_PATH_LEN
];
char
hostname
[
MAX_PATH_LEN
];
/*
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
::
is_mysqld_compatible_name
(
&
instance_name
))
strxnmov
(
pidfilename
,
MAX_PATH_LEN
-
1
,
hostname
,
".pid"
,
NullS
);
else
strxnmov
(
pidfilename
,
MAX_PATH_LEN
-
1
,
instance_name
.
str
,
"-"
,
hostname
,
".pid"
,
NullS
);
}
else
{
if
(
Instance
::
is_mysqld_compatible_name
(
&
instance_name
))
strxnmov
(
pidfilename
,
MAX_PATH_LEN
-
1
,
"mysql"
,
".pid"
,
NullS
);
else
strxnmov
(
pidfilename
,
MAX_PATH_LEN
-
1
,
instance_name
.
str
,
".pid"
,
NullS
);
}
Named_value
option
((
char
*
)
"pid-file"
,
pidfilename
);
set_option
(
&
option
);
}
if
(
get_pid_filename
(
pid_file_with_path
))
return
TRUE
;
/* we need to reserve space for the final zero + possible default options */
if
(
!
(
argv
=
(
char
**
)
alloc_root
(
&
alloc
,
(
get_num_options
()
+
1
+
MAX_NUMBER_OF_DEFAULT_OPTIONS
)
*
sizeof
(
char
*
))))
return
TRUE
;
filled_default_options
=
0
;
/* the path must be first in the argv */
if
(
add_to_argv
(
mysqld_path
.
str
))
return
TRUE
;
if
(
add_to_argv
(
tmp
))
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
);
}
argv
[
arg_idx
]
=
0
;
if
(
fill_log_options
()
||
fill_mysqld_real_path
()
||
fill_instance_version
())
return
TRUE
;
return
FALSE
;
}
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
;
}
void
Instance_options
::
unset_option
(
const
char
*
option_name
)
{
int
idx
=
find_option
(
option_name
);
if
(
idx
<
0
)
return
;
/* the option has not been set. */
options
.
remove_element
(
idx
);
update_var
(
option_name
,
NULL
);
}
void
Instance_options
::
update_var
(
const
char
*
option_name
,
const
char
*
option_value
)
{
struct
options_st
{
const
char
*
name
;
uint
name_len
;
const
char
**
var
;
}
options_def
[]
=
{
{
"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
}
};
for
(
options_st
*
opt
=
options_def
;
opt
->
name
;
++
opt
)
{
if
(
!
strncmp
(
opt
->
name
,
option_name
,
opt
->
name_len
))
{
*
(
opt
->
var
)
=
option_value
;
break
;
}
}
}
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
;
}
return
-
1
;
}
int
Instance_options
::
add_to_argv
(
const
char
*
option
)
{
DBUG_ASSERT
(
filled_default_options
<
MAX_NUMBER_OF_DEFAULT_OPTIONS
);
if
(
option
)
argv
[
filled_default_options
++
]
=
(
char
*
)
option
;
return
0
;
}
/* function for debug purposes */
void
Instance_options
::
print_argv
()
{
int
i
;
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
]);
}
/*
We execute this function to initialize some options.
RETURN
FALSE - ok
TRUE - memory allocation error
*/
bool
Instance_options
::
init
(
const
LEX_STRING
*
instance_name_arg
)
{
instance_name
.
length
=
instance_name_arg
->
length
;
init_alloc_root
(
&
alloc
,
MEM_ROOT_BLOCK_SIZE
,
0
);
if
(
options
.
init
())
return
TRUE
;
if
(
!
(
instance_name
.
str
=
strmake_root
(
&
alloc
,
instance_name_arg
->
str
,
instance_name_arg
->
length
)))
return
TRUE
;
return
FALSE
;
}
Instance_options
::~
Instance_options
()
{
free_root
(
&
alloc
,
MYF
(
0
));
}
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
;
}
server-tools/instance-manager/instance_options.h
deleted
100644 → 0
View file @
ca8706ff
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
#include <my_sys.h>
#include "parse.h"
#include "portability.h"
/* for pid_t on Win32 */
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
/*
This class contains options of an instance and methods to operate them.
We do not provide this class with the means of synchronization as it is
supposed that options for instances are all loaded at once during the
instance_map initilization and we do not change them later. This way we
don't have to synchronize between threads.
*/
class
Instance_options
{
public:
/* 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
();
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
my_global.h to use my_load_path()
*/
enum
{
MAX_PATH_LEN
=
512
};
enum
{
MAX_NUMBER_OF_DEFAULT_OPTIONS
=
2
};
char
pid_file_with_path
[
MAX_PATH_LEN
];
char
**
argv
;
/*
Here we cache the version string, obtained from mysqld --version.
In the case when mysqld binary is not found we get NULL here.
*/
const
char
*
mysqld_version
;
/* We need the some options, so we store them as a separate pointers */
const
char
*
mysqld_socket
;
const
char
*
mysqld_datadir
;
const
char
*
mysqld_pid_file
;
LEX_STRING
instance_name
;
LEX_STRING
mysqld_path
;
LEX_STRING
mysqld_real_path
;
const
char
*
nonguarded
;
/* log enums are defined in parse.h */
char
*
logs
[
3
];
private:
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 */
server-tools/instance-manager/listener.cc
deleted
100644 → 0
View file @
ca8706ff
/* 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 */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "listener.h"
#include <my_global.h>
#include <mysql.h>
#include <violite.h>
#include <sys/stat.h>
#ifndef __WIN__
#include <sys/un.h>
#endif
#include "log.h"
#include "mysql_connection.h"
#include "options.h"
#include "portability.h"
#include "priv.h"
#include "thread_registry.h"
static
void
set_non_blocking
(
int
socket
)
{
#ifndef __WIN__
int
flags
=
fcntl
(
socket
,
F_GETFL
,
0
);
fcntl
(
socket
,
F_SETFL
,
flags
|
O_NONBLOCK
);
#else
u_long
arg
=
1
;
ioctlsocket
(
socket
,
FIONBIO
,
&
arg
);
#endif
}
static
void
set_no_inherit
(
int
socket
)
{
#ifndef __WIN__
int
flags
=
fcntl
(
socket
,
F_GETFD
,
0
);
fcntl
(
socket
,
F_SETFD
,
flags
|
FD_CLOEXEC
);
#endif
}
const
int
Listener
::
LISTEN_BACK_LOG_SIZE
=
5
;
/* standard backlog size */
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::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
one day.
See also comments in mysqlmanager.cc to picture general Instance Manager
architecture.
*/
void
Listener
::
run
()
{
int
i
,
n
=
0
;
#ifndef __WIN__
struct
sockaddr_un
unix_socket_address
;
#endif
log_info
(
"Listener: started."
);
thread_registry
->
register_thread
(
&
thread_info
);
FD_ZERO
(
&
read_fds
);
/* I. prepare 'listen' sockets */
if
(
create_tcp_socket
())
goto
err
;
#ifndef __WIN__
if
(
create_unix_socket
(
unix_socket_address
))
goto
err
;
#endif
/* II. Listen sockets and spawn childs */
for
(
i
=
0
;
i
<
num_sockets
;
i
++
)
n
=
max
(
n
,
sockets
[
i
]);
n
++
;
timeval
tv
;
while
(
!
thread_registry
->
is_shutdown
())
{
fd_set
read_fds_arg
=
read_fds
;
/*
We should reintialize timer as on linux it is modified
to reflect amount of time not slept.
*/
tv
.
tv_sec
=
0
;
tv
.
tv_usec
=
100000
;
/*
When using valgrind 2.0 this syscall doesn't get kicked off by a
signal during shutdown. This results in failing assert
(Thread_registry::~Thread_registry). Valgrind 2.2 works fine.
*/
int
rc
=
select
(
n
,
&
read_fds_arg
,
0
,
0
,
&
tv
);
if
(
rc
==
0
||
rc
==
-
1
)
{
if
(
rc
==
-
1
&&
errno
!=
EINTR
)
log_error
(
"Listener: select() failed: %s."
,
(
const
char
*
)
strerror
(
errno
));
continue
;
}
for
(
int
socket_index
=
0
;
socket_index
<
num_sockets
;
socket_index
++
)
{
/* Assuming that rc > 0 as we asked to wait forever */
if
(
FD_ISSET
(
sockets
[
socket_index
],
&
read_fds_arg
))
{
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
);
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
{
shutdown
(
client_fd
,
SHUT_RDWR
);
closesocket
(
client_fd
);
}
}
}
}
}
/* III. Release all resources and exit */
log_info
(
"Listener: shutdown requested, exiting..."
);
for
(
i
=
0
;
i
<
num_sockets
;
i
++
)
closesocket
(
sockets
[
i
]);
#ifndef __WIN__
unlink
(
unix_socket_address
.
sun_path
);
#endif
thread_registry
->
unregister_thread
(
&
thread_info
);
log_info
(
"Listener: finished."
);
return
;
err:
log_error
(
"Listener: failed to initialize. Initiate shutdown..."
);
// we have to close the ip sockets in case of error
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
();
return
;
}
int
Listener
::
create_tcp_socket
()
{
/* value to be set by setsockopt */
int
arg
=
1
;
int
ip_socket
=
socket
(
AF_INET
,
SOCK_STREAM
,
0
);
if
(
ip_socket
==
INVALID_SOCKET
)
{
log_error
(
"Listener: socket(AF_INET) failed: %s."
,
(
const
char
*
)
strerror
(
errno
));
return
-
1
;
}
struct
sockaddr_in
ip_socket_address
;
bzero
(
&
ip_socket_address
,
sizeof
(
ip_socket_address
));
ulong
im_bind_addr
;
if
(
Options
::
Main
::
bind_address
!=
0
)
{
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
::
Main
::
port_number
;
ip_socket_address
.
sin_family
=
AF_INET
;
ip_socket_address
.
sin_addr
.
s_addr
=
im_bind_addr
;
ip_socket_address
.
sin_port
=
(
unsigned
short
)
htons
((
unsigned
short
)
im_port
);
setsockopt
(
ip_socket
,
SOL_SOCKET
,
SO_REUSEADDR
,
(
char
*
)
&
arg
,
sizeof
(
arg
));
if
(
bind
(
ip_socket
,
(
struct
sockaddr
*
)
&
ip_socket_address
,
sizeof
(
ip_socket_address
)))
{
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: listen(ip socket) failed: %s."
,
(
const
char
*
)
strerror
(
errno
));
closesocket
(
ip_socket
);
return
-
1
;
}
/* set the socket nonblocking */
set_non_blocking
(
ip_socket
);
/* make sure that instances won't be listening our sockets */
set_no_inherit
(
ip_socket
);
FD_SET
(
ip_socket
,
&
read_fds
);
sockets
[
num_sockets
++
]
=
ip_socket
;
log_info
(
"Listener: accepting connections on ip socket (port: %d)..."
,
(
int
)
im_port
);
return
0
;
}
#ifndef __WIN__
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: 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
::
Main
::
socket_file_name
,
sizeof
(
unix_socket_address
.
sun_path
));
unlink
(
unix_socket_address
.
sun_path
);
// in case we have stale socket file
/*
POSIX specifies default permissions for a pathname created by bind
to be 0777. We need everybody to have access to the socket.
*/
mode_t
old_mask
=
umask
(
0
);
if
(
bind
(
unix_socket
,
(
struct
sockaddr
*
)
&
unix_socket_address
,
sizeof
(
unix_socket_address
)))
{
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
;
}
umask
(
old_mask
);
if
(
listen
(
unix_socket
,
LISTEN_BACK_LOG_SIZE
))
{
log_error
(
"Listener: listen(unix socket) failed: %s."
,
(
const
char
*
)
strerror
(
errno
));
close
(
unix_socket
);
return
-
1
;
}
/* set the socket nonblocking */
set_non_blocking
(
unix_socket
);
/* make sure that instances won't be listening our sockets */
set_no_inherit
(
unix_socket
);
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
;
}
#endif
/*
Create new mysql connection. Created thread is responsible for deletion of
the Mysql_connection and Vio instances passed to it.
SYNOPSIS
handle_new_mysql_connection()
*/
void
Listener
::
handle_new_mysql_connection
(
struct
st_vio
*
vio
)
{
Mysql_connection
*
mysql_connection
=
new
Mysql_connection
(
thread_registry
,
user_map
,
vio
,
++
total_connection_count
);
if
(
mysql_connection
==
NULL
||
mysql_connection
->
start
(
Thread
::
DETACHED
))
{
log_error
(
"Listener: can not start connection handler."
);
delete
mysql_connection
;
vio_delete
(
vio
);
}
/* The connection will delete itself when the thread is finished */
}
server-tools/instance-manager/listener.h
deleted
100644 → 0
View file @
ca8706ff
/* 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_INSTANCE_MANAGER_LISTENER_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H
#include "thread_registry.h"
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
class
Thread_registry
;
class
User_map
;
/**
Listener - a thread listening on sockets and spawning
connection threads.
*/
class
Listener
:
public
Thread
{
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
server-tools/instance-manager/log.cc
deleted
100644 → 0
View file @
ca8706ff
/* 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 */
#include "log.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
- rewrite all fprintf with fwrite
- think about using 'write' instead of fwrite/fprintf on POSIX systems
*/
/*
Format log entry and write it to the given stream.
SYNOPSIS
log()
*/
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()
once per log(), or use flockfile()/funlockfile(). But flockfile() is
POSIX, not ANSI C, so we try to vsnprintf the whole message to the
stack, and if stack buffer is not enough, to malloced string. When
message is formatted, it is fprintf()'ed to the file.
*/
/* Format time like MYSQL_LOG does. */
time_t
now
=
time
(
0
);
struct
tm
bd_time
;
// broken-down time
localtime_r
(
&
now
,
&
bd_time
);
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
];
int
n
=
vsnprintf
(
buff_stack
,
sizeof
(
buff_stack
),
format
,
args
);
/*
return value of vsnprintf can vary, according to various standards;
try to check all cases.
*/
char
*
buff_msg
=
buff_stack
;
if
(
n
<
0
||
n
==
sizeof
(
buff_stack
))
{
int
size
=
sizeof
(
buff_stack
)
*
2
;
buff_msg
=
(
char
*
)
my_malloc
(
size
,
MYF
(
0
));
while
(
TRUE
)
{
if
(
buff_msg
==
0
)
{
strmake
(
buff_stack
,
"log(): message is too big, my_malloc() failed"
,
sizeof
(
buff_stack
)
-
1
);
buff_msg
=
buff_stack
;
break
;
}
n
=
vsnprintf
(
buff_msg
,
size
,
format
,
args
);
if
(
n
>=
0
&&
n
<
size
)
break
;
size
*=
2
;
/* realloc() does unnecessary memcpy */
my_free
(
buff_msg
,
0
);
buff_msg
=
(
char
*
)
my_malloc
(
size
,
MYF
(
0
));
}
}
else
if
((
size_t
)
n
>
sizeof
(
buff_stack
))
{
buff_msg
=
(
char
*
)
my_malloc
(
n
+
1
,
MYF
(
0
));
#ifdef DBUG
DBUG_ASSERT
(
n
==
vsnprintf
(
buff_msg
,
n
+
1
,
format
,
args
));
#else
vsnprintf
(
buff_msg
,
n
+
1
,
format
,
args
);
#endif
}
fprintf
(
file
,
"%s%s
\n
"
,
buff_date
,
buff_msg
);
if
(
buff_msg
!=
buff_stack
)
my_free
(
buff_msg
,
0
);
/* don't fflush() the file: buffering strategy is set in log_init() */
}
/**************************************************************************
Logging: implementation of public interface.
**************************************************************************/
/*
The function initializes logging sub-system.
SYNOPSIS
log_init()
*/
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
);
}
/*
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
);
log
(
stdout
,
"ERROR"
,
format
,
args
);
fflush
(
stdout
);
log
(
stderr
,
"ERROR"
,
format
,
args
);
fflush
(
stderr
);
va_end
(
args
);
}
/*
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
);
log
(
stdout
,
"INFO"
,
format
,
args
);
va_end
(
args
);
}
/*
The function prints information to the error log and eixt(1).
SYNOPSIS
die()
format [IN] format string
... [IN] arguments to format
*/
void
die
(
const
char
*
format
,
...)
{
va_list
args
;
fprintf
(
stderr
,
"%s: "
,
my_progname
);
va_start
(
args
,
format
);
vfprintf
(
stderr
,
format
,
args
);
va_end
(
args
);
fprintf
(
stderr
,
"
\n
"
);
exit
(
1
);
}
server-tools/instance-manager/log.h
deleted
100644 → 0
View file @
ca8706ff
/* 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_INSTANCE_MANAGER_LOG_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_LOG_H
/*
Logging facilities.
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 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
*/
void
log_init
();
void
log_info
(
const
char
*
format
,
...)
#ifdef __GNUC__
__attribute__
((
format
(
printf
,
1
,
2
)))
#endif
;
void
log_error
(
const
char
*
format
,
...)
#ifdef __GNUC__
__attribute__
((
format
(
printf
,
1
,
2
)))
#endif
;
void
die
(
const
char
*
format
,
...)
#ifdef __GNUC__
__attribute__
((
format
(
printf
,
1
,
2
)))
#endif
;
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_LOG_H
server-tools/instance-manager/manager.cc
deleted
100644 → 0
View file @
ca8706ff
/* 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 */
#include "manager.h"
#include <my_global.h>
#include <m_string.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"
/**********************************************************************
{{{ Platform-specific implementation.
**********************************************************************/
#ifndef __WIN__
void
set_signals
(
sigset_t
*
mask
)
{
/* block signals */
sigemptyset
(
mask
);
sigaddset
(
mask
,
SIGINT
);
sigaddset
(
mask
,
SIGTERM
);
sigaddset
(
mask
,
SIGPIPE
);
sigaddset
(
mask
,
SIGHUP
);
signal
(
SIGPIPE
,
SIG_IGN
);
/*
We want this signal to be blocked in all theads but the signal
one. It is needed for the thr_alarm subsystem to work.
*/
sigaddset
(
mask
,
THR_SERVER_ALARM
);
/* all new threads will inherite this signal mask */
pthread_sigmask
(
SIG_BLOCK
,
mask
,
NULL
);
/*
In our case the signal thread also implements functions of alarm thread.
Here we init alarm thread functionality. We suppose that we won't have
more then 10 alarms at the same time.
*/
init_thr_alarm
(
10
);
}
#else
bool
have_signal
;
void
onsignal
(
int
signo
)
{
have_signal
=
TRUE
;
}
void
set_signals
(
sigset_t
*
set
)
{
signal
(
SIGINT
,
onsignal
);
signal
(
SIGTERM
,
onsignal
);
have_signal
=
FALSE
;
}
int
my_sigwait
(
const
sigset_t
*
set
,
int
*
sig
)
{
while
(
!
have_signal
)
{
Sleep
(
100
);
}
return
0
;
}
#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
Manager
::
stop_all_threads
()
{
/*
Let Guardian thread know that it should break it's processing cycle,
once it wakes up.
*/
p_guardian
->
request_shutdown
();
/* Stop all threads. */
p_thread_registry
->
deliver_shutdown
();
/* Set error status in the thread registry. */
p_thread_registry
->
set_error_status
();
}
/**
Initialize user map and load password file.
SYNOPSIS
init_user_map()
RETURN
FALSE on success
TRUE on failure
*/
bool
Manager
::
init_user_map
(
User_map
*
user_map
)
{
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 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
;
Guardian
guardian
(
&
thread_registry
,
&
instance_map
);
Listener
listener
(
&
thread_registry
,
&
user_map
);
p_instance_map
=
&
instance_map
;
p_guardian
=
&
guardian
;
p_thread_registry
=
&
thread_registry
;
p_user_map
=
&
user_map
;
/* Initialize instance map. */
if
(
instance_map
.
init
())
{
log_error
(
"Manager: can not initialize instance list: out of memory."
);
return
1
;
}
/* Initialize user db. */
if
(
init_user_map
(
&
user_map
))
return
1
;
/* logging has been already done. */
/* 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.
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().
*/
sigset_t
mask
;
set_signals
(
&
mask
);
/*
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. */
if
(
Manager
::
flush_instances
())
{
log_error
(
"Manager: can not init instances repository."
);
stop_all_threads
();
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.
*/
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
(
"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
signals. This is why we skip it's processing on these
platforms. For more details and test program see
Bug #14164 IM tests fail on MacOS X (powermacg5)
*/
#ifdef IGNORE_SIGHUP_SIGQUIT
if
(
SIGHUP
==
signo
)
continue
;
#endif
if
(
THR_SERVER_ALARM
==
signo
)
process_alarm
(
signo
);
else
#endif
{
log_info
(
"Manager: got shutdown signal."
);
if
(
!
guardian
.
is_stopped
())
{
guardian
.
request_shutdown
();
}
else
{
thread_registry
.
deliver_shutdown
();
shutdown_complete
=
TRUE
;
}
}
}
log_info
(
"Manager: finished."
);
err:
/* delete the pid file */
my_delete
(
Options
::
Main
::
pid_file_name
,
MYF
(
0
));
#ifndef __WIN__
/* free alarm structures */
end_thr_alarm
(
1
);
#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
;
}
server-tools/instance-manager/manager.h
deleted
100644 → 0
View file @
ca8706ff
/* 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_INSTANCE_MANAGER_MANAGER_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
#include <my_global.h>
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
server-tools/instance-manager/messages.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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 */
#include "messages.h"
#include <my_global.h>
#include <mysql_com.h>
#include "mysqld_error.h"
#include "mysql_manager_error.h"
static
const
char
*
mysqld_error_message
(
unsigned
sql_errno
)
{
switch
(
sql_errno
)
{
case
ER_HANDSHAKE_ERROR
:
return
"Bad handshake"
;
case
ER_OUT_OF_RESOURCES
:
return
"Out of memory; Check if mysqld or some other process"
" uses all available memory. If not you may have to use"
" 'ulimit' to allow mysqld to use more memory or you can"
" add more swap space"
;
case
ER_ACCESS_DENIED_ERROR
:
return
"Access denied. Bad username/password pair"
;
case
ER_NOT_SUPPORTED_AUTH_MODE
:
return
"Client does not support authentication protocol requested by"
" server; consider upgrading MySQL client"
;
case
ER_UNKNOWN_COM_ERROR
:
return
"Unknown command"
;
case
ER_SYNTAX_ERROR
:
return
"You have an error in your command syntax. Check the manual that"
" corresponds to your MySQL Instance Manager version for the right"
" syntax to use"
;
case
ER_BAD_INSTANCE_NAME
:
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."
;
case
ER_INSTANCE_ALREADY_STARTED
:
return
"The instance is already started"
;
case
ER_CANNOT_START_INSTANCE
:
return
"Cannot start instance. Possible reasons are wrong instance options"
" or resources shortage"
;
case
ER_OFFSET_ERROR
:
return
"Cannot read negative number of bytes"
;
case
ER_STOP_INSTANCE
:
return
"Cannot stop instance"
;
case
ER_READ_FILE
:
return
"Cannot read requested part of the logfile"
;
case
ER_NO_SUCH_LOG
:
return
"The instance has no such log enabled"
;
case
ER_OPEN_LOGFILE
:
return
"Cannot open log file"
;
case
ER_GUESS_LOGFILE
:
return
"Cannot guess the log filename. Try specifying full log name"
" 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
;
}
}
const
char
*
message
(
unsigned
sql_errno
)
{
return
mysqld_error_message
(
sql_errno
);
}
const
char
*
errno_to_sqlstate
(
unsigned
sql_errno
)
{
return
mysql_errno_to_sqlstate
(
sql_errno
);
}
server-tools/instance-manager/messages.h
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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_INSTANCE_MANAGER_MESSAGES_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_MESSAGES_H
const
char
*
message
(
unsigned
sql_errno
);
const
char
*
errno_to_sqlstate
(
unsigned
sql_errno
);
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MESSAGES_H
server-tools/instance-manager/mysql_connection.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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 */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "mysql_connection.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
::
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
)
{
}
/*
NET subsystem requieres its user to provide my_net_local_init extern
C function (exactly as declared below). my_net_local_init is called by
my_net_init and is supposed to set NET controlling variables.
See also priv.h for variables description.
*/
C_MODE_START
void
my_net_local_init
(
NET
*
net
)
{
net
->
max_packet
=
net_buffer_length
;
my_net_set_read_timeout
(
net
,
(
uint
)
net_read_timeout
);
my_net_set_write_timeout
(
net
,
(
uint
)
net_write_timeout
);
net
->
retry_count
=
net_retry_count
;
net
->
max_packet_size
=
max_allowed_packet
;
}
C_MODE_END
/*
Unused stub hook required for linking the client API.
*/
C_MODE_START
void
slave_io_thread_detach_vio
()
{
}
C_MODE_END
/*
Every resource, which we can fail to acquire, is allocated in init().
This function is complementary to cleanup().
*/
bool
Mysql_connection
::
init
()
{
/* Allocate buffers for network I/O */
if
(
my_net_init
(
&
net
,
vio
))
return
TRUE
;
net
.
return_status
=
&
status
;
/* Initialize random number generator */
{
ulong
seed1
=
(
ulong
)
&
rand_st
+
rand
();
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
;
thread_registry
->
register_thread
(
&
thread_info
);
return
FALSE
;
}
void
Mysql_connection
::
cleanup
()
{
net_end
(
&
net
);
thread_registry
->
unregister_thread
(
&
thread_info
);
}
Mysql_connection
::~
Mysql_connection
()
{
/* vio_delete closes the socket if necessary */
vio_delete
(
vio
);
}
void
Mysql_connection
::
main
()
{
log_info
(
"Connection %lu: accepted."
,
(
unsigned
long
)
connection_id
);
if
(
check_connection
())
{
log_info
(
"Connection %lu: failed to authorize the user."
,
(
unsigned
long
)
connection_id
);
return
;
}
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
())
{
if
(
do_command
())
break
;
}
}
int
Mysql_connection
::
check_connection
()
{
ulong
pkt_len
=
0
;
// to hold client reply length
/* buffer for the first packet */
/* packet contains: */
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,
uchar
*
pos
=
buff
;
ulong
server_flags
;
memcpy
(
pos
,
mysqlmanager_version
.
str
,
mysqlmanager_version
.
length
+
1
);
pos
+=
mysqlmanager_version
.
length
+
1
;
int4store
((
uchar
*
)
pos
,
connection_id
);
pos
+=
4
;
/*
Old clients does not understand long scrambles, but can ignore packet
tail: that's why first part of the scramble is placed here, and second
part at the end of packet (even though we don't support old clients,
we must follow standard packet format.)
*/
memcpy
(
pos
,
scramble
,
SCRAMBLE_LENGTH_323
);
pos
+=
SCRAMBLE_LENGTH_323
;
*
pos
++=
'\0'
;
server_flags
=
CLIENT_LONG_FLAG
|
CLIENT_PROTOCOL_41
|
CLIENT_SECURE_CONNECTION
;
/*
18-bytes long section for various flags/variables
Every flag occupies a bit in first half of ulong; int2store will
gracefully pick up all flags.
*/
int2store
(
pos
,
server_flags
);
pos
+=
2
;
*
pos
++=
(
char
)
default_charset_info
->
number
;
// global mysys variable
int2store
(
pos
,
status
);
// connection status
pos
+=
2
;
bzero
(
pos
,
13
);
// not used now
pos
+=
13
;
/* second part of the scramble, null-terminated */
memcpy
(
pos
,
scramble
+
SCRAMBLE_LENGTH_323
,
SCRAMBLE_LENGTH
-
SCRAMBLE_LENGTH_323
+
1
);
pos
+=
SCRAMBLE_LENGTH
-
SCRAMBLE_LENGTH_323
+
1
;
/* write connection message and read reply */
enum
{
MIN_HANDSHAKE_SIZE
=
2
};
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
)
{
net_send_error
(
&
net
,
ER_HANDSHAKE_ERROR
);
return
1
;
}
client_capabilities
=
uint2korr
(
net
.
read_pos
);
if
(
!
(
client_capabilities
&
CLIENT_PROTOCOL_41
))
{
net_send_error_323
(
&
net
,
ER_NOT_SUPPORTED_AUTH_MODE
);
return
1
;
}
client_capabilities
|=
((
ulong
)
uint2korr
(
net
.
read_pos
+
2
))
<<
16
;
pos
=
net
.
read_pos
+
32
;
/* At least one byte for username and one byte for password */
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
=
(
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_name
,
password
,
scramble
))
{
net_send_error
(
&
net
,
ER_ACCESS_DENIED_ERROR
);
return
1
;
}
net_send_ok
(
&
net
,
connection_id
,
NULL
);
return
0
;
}
int
Mysql_connection
::
do_command
()
{
char
*
packet
;
ulong
packet_length
;
/* We start to count packets from 0 for each new command */
net
.
pkt_nr
=
0
;
if
((
packet_length
=
my_net_read
(
&
net
))
==
packet_error
)
{
/* 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
())
return
1
;
net_send_error
(
&
net
,
net
.
last_errno
);
net
.
error
=
0
;
return
0
;
}
else
{
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: 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
::
dispatch_command
(
enum
enum_server_command
command
,
const
char
*
packet
)
{
switch
(
command
)
{
case
COM_QUIT
:
// client exit
log_info
(
"Connection %lu: received QUIT command."
,
(
unsigned
long
)
connection_id
);
return
1
;
case
COM_PING
:
log_info
(
"Connection %lu: received PING command."
,
(
unsigned
long
)
connection_id
);
net_send_ok
(
&
net
,
connection_id
,
NULL
);
return
0
;
case
COM_QUERY
:
{
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
(
"Connection %lu: query parsed successfully."
,
(
unsigned
long
)
connection_id
);
res
=
com
->
execute
(
&
net
,
connection_id
);
delete
com
;
if
(
!
res
)
{
log_info
(
"Connection %lu: query executed successfully"
,
(
unsigned
long
)
connection_id
);
}
else
{
log_info
(
"Connection %lu: can not execute query (error: %d)."
,
(
unsigned
long
)
connection_id
,
(
int
)
res
);
net_send_error
(
&
net
,
res
);
}
}
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
;
}
default:
log_info
(
"Connection %lu: received unsupported command (%d)."
,
(
unsigned
long
)
connection_id
,
(
int
)
command
);
net_send_error
(
&
net
,
ER_UNKNOWN_COM_ERROR
);
return
0
;
}
return
0
;
/* Just to make compiler happy. */
}
void
Mysql_connection
::
run
()
{
if
(
init
())
log_error
(
"Connection %lu: can not init handler."
,
(
unsigned
long
)
connection_id
);
else
{
main
();
cleanup
();
}
delete
this
;
}
/*
vim: fdm=marker
*/
server-tools/instance-manager/mysql_connection.h
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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_INSTANCE_MANAGER_MYSQL_CONNECTION_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_CONNECTION_H
#include "thread_registry.h"
#include <mysql_com.h>
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
struct
st_vio
;
class
User_map
;
/*
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
;
ulong
connection_id
;
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
();
/* 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
server-tools/instance-manager/mysql_manager_error.h
deleted
100644 → 0
View file @
ca8706ff
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* Definefile for instance manager error messagenumbers */
#define ER_BAD_INSTANCE_NAME 3000
#define ER_INSTANCE_IS_NOT_STARTED 3001
#define ER_INSTANCE_ALREADY_STARTED 3002
#define ER_CANNOT_START_INSTANCE 3003
#define ER_STOP_INSTANCE 3004
#define ER_NO_SUCH_LOG 3005
#define ER_OPEN_LOGFILE 3006
#define ER_GUESS_LOGFILE 3007
#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 */
server-tools/instance-manager/mysqlmanager.cc
deleted
100644 → 0
View file @
ca8706ff
/* 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 */
#include <my_global.h>
#include <my_dir.h>
#include <my_sys.h>
#include <string.h>
#ifndef __WIN__
#include <pwd.h>
#include <grp.h>
#endif
#include "angel.h"
#include "log.h"
#include "manager.h"
#include "options.h"
#include "user_management_commands.h"
#ifdef __WIN__
#include "IMService.h"
#endif
/*
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
int
main_impl
(
int
argc
,
char
*
argv
[]);
#ifndef __WIN__
static
struct
passwd
*
check_user
();
static
bool
switch_user
();
#endif
/************************************************************************/
/**
The entry point.
*************************************************************************/
int
main
(
int
argc
,
char
*
argv
[])
{
int
return_value
;
puts
(
"
\n
"
"WARNING: This program is deprecated and will be removed in 6.0.
\n
"
);
/* Initialize. */
MY_INIT
(
argv
[
0
]);
log_init
();
umask
(
0117
);
srand
((
uint
)
time
(
0
));
/* Main function. */
log_info
(
"IM: started."
);
return_value
=
main_impl
(
argc
,
argv
);
log_info
(
"IM: finished."
);
/* Cleanup. */
Options
::
cleanup
();
my_end
(
0
);
return
return_value
;
}
/************************************************************************/
/**
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
=
Options
::
Daemon
::
user
;
struct
passwd
*
user_info
;
uid_t
user_id
=
geteuid
();
/* Don't bother if we aren't superuser */
if
(
user_id
)
{
if
(
user
)
{
/* Don't give a warning, if real user is same as given with --user */
user_info
=
getpwnam
(
user
);
if
((
!
user_info
||
user_id
!=
user_info
->
pw_uid
))
log_info
(
"One can only use the --user switch if running as root
\n
"
);
}
return
NULL
;
}
if
(
!
user
)
{
log_info
(
"You are running mysqlmanager as root! This might introduce security problems. It is safer to use --user option istead.
\n
"
);
return
NULL
;
}
if
(
!
strcmp
(
user
,
"root"
))
return
NULL
;
/* Avoid problem with dynamic libraries */
if
(
!
(
user_info
=
getpwnam
(
user
)))
{
/* Allow a numeric uid to be used */
const
char
*
pos
;
for
(
pos
=
user
;
my_isdigit
(
default_charset_info
,
*
pos
);
pos
++
)
{}
if
(
*
pos
)
/* Not numeric id */
goto
err
;
if
(
!
(
user_info
=
getpwuid
(
atoi
(
user
))))
goto
err
;
else
return
user_info
;
}
else
return
user_info
;
err:
log_error
(
"Can not start under user '%s'."
,
(
const
char
*
)
user
);
return
NULL
;
}
/************************************************************************/
/**
Switch user.
*************************************************************************/
static
bool
switch_user
()
{
struct
passwd
*
user_info
=
check_user
();
if
(
!
user_info
)
return
FALSE
;
#ifdef HAVE_INITGROUPS
initgroups
(
Options
::
Daemon
::
user
,
user_info
->
pw_gid
);
#endif
if
(
setgid
(
user_info
->
pw_gid
)
==
-
1
)
{
log_error
(
"setgid() failed"
);
return
TRUE
;
}
if
(
setuid
(
user_info
->
pw_uid
)
==
-
1
)
{
log_error
(
"setuid() failed"
);
return
TRUE
;
}
return
FALSE
;
}
#endif
server-tools/instance-manager/options.cc
deleted
100644 → 0
View file @
ca8706ff
/* 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 */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "options.h"
#include <my_global.h>
#include <my_sys.h>
#include <my_getopt.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__
/* 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
/* 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
;
const
char
**
Options
::
default_directories
=
NULL
;
/* 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
;
#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
);
/*
List of options, accepted by the instance manager.
List must be closed with empty option.
*/
enum
options
{
OPT_USERNAME
=
'u'
,
OPT_PASSWORD
=
'p'
,
OPT_LOG
=
256
,
OPT_PID_FILE
,
OPT_SOCKET
,
OPT_PASSWORD_FILE
,
OPT_MYSQLD_PATH
,
#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_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
[]
=
{
{
"help"
,
'?'
,
"Display this help and exit."
,
0
,
0
,
0
,
GET_NO_ARG
,
NO_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."
,
(
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
{
"bind-address"
,
OPT_BIND_ADDRESS
,
"Bind address to use for connection."
,
(
uchar
*
*
)
&
Options
::
Main
::
bind_address
,
(
uchar
*
*
)
&
Options
::
Main
::
bind_address
,
0
,
GET_STR
,
REQUIRED_ARG
,
0
,
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
},
{
"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."
,
(
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."
,
(
uchar
*
*
)
&
Options
::
Main
::
monitoring_interval
,
(
uchar
*
*
)
&
Options
::
Main
::
monitoring_interval
,
0
,
GET_UINT
,
REQUIRED_ARG
,
DEFAULT_MONITORING_INTERVAL
,
0
,
0
,
0
,
0
,
0
},
{
"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."
,
(
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."
,
(
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"
,
(
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."
,
(
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
,
(
const
char
*
)
mysqlmanager_version
.
str
,
SYSTEM_TYPE
,
MACHINE_TYPE
);
}
static
const
char
*
default_groups
[]
=
{
"manager"
,
0
};
static
void
usage
()
{
version
();
printf
(
"Copyright (C) 2003, 2004 MySQL AB
\n
"
"This software comes with ABSOLUTELY NO WARRANTY. This is free software,
\n
"
"and you are welcome to modify and redistribute it under the GPL license
\n
"
);
printf
(
"Usage: %s [OPTIONS]
\n
"
,
my_progname
);
my_print_help
(
my_long_options
);
printf
(
"
\n
The following options may be given as the first argument:
\n
"
"--print-defaults Print the program argument list and exit
\n
"
"--defaults-file=# Only read manager configuration and instance
\n
"
" setings from the given file #. The same file
\n
"
" will be used to modify configuration of instances
\n
"
" with SET commands.
\n
"
);
my_print_variables
(
my_long_options
);
}
C_MODE_START
static
my_bool
get_one_option
(
int
optid
,
const
struct
my_option
*
opt
__attribute__
((
unused
)),
char
*
argument
)
{
switch
(
optid
)
{
case
'V'
:
version
();
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
;
}
C_MODE_END
/*
- Process argv of original program: get tid of --defaults-extra-file
and print a message if met there.
- call load_defaults to load configuration file section and save the pointer
for free_defaults.
- call handle_options to assign defaults and command-line arguments
to the class members.
if either of these function fail, return the error code.
*/
int
Options
::
load
(
int
argc
,
char
**
argv
)
{
if
(
argc
>=
2
)
{
if
(
is_prefix
(
argv
[
1
],
"--defaults-file="
))
{
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"
))
{
/* the log is not enabled yet */
fprintf
(
stderr
,
"The --defaults-extra-file and --no-defaults options"
" are not supported by
\n
"
"Instance Manager. Program aborted.
\n
"
);
return
ERR_INVALID_USAGE
;
}
}
#ifdef __WIN__
if
(
setup_windows_defaults
())
{
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 */
log_info
(
"Loading config file '%s'..."
,
(
const
char
*
)
Main
::
config_file
);
my_load_defaults
(
Main
::
config_file
,
default_groups
,
&
argc
,
&
saved_argv
,
&
default_directories
);
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
::
Daemon
::
run_as_service
)
{
if
(
Options
::
Daemon
::
angel_pid_file_name
==
NULL
)
{
/*
Calculate angel pid file on the IM pid file basis: replace the
extension (everything after the last dot) of the pid file basename to
'.angel.pid'.
*/
char
*
local_angel_pid_file_name
;
char
*
base_name_ptr
;
char
*
ext_ptr
;
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
::
Main
::
pid_file_name
);
base_name_ptr
=
strrchr
(
local_angel_pid_file_name
,
'/'
);
if
(
!
base_name_ptr
)
base_name_ptr
=
local_angel_pid_file_name
+
1
;
ext_ptr
=
strrchr
(
base_name_ptr
,
'.'
);
if
(
ext_ptr
)
*
ext_ptr
=
0
;
strcat
(
local_angel_pid_file_name
,
ANGEL_PID_FILE_SUFFIX
);
Options
::
Daemon
::
angel_pid_file_name
=
local_angel_pid_file_name
;
}
else
{
Options
::
Daemon
::
angel_pid_file_name
=
strdup
(
Options
::
Daemon
::
angel_pid_file_name
);
}
}
#endif
return
0
;
}
void
Options
::
cleanup
()
{
if
(
saved_argv
)
free_defaults
(
saved_argv
);
delete
User_management
::
cmd
;
#ifndef __WIN__
if
(
Options
::
Daemon
::
run_as_service
)
free
((
void
*
)
Options
::
Daemon
::
angel_pid_file_name
);
#endif
}
#ifdef __WIN__
static
int
setup_windows_defaults
()
{
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
;
}
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
);
return
0
;
}
#endif
server-tools/instance-manager/options.h
deleted
100644 → 0
View file @
ca8706ff
/* 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_INSTANCE_MANAGER_OPTIONS_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H
/*
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
{
/*
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
struct
Service
{
static
my_bool
install_as_service
;
static
my_bool
remove_service
;
static
my_bool
stand_alone
;
};
#endif
public:
/* Array of paths to be passed to my_search_option_files() later */
static
const
char
**
default_directories
;
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
;
};
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H
server-tools/instance-manager/parse.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "parse.h"
#include "commands.h"
enum
Token
{
TOK_CREATE
=
0
,
TOK_DROP
,
TOK_ERROR
,
/* Encodes the "ERROR" word, it doesn't indicate error. */
TOK_FILES
,
TOK_FLUSH
,
TOK_GENERAL
,
TOK_INSTANCE
,
TOK_INSTANCES
,
TOK_LOG
,
TOK_OPTIONS
,
TOK_SET
,
TOK_SLOW
,
TOK_START
,
TOK_STATUS
,
TOK_STOP
,
TOK_SHOW
,
TOK_UNSET
,
TOK_NOT_FOUND
,
// must be after all tokens
TOK_END
};
struct
tokens_st
{
uint
length
;
const
char
*
tok_name
;
};
static
struct
tokens_st
tokens
[]
=
{
{
6
,
"CREATE"
},
{
4
,
"DROP"
},
{
5
,
"ERROR"
},
{
5
,
"FILES"
},
{
5
,
"FLUSH"
},
{
7
,
"GENERAL"
},
{
8
,
"INSTANCE"
},
{
9
,
"INSTANCES"
},
{
3
,
"LOG"
},
{
7
,
"OPTIONS"
},
{
3
,
"SET"
},
{
4
,
"SLOW"
},
{
5
,
"START"
},
{
6
,
"STATUS"
},
{
4
,
"STOP"
},
{
4
,
"SHOW"
},
{
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
,
size_t
word_len
)
{
int
i
=
0
;
do
{
if
(
my_strnncoll
(
default_charset_info
,
(
const
uchar
*
)
tokens
[
i
].
tok_name
,
tokens
[
i
].
length
,
(
const
uchar
*
)
word
,
word_len
)
==
0
)
break
;
}
while
(
++
i
<
TOK_NOT_FOUND
);
return
(
Token
)
i
;
}
Token
get_token
(
const
char
**
text
,
size_t
*
word_len
)
{
get_word
(
text
,
word_len
);
if
(
*
word_len
)
return
find_token
(
*
text
,
*
word_len
);
return
TOK_END
;
}
Token
shift_token
(
const
char
**
text
,
size_t
*
word_len
)
{
Token
save
=
get_token
(
text
,
word_len
);
(
*
text
)
+=
*
word_len
;
return
save
;
}
int
get_text_id
(
const
char
**
text
,
LEX_STRING
*
token
)
{
get_word
(
text
,
&
token
->
length
);
if
(
token
->
length
==
0
)
return
1
;
token
->
str
=
(
char
*
)
*
text
;
return
0
;
}
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
)
{
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
.
str
=
(
char
*
)
text
;
instance_name
.
length
=
word_len
;
text
+=
word_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
{
/* 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
)
goto
syntax_error
;
get_word
(
&
text
,
&
word_len
,
NONSPACE
);
if
(
word_len
)
goto
syntax_error
;
command
=
new
Flush_instances
();
break
;
case
TOK_UNSET
:
case
TOK_SET
:
{
Abstract_option_cmd
*
cmd
;
if
(
tok1
==
TOK_SET
)
cmd
=
new
Set_option
();
else
cmd
=
new
Unset_option
();
if
(
!
cmd
)
return
NULL
;
/* Report ER_OUT_OF_RESOURCES. */
if
(
cmd
->
init
(
&
text
))
{
delete
cmd
;
goto
syntax_error
;
}
command
=
cmd
;
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
();
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
))
goto
syntax_error
;
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
(
&
instance_name
);
else
command
=
new
Show_instance_options
(
&
instance_name
);
break
;
default:
goto
syntax_error
;
}
break
;
default:
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
))
{
case
TOK_FILES
:
get_word
(
&
text
,
&
word_len
,
NONSPACE
);
/* check that this is the end of the command */
if
(
word_len
)
goto
syntax_error
;
command
=
new
Show_instance_log_files
(
&
instance_name
);
break
;
case
TOK_ERROR
:
case
TOK_GENERAL
:
case
TOK_SLOW
:
/* define a log type */
switch
(
tok3
)
{
case
TOK_ERROR
:
log_type
=
IM_LOG_ERROR
;
break
;
case
TOK_GENERAL
:
log_type
=
IM_LOG_GENERAL
;
break
;
case
TOK_SLOW
:
log_type
=
IM_LOG_SLOW
;
break
;
default:
goto
syntax_error
;
}
/* get the size of the log we want to retrieve */
if
(
get_text_id
(
&
text
,
&
log_size_str
))
goto
syntax_error
;
text
+=
log_size_str
.
length
;
/* this parameter is required */
if
(
!
log_size_str
.
length
)
goto
syntax_error
;
/* the next token should be comma, or nothing */
get_word
(
&
text
,
&
word_len
);
switch
(
*
text
)
{
case
','
:
text
++
;
/* swallow the comma */
/* read the next word */
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
;
get_word
(
&
text
,
&
word_len
,
NONSPACE
);
/* check that this is the end of the command */
if
(
word_len
)
goto
syntax_error
;
break
;
case
'\0'
:
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
;
}
break
;
default:
goto
syntax_error
;
}
}
else
goto
syntax_error
;
break
;
}
break
;
default:
syntax_error:
command
=
new
Syntax_error
();
}
DBUG_ASSERT
(
command
);
return
command
;
}
server-tools/instance-manager/parse.h
deleted
100644 → 0
View file @
ca8706ff
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
#include <my_sys.h>
#include <m_string.h>
class
Command
;
enum
Log_type
{
IM_LOG_ERROR
=
0
,
IM_LOG_GENERAL
,
IM_LOG_SLOW
};
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
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
if found, returns the beginning and puts word length to word_len argument.
if not found returns pointer to first non-space or to '\0', word_len == 0
*/
inline
void
get_word
(
const
char
**
text
,
size_t
*
word_len
,
enum_seek_method
seek_method
=
ALPHANUM
)
{
const
char
*
word_end
;
/* skip space */
while
(
my_isspace
(
default_charset_info
,
**
text
))
++
(
*
text
);
word_end
=
*
text
;
switch
(
seek_method
)
{
case
ALPHANUM
:
while
(
my_isalnum
(
default_charset_info
,
*
word_end
))
++
word_end
;
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
);
}
#endif
/* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H */
server-tools/instance-manager/parse_output.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "parse_output.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
)
{
const
char
*
start
=
*
text
;
while
(
*
start
!=
0
&&
*
start
==
' '
)
start
++
;
*
text
=
start
;
int
len
=
strlen
(
start
);
const
char
*
end
=
start
+
len
-
1
;
while
(
end
>
start
&&
my_isspace
(
&
my_charset_latin1
,
*
end
))
end
--
;
*
word_len
=
(
end
-
start
)
+
1
;
}
/*************************************************************************/
/**
@brief A facade to the internal workings of optaining the output from an
executed system process.
*/
class
Mysqld_output_parser
{
public:
Mysqld_output_parser
()
{
}
virtual
~
Mysqld_output_parser
()
{
}
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
);
protected:
/**
@brief Run a process and attach stdout- and stdin-pipes to it.
@param command The path to the process to be executed
@return Error status.
@retval TRUE An error occurred
@retval FALSE Operation was a success
*/
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
*/
virtual
bool
read_line
(
char
*
line_buffer
,
uint
line_buffer_size
)
=
0
;
/**
@brief Release any resources needed after a execution and parsing.
*/
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
=
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
;
linep
+=
option_name_length
;
switch
(
option_type
)
{
case
GET_VALUE
:
trim_space
((
const
char
**
)
&
linep
,
&
found_word_len
);
if
(
option_value_buf_size
<=
found_word_len
)
{
cleanup
();
return
TRUE
;
}
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.
*/
setvbuf
(
m_stdout
,
NULL
,
_IOFBF
,
0
);
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
);
}
server-tools/instance-manager/parse_output.h
deleted
100644 → 0
View file @
ca8706ff
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_OUTPUT_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_OUTPUT_H
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
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 */
server-tools/instance-manager/portability.h
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2005-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 */
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PORTABILITY_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_PORTABILITY_H
#if (defined(_SCO_DS) || defined(UNIXWARE_7)) && !defined(SHUT_RDWR)
/*
SHUT_* functions are defined only if
"(defined(_XOPEN_SOURCE) && _XOPEN_SOURCE_EXTENDED - 0 >= 1)"
*/
#define SHUT_RDWR 2
#endif
#ifdef __WIN__
#define vsnprintf _vsnprintf
#define snprintf _snprintf
#define SIGKILL 9
/*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 */
server-tools/instance-manager/priv.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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 */
#include "priv.h"
#include <my_global.h>
#include <mysql_com.h>
#include <my_sys.h>
#include "log.h"
/*
The following string must be less then 80 characters, as
mysql_connection.cc relies on it
*/
const
LEX_STRING
mysqlmanager_version
=
{
C_STRING_WITH_LEN
(
"1.0-beta"
)
};
const
unsigned
char
protocol_version
=
PROTOCOL_VERSION
;
unsigned
long
net_buffer_length
=
16384
;
unsigned
long
max_allowed_packet
=
16384
;
unsigned
long
net_read_timeout
=
NET_WAIT_TIMEOUT
;
// same as in mysqld
unsigned
long
net_write_timeout
=
60
;
// same as in mysqld
unsigned
long
net_retry_count
=
10
;
// same as in mysqld
/* needed by net_serv.cc */
unsigned
int
test_flags
=
0
;
unsigned
long
bytes_sent
=
0L
,
bytes_received
=
0L
;
unsigned
long
mysqld_net_retry_count
=
10L
;
unsigned
long
open_files_limit
;
bool
create_pid_file
(
const
char
*
pid_file_name
,
int
pid
)
{
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
;
}
server-tools/instance-manager/priv.h
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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_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>
#ifndef __WIN__
#include <unistd.h>
#endif
#include "portability.h"
/* IM-wide platform-independent defines */
#define SERVER_DEFAULT_PORT MYSQL_PORT
#define DEFAULT_MONITORING_INTERVAL 20
#define DEFAULT_PORT 2273
/* three-week timeout should be enough */
#define LONG_TIMEOUT ((ulong) 3600L*24L*21L)
const
int
MEM_ROOT_BLOCK_SIZE
=
512
;
/* The maximal length of option name and option value. */
const
int
MAX_OPTION_LEN
=
1024
;
/*
The maximal length of whole option string:
--<option name>=<option value>
*/
const
int
MAX_OPTION_STR_LEN
=
2
+
MAX_OPTION_LEN
+
1
+
MAX_OPTION_LEN
+
1
;
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
;
/*
These variables are used in MySQL subsystem to work with mysql clients
To be moved to a config file/options one day.
*/
/* Buffer length for TCP/IP and socket communication */
extern
unsigned
long
net_buffer_length
;
/* Maximum allowed incoming/ougoung packet length */
extern
unsigned
long
max_allowed_packet
;
/*
Number of seconds to wait for more data from a connection before aborting
the read
*/
extern
unsigned
long
net_read_timeout
;
/*
Number of seconds to wait for a block to be written to a connection
before aborting the write.
*/
extern
unsigned
long
net_write_timeout
;
/*
If a read on a communication port is interrupted, retry this many times
before giving up.
*/
extern
unsigned
long
net_retry_count
;
extern
unsigned
int
test_flags
;
extern
unsigned
long
bytes_sent
,
bytes_received
;
extern
unsigned
long
mysqld_net_retry_count
;
extern
unsigned
long
open_files_limit
;
bool
create_pid_file
(
const
char
*
pid_file_name
,
int
pid
);
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H
server-tools/instance-manager/protocol.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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 */
#include "protocol.h"
#include "messages.h"
#include <mysql_com.h>
#include <m_string.h>
static
uchar
eof_buff
[
1
]
=
{
(
char
)
254
};
/* Marker for end of fields */
static
const
char
ERROR_PACKET_CODE
=
(
char
)
255
;
int
net_send_ok
(
struct
st_net
*
net
,
unsigned
long
connection_id
,
const
char
*
message
)
{
/*
The format of a packet
1 packet type code
1-9 affected rows count
1-9 connection id
2 thread return status
2 warning count
1-9 + message length message to send (isn't stored if no message)
*/
Buffer
buff
;
uchar
*
pos
=
buff
.
buffer
;
/* check that we have space to hold mandatory fields */
buff
.
reserve
(
0
,
23
);
enum
{
OK_PACKET_CODE
=
0
};
*
pos
++=
OK_PACKET_CODE
;
pos
=
net_store_length
(
pos
,
(
ulonglong
)
0
);
pos
=
net_store_length
(
pos
,
(
ulonglong
)
connection_id
);
int2store
(
pos
,
*
net
->
return_status
);
pos
+=
2
;
/* We don't support warnings, so store 0 for total warning count */
int2store
(
pos
,
0
);
pos
+=
2
;
size_t
position
=
pos
-
buff
.
buffer
;
/* we might need it for message */
if
(
message
!=
NULL
)
{
buff
.
reserve
(
position
,
9
+
strlen
(
message
));
store_to_protocol_packet
(
&
buff
,
message
,
&
position
);
}
return
my_net_write
(
net
,
buff
.
buffer
,
position
)
||
net_flush
(
net
);
}
int
net_send_error
(
struct
st_net
*
net
,
uint
sql_errno
)
{
const
char
*
err
=
message
(
sql_errno
);
char
buff
[
1
+
// packet type code
2
+
// sql error number
1
+
SQLSTATE_LENGTH
+
// sql state
MYSQL_ERRMSG_SIZE
];
// message
char
*
pos
=
buff
;
*
pos
++=
ERROR_PACKET_CODE
;
int2store
(
pos
,
sql_errno
);
pos
+=
2
;
/* The first # is to make the protocol backward compatible */
*
pos
++=
'#'
;
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
,
(
uchar
*
)
buff
,
(
size_t
)
(
pos
-
buff
))
||
net_flush
(
net
));
}
int
net_send_error_323
(
struct
st_net
*
net
,
uint
sql_errno
)
{
const
char
*
err
=
message
(
sql_errno
);
char
buff
[
1
+
// packet type code
2
+
// sql error number
MYSQL_ERRMSG_SIZE
];
// message
char
*
pos
=
buff
;
*
pos
++=
ERROR_PACKET_CODE
;
int2store
(
pos
,
sql_errno
);
pos
+=
2
;
pos
=
strmake
(
pos
,
err
,
MYSQL_ERRMSG_SIZE
-
1
)
+
1
;
return
(
my_net_write
(
net
,
(
uchar
*
)
buff
,
(
size_t
)
(
pos
-
buff
))
||
net_flush
(
net
));
}
char
*
net_store_length
(
char
*
pkg
,
uint
length
)
{
uchar
*
packet
=
(
uchar
*
)
pkg
;
if
(
length
<
251
)
{
*
packet
=
(
uchar
)
length
;
return
(
char
*
)
packet
+
1
;
}
*
packet
++=
252
;
int2store
(
packet
,(
uint
)
length
);
return
(
char
*
)
packet
+
2
;
}
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
=
(
net_store_length
(
buf
->
buffer
+
*
position
,
(
ulonglong
)
string_len
)
-
buf
->
buffer
);
if
(
buf
->
append
(
currpos
,
string
,
string_len
))
goto
err
;
*
position
=
*
position
+
string_len
+
(
currpos
-
*
position
);
return
0
;
err:
return
1
;
}
int
store_to_protocol_packet
(
Buffer
*
buf
,
const
char
*
string
,
size_t
*
position
)
{
size_t
string_len
;
string_len
=
strlen
(
string
);
return
store_to_protocol_packet
(
buf
,
string
,
position
,
string_len
);
}
int
send_eof
(
struct
st_net
*
net
)
{
uchar
buff
[
1
+
/* eof packet code */
2
+
/* warning count */
2
];
/* server status */
buff
[
0
]
=
254
;
int2store
(
buff
+
1
,
0
);
int2store
(
buff
+
3
,
0
);
return
my_net_write
(
net
,
buff
,
sizeof
(
buff
));
}
int
send_fields
(
struct
st_net
*
net
,
LIST
*
fields
)
{
LIST
*
tmp
=
fields
;
Buffer
send_buff
;
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
));
if
(
my_net_write
(
net
,
small_buff
,
(
uint
)
1
))
goto
err
;
while
(
tmp
)
{
position
=
0
;
field
=
(
LEX_STRING
*
)
tmp
->
data
;
store_to_protocol_packet
(
&
send_buff
,
(
char
*
)
""
,
&
position
);
/* catalog name */
store_to_protocol_packet
(
&
send_buff
,
(
char
*
)
""
,
&
position
);
/* db name */
store_to_protocol_packet
(
&
send_buff
,
(
char
*
)
""
,
&
position
);
/* table name */
store_to_protocol_packet
(
&
send_buff
,
(
char
*
)
""
,
&
position
);
/* table name alias */
store_to_protocol_packet
(
&
send_buff
,
field
->
str
,
&
position
);
/* column name */
store_to_protocol_packet
(
&
send_buff
,
field
->
str
,
&
position
);
/* column name alias */
send_buff
.
reserve
(
position
,
12
);
if
(
send_buff
.
is_error
())
goto
err
;
send_buff
.
buffer
[
position
++
]
=
12
;
int2store
(
send_buff
.
buffer
+
position
,
1
);
/* charsetnr */
int4store
(
send_buff
.
buffer
+
position
+
2
,
field
->
length
);
/* field length */
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
;
send_buff
.
buffer
[
position
+
11
]
=
0
;
position
+=
12
;
if
(
my_net_write
(
net
,
send_buff
.
buffer
,
(
uint
)
position
+
1
))
goto
err
;
tmp
=
list_rest
(
tmp
);
}
if
(
my_net_write
(
net
,
eof_buff
,
1
))
goto
err
;
return
0
;
err:
return
1
;
}
server-tools/instance-manager/protocol.h
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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_INSTANCE_MANAGER_PROTOCOL_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_PROTOCOL_H
#include "buffer.h"
#include <my_list.h>
/* default field length to be used in various field-realted functions */
enum
{
DEFAULT_FIELD_LENGTH
=
20
};
struct
st_net
;
int
net_send_ok
(
struct
st_net
*
net
,
unsigned
long
connection_id
,
const
char
*
message
);
int
net_send_error
(
struct
st_net
*
net
,
unsigned
sql_errno
);
int
net_send_error_323
(
struct
st_net
*
net
,
unsigned
sql_errno
);
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
,
size_t
*
position
);
int
store_to_protocol_packet
(
Buffer
*
buf
,
const
char
*
string
,
size_t
*
position
,
size_t
string_len
);
int
send_eof
(
struct
st_net
*
net
);
#endif
/* INCLUDES_MYSQL_INSTANCE_MANAGER_PROTOCOL_H */
server-tools/instance-manager/thread_registry.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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 */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "thread_registry.h"
#include <thr_alarm.h>
#include <signal.h>
#include "log.h"
#ifndef __WIN__
/* Kick-off signal handler */
enum
{
THREAD_KICK_OFF_SIGNAL
=
SIGUSR2
};
extern
"C"
void
handle_signal
(
int
);
void
handle_signal
(
int
__attribute__
((
unused
))
sig_no
)
{
}
#endif
/* Thread_info initializer methods */
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)
to Thread_info. It will reduce contention and allow signal deliverence to
a particular thread, not to the whole worker crew
*/
Thread_registry
::
Thread_registry
()
:
shutdown_in_progress
(
FALSE
)
,
sigwait_thread_pid
(
pthread_self
())
,
error_status
(
FALSE
)
{
pthread_mutex_init
(
&
LOCK_thread_registry
,
0
);
pthread_cond_init
(
&
COND_thread_registry_is_empty
,
0
);
/* head is used by-value to simplify nodes inserting */
head
.
next
=
head
.
prev
=
&
head
;
}
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
);
pthread_mutex_unlock
(
&
LOCK_thread_registry
);
pthread_cond_destroy
(
&
COND_thread_registry_is_empty
);
pthread_mutex_destroy
(
&
LOCK_thread_registry
);
}
/*
Set signal handler for kick-off thread, and insert a thread info to the
repository. New node is appended to the end of the list; head.prev always
points to the last node.
*/
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
;
sa
.
sa_flags
=
0
;
sigemptyset
(
&
sa
.
sa_mask
);
sigaction
(
THREAD_KICK_OFF_SIGNAL
,
&
sa
,
0
);
#endif
info
->
current_cond
=
0
;
pthread_mutex_lock
(
&
LOCK_thread_registry
);
info
->
next
=
&
head
;
info
->
prev
=
head
.
prev
;
head
.
prev
->
next
=
info
;
head
.
prev
=
info
;
pthread_mutex_unlock
(
&
LOCK_thread_registry
);
}
/*
Unregister a thread from the repository and free Thread_info structure.
Every registered thread must unregister. Unregistering should be the last
thing a thread is doing, otherwise it could have no time to finalize.
*/
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
);
}
/*
Check whether shutdown is in progress, and if yes, return immediately.
Else set info->current_cond and call pthread_cond_wait. When
pthread_cond_wait returns, unregister current cond and check the shutdown
status again.
RETURN VALUE
return value from pthread_cond_wait
*/
int
Thread_registry
::
cond_wait
(
Thread_info
*
info
,
pthread_cond_t
*
cond
,
pthread_mutex_t
*
mutex
)
{
pthread_mutex_lock
(
&
LOCK_thread_registry
);
if
(
shutdown_in_progress
)
{
pthread_mutex_unlock
(
&
LOCK_thread_registry
);
return
0
;
}
info
->
current_cond
=
cond
;
pthread_mutex_unlock
(
&
LOCK_thread_registry
);
/* sic: race condition here, cond can be signaled in deliver_shutdown */
int
rc
=
pthread_cond_wait
(
cond
,
mutex
);
pthread_mutex_lock
(
&
LOCK_thread_registry
);
info
->
current_cond
=
0
;
pthread_mutex_unlock
(
&
LOCK_thread_registry
);
return
rc
;
}
int
Thread_registry
::
cond_timedwait
(
Thread_info
*
info
,
pthread_cond_t
*
cond
,
pthread_mutex_t
*
mutex
,
struct
timespec
*
wait_time
)
{
int
rc
;
pthread_mutex_lock
(
&
LOCK_thread_registry
);
if
(
shutdown_in_progress
)
{
pthread_mutex_unlock
(
&
LOCK_thread_registry
);
return
0
;
}
info
->
current_cond
=
cond
;
pthread_mutex_unlock
(
&
LOCK_thread_registry
);
/* sic: race condition here, cond can be signaled in deliver_shutdown */
if
((
rc
=
pthread_cond_timedwait
(
cond
,
mutex
,
wait_time
))
==
ETIME
)
rc
=
ETIMEDOUT
;
// For easier usage
pthread_mutex_lock
(
&
LOCK_thread_registry
);
info
->
current_cond
=
0
;
pthread_mutex_unlock
(
&
LOCK_thread_registry
);
return
rc
;
}
/*
Deliver shutdown message to the workers crew.
As it's impossible to avoid all race conditions, signal latecomers
again.
*/
void
Thread_registry
::
deliver_shutdown
()
{
pthread_mutex_lock
(
&
LOCK_thread_registry
);
shutdown_in_progress
=
TRUE
;
#ifndef __WIN__
/* to stop reading from the network we need to flush alarm queue */
end_thr_alarm
(
0
);
/*
We have to deliver final alarms this way, as the main thread has already
stopped alarm processing.
*/
process_alarm
(
THR_SERVER_ALARM
);
#endif
/*
sic: race condition here, the thread may not yet fall into
pthread_cond_wait.
*/
interrupt_threads
();
wait_for_threads_to_unregister
();
/*
If previous signals did not reach some threads, they must be sleeping
in pthread_cond_wait or in a blocking syscall. Wake them up:
every thread shall check signal variables after each syscall/cond_wait,
so this time everybody should be informed (presumably each worker can
get CPU during shutdown_time.)
*/
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
)
{
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
);
}
void
Thread_registry
::
request_shutdown
()
{
pthread_kill
(
sigwait_thread_pid
,
SIGTERM
);
}
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
;
pthread_mutex_lock
(
&
LOCK_thread_registry
);
ret_error_status
=
error_status
;
pthread_mutex_unlock
(
&
LOCK_thread_registry
);
return
ret_error_status
;
}
void
Thread_registry
::
set_error_status
()
{
pthread_mutex_lock
(
&
LOCK_thread_registry
);
error_status
=
TRUE
;
pthread_mutex_unlock
(
&
LOCK_thread_registry
);
}
server-tools/instance-manager/thread_registry.h
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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_INSTANCE_MANAGER_THREAD_REGISTRY_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REGISTRY_H
/*
A multi-threaded application shall nicely work with signals.
This means it shall, first of all, shut down nicely on ``quit'' signals:
stop all running threads, cleanup and exit.
Note, that a thread can't be shut down nicely if it doesn't want to be.
That's why to perform clean shutdown, all threads constituting a process
must observe certain rules. Here we use the rules, described in Butenhof
book 'Programming with POSIX threads', namely:
- all user signals are handled in 'signal thread' in synchronous manner
(by means of sigwait). To guarantee that the signal thread is the only who
can receive user signals, all threads block them, and signal thread is
the only who calls sigwait() with an apporpriate sigmask.
To propogate a signal to the workers the signal thread sets
a variable, corresponding to the signal. Additionally the signal thread
sends each worker an internal signal (by means of pthread_kill) to kick it
out from possible blocking syscall, and possibly pthread_cond_signal if
some thread is blocked in pthread_cond_[timed]wait.
- a worker handles only internal 'kick' signal (the handler does nothing).
In case when a syscall returns 'EINTR' the worker checks all
signal-related variables and behaves accordingly.
Also these variables shall be checked from time to time in long
CPU-bounded operations, and before/after pthread_cond_wait. (It's supposed
that a worker thread either waits in a syscall/conditional variable, or
computes something.)
- to guarantee signal deliverence, there should be some kind of feedback,
e. g. all workers shall account in the signal thread Thread Repository and
unregister from it on exit.
Configuration reload (on SIGHUP) and thread timeouts/alarms can be handled
in manner, similar to ``quit'' signals.
*/
#include <my_global.h>
#include <my_pthread.h>
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
/**
Thread_info - repository entry for each worker thread
All entries comprise double-linked list like:
0 -- entry -- entry -- entry - 0
Double-linked list is used to unregister threads easy.
*/
class
Thread_info
{
public:
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.
*/
class
Thread_registry
{
public:
Thread_registry
();
~
Thread_registry
();
void
register_thread
(
Thread_info
*
info
,
bool
send_signal_on_shutdown
=
TRUE
);
void
unregister_thread
(
Thread_info
*
info
);
void
deliver_shutdown
();
void
request_shutdown
();
inline
bool
is_shutdown
();
int
cond_wait
(
Thread_info
*
info
,
pthread_cond_t
*
cond
,
pthread_mutex_t
*
mutex
);
int
cond_timedwait
(
Thread_info
*
info
,
pthread_cond_t
*
cond
,
pthread_mutex_t
*
mutex
,
struct
timespec
*
wait_time
);
int
get_error_status
();
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
&
);
};
inline
bool
Thread_registry
::
is_shutdown
()
{
pthread_mutex_lock
(
&
LOCK_thread_registry
);
bool
res
=
shutdown_in_progress
;
pthread_mutex_unlock
(
&
LOCK_thread_registry
);
return
res
;
}
#endif
/* INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REGISTRY_H */
server-tools/instance-manager/user_management_commands.cc
deleted
100644 → 0
View file @
ca8706ff
/* 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
;
}
server-tools/instance-manager/user_management_commands.h
deleted
100644 → 0
View file @
ca8706ff
#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
server-tools/instance-manager/user_map.cc
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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 */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "user_map.h"
#include "exit_codes.h"
#include "log.h"
#include "portability.h"
User
::
User
(
const
LEX_STRING
*
user_name_arg
,
const
char
*
password
)
{
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
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
]
!=
':'
)
{
log_error
(
"Invalid format (unmatched quote) of user line (%s)."
,
(
const
char
*
)
line
);
return
1
;
}
password
=
name_end
+
2
;
}
else
{
name_begin
=
line
;
name_end
=
strchr
(
name_begin
,
':'
);
if
(
name_end
==
0
)
{
log_error
(
"Invalid format (no delimiter) of user line (%s)."
,
(
const
char
*
)
line
);
return
1
;
}
password
=
name_end
+
1
;
}
user_length
=
(
uint8
)
(
name_end
-
name_begin
);
if
(
user_length
>
USERNAME_LENGTH
)
{
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'."
,
(
const
char
*
)
user
);
return
0
;
}
C_MODE_START
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
(
uchar
*
)
user
->
user
;
}
static
void
delete_user
(
void
*
u
)
{
User
*
user
=
(
User
*
)
u
;
delete
user
;
}
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
))
return
1
;
initialized
=
TRUE
;
return
0
;
}
User_map
::
User_map
()
:
initialized
(
FALSE
)
{
}
User_map
::~
User_map
()
{
if
(
initialized
)
hash_free
(
&
hash
);
}
/*
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
,
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 */
1
+
/* for ':' */
2
+
/* for newline */
1
];
/* for trailing zero */
User
*
user
;
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
)
{
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
(
!
user_line
[
0
]
||
user_line
[
0
]
==
'#'
)
continue
;
if
((
user
=
new
User
)
==
0
)
{
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
;
}
}
log_info
(
"The password database loaded successfully."
);
my_fclose
(
file
,
MYF
(
0
));
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
;
}
/*
Check if user exists and password is correct
RETURN VALUE
0 - user found and password OK
1 - password mismatch
2 - user not found
*/
int
User_map
::
authenticate
(
const
LEX_STRING
*
user_name
,
const
char
*
scrambled_password
,
const
char
*
scramble
)
const
{
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
;
}
server-tools/instance-manager/user_map.h
deleted
100644 → 0
View file @
ca8706ff
/* Copyright (C) 2004-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_INSTANCE_MANAGER_USER_MAP_H
#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
*/
class
User_map
{
public:
/* 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
,
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
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment