Merge. Backport WL#4085 from 6.0.

This commit is contained in:
Alexander Nozdrin 2009-10-10 19:19:12 +04:00
commit 8975cc5f98
84 changed files with 66 additions and 15307 deletions

View File

@ -816,7 +816,6 @@ sub command_line_setup {
'combination=s' => \@opt_combinations,
'skip-combinations' => \&collect_option,
'experimental=s' => \$opt_experimental,
'skip-im' => \&ignore_option,
# Specify ports
'build-thread|mtr-build-thread=i' => \$opt_build_thread,

View File

@ -1,47 +0,0 @@
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
SHOW INSTANCES;
instance_name state
mysqld1 XXXXX
mysqld2 offline
--> Listing users...
im_admin
==> Adding user 'testuser'...
--> IM password file:
testuser:*0D3CED9BEC10A777AEC23CCC353A8C08A633045E
im_admin:*598D51AD2DFF7792045D6DF3DDF9AA1AF737B295
--> EOF
--> Printing out line for 'testuser'...
testuser:*0D3CED9BEC10A777AEC23CCC353A8C08A633045E
--> Listing users...
im_admin
testuser
==> Changing the password of 'testuser'...
--> IM password file:
im_admin:*598D51AD2DFF7792045D6DF3DDF9AA1AF737B295
testuser:*39C549BDECFBA8AFC3CE6B948C9359A0ECE08DE2
--> EOF
--> Printing out line for 'testuser'...
testuser:*39C549BDECFBA8AFC3CE6B948C9359A0ECE08DE2
--> Listing users...
testuser
im_admin
==> Dropping user 'testuser'...
--> IM password file:
im_admin:*598D51AD2DFF7792045D6DF3DDF9AA1AF737B295
--> EOF
--> Listing users...
im_admin

View File

@ -1,26 +0,0 @@
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
SHOW INSTANCES;
instance_name state
mysqld1 XXXXX
mysqld2 offline
Killing the process...
Waiting...
Success: the process was restarted.
Success: server is ready to accept connection on socket.
--------------------------------------------------------------------
-- Test for BUG#12751
--------------------------------------------------------------------
START INSTANCE mysqld2;
Success: the process has been started.
Killing the process...
Waiting...
Success: the process was restarted.
Success: server is ready to accept connection on socket.
SHOW INSTANCE STATUS mysqld1;
instance_name state version_number version mysqld_compatible
mysqld1 STATE VERSION_NUMBER VERSION no
STOP INSTANCE mysqld2;
Success: the process has been stopped.

View File

@ -1,225 +0,0 @@
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
SHOW INSTANCES;
instance_name state
mysqld1 XXXXX
mysqld2 offline
--------------------------------------------------------------------
server_id = 1
server_id = 2
--------------------------------------------------------------------
CREATE INSTANCE mysqld3
server_id = 3,
socket = "$MYSQL_TMP_DIR/mysqld_3.sock";
SHOW INSTANCES;
instance_name state
mysqld3 offline
mysqld2 offline
mysqld1 online
--------------------------------------------------------------------
server_id = 1
server_id = 2
server_id=3
--------------------------------------------------------------------
CREATE INSTANCE mysqld1;
ERROR HY000: Instance already exists
CREATE INSTANCE mysqld2;
ERROR HY000: Instance already exists
CREATE INSTANCE mysqld3;
ERROR HY000: Instance already exists
--------------------------------------------------------------------
nonguarded
--------------------------------------------------------------------
CREATE INSTANCE mysqld4
nonguarded,
server_id = 4,
socket = "$MYSQL_TMP_DIR/mysqld_4.sock";
SHOW INSTANCES;
instance_name state
mysqld3 offline
mysqld4 offline
mysqld1 online
mysqld2 offline
--------------------------------------------------------------------
nonguarded
nonguarded
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
CREATE INSTANCE mysqld5
test-A = 000,
test-B = test,
server_id = 5,
socket = "$MYSQL_TMP_DIR/mysqld_5.sock";
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld4 offline
mysqld5 offline
mysqld2 offline
mysqld3 offline
--------------------------------------------------------------------
test-A=000
--------------------------------------------------------------------
test-B=test
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
CREATE INSTANCE mysqld6
test-C1 = 10 ,
test-C2 = 02 ,
server_id = 6,
socket = "$MYSQL_TMP_DIR/mysqld_6.sock";
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld2 offline
mysqld5 offline
mysqld6 offline
mysqld3 offline
mysqld4 offline
--------------------------------------------------------------------
test-C1=10
--------------------------------------------------------------------
test-C2=02
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
CREATE INSTANCE mysqld7 test-D = test-D-value ;
ERROR 42000: 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
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld2 offline
mysqld5 offline
mysqld6 offline
mysqld3 offline
mysqld4 offline
CREATE INSTANCE mysqld8 test-E 0 ;
ERROR 42000: 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
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld2 offline
mysqld5 offline
mysqld6 offline
mysqld3 offline
mysqld4 offline
CREATE INSTANCE mysqld8 test-F = ;
ERROR 42000: 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
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld2 offline
mysqld5 offline
mysqld6 offline
mysqld3 offline
mysqld4 offline
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
CREATE INSTANCE mysqld9
test-1=" hello world ",
test-2=' ',
server_id = 9,
socket = "$MYSQL_TMP_DIR/mysqld_9.sock";
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld2 offline
mysqld5 offline
mysqld6 offline
mysqld3 offline
mysqld4 offline
mysqld9 offline
CREATE INSTANCE mysqld10
test-3='\b\babc\sdef',
server_id = 10,
socket = "$MYSQL_TMP_DIR/mysqld_10.sock";
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld9 offline
mysqld5 offline
mysqld6 offline
mysqld3 offline
mysqld4 offline
mysqld10 offline
mysqld2 offline
CREATE INSTANCE mysqld11
test-4='abc\tdef',
test-5='abc\ndef',
server_id = 11,
socket = "$MYSQL_TMP_DIR/mysqld_11.sock";
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld11 offline
mysqld5 offline
mysqld6 offline
mysqld3 offline
mysqld4 offline
mysqld10 offline
mysqld2 offline
mysqld9 offline
CREATE INSTANCE mysqld12
test-6="abc\rdef",
test-7="abc\\def",
server_id = 12,
socket = "$MYSQL_TMP_DIR/mysqld_12.sock";
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld9 offline
mysqld5 offline
mysqld6 offline
mysqld3 offline
mysqld4 offline
mysqld10 offline
mysqld2 offline
mysqld12 offline
mysqld11 offline
CREATE INSTANCE mysqld13 test-bad=' \ ';
ERROR 42000: 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
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld9 offline
mysqld5 offline
mysqld6 offline
mysqld3 offline
mysqld4 offline
mysqld10 offline
mysqld2 offline
mysqld12 offline
mysqld11 offline
--------------------------------------------------------------------
test-1= hello world
--------------------------------------------------------------------
test-2=
--------------------------------------------------------------------
test-3=abc def
--------------------------------------------------------------------
test-4=abc def
--------------------------------------------------------------------
test-5=abc
--------------------------------------------------------------------
test-6=abc def
--------------------------------------------------------------------
test-7=abc\def
--------------------------------------------------------------------
--------------------------------------------------------------------
CREATE INSTANCE qqq1;
ERROR HY000: Malformed instance name.

View File

@ -1,73 +0,0 @@
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
SHOW INSTANCES;
instance_name state
mysqld1 XXXXX
mysqld2 offline
--------------------------------------------------------------------
-- 1.1.2.
--------------------------------------------------------------------
START INSTANCE mysqld2;
Success: the process has been started.
SHOW VARIABLES LIKE 'port';
Variable_name Value
port IM_MYSQLD2_PORT
--------------------------------------------------------------------
-- 1.1.3.
--------------------------------------------------------------------
STOP INSTANCE mysqld2;
Success: the process has been stopped.
--------------------------------------------------------------------
-- 1.1.4.
--------------------------------------------------------------------
START INSTANCE mysqld3;
ERROR HY000: Unknown instance name
START INSTANCE mysqld1;
ERROR HY000: The instance is already started
--------------------------------------------------------------------
-- 1.1.5.
--------------------------------------------------------------------
STOP INSTANCE mysqld3;
ERROR HY000: Unknown instance name
--------------------------------------------------------------------
-- 1.1.6.
--------------------------------------------------------------------
Killing the process...
Waiting...
Success: the process was restarted.
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld2 offline
--------------------------------------------------------------------
-- 1.1.7.
--------------------------------------------------------------------
START INSTANCE mysqld2;
Success: the process has been started.
Killing the process...
Waiting...
Success: the process was killed.
--------------------------------------------------------------------
-- 1.1.8.
--------------------------------------------------------------------
SHOW INSTANCE STATUS;
ERROR 42000: 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
--------------------------------------------------------------------
-- BUG#12813
--------------------------------------------------------------------
START INSTANCE mysqld1,mysqld2,mysqld3;
ERROR 42000: 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
STOP INSTANCE mysqld1,mysqld2,mysqld3;
ERROR 42000: 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
STOP INSTANCE mysqld2;
ERROR HY000: Cannot stop instance. Perhaps the instance is not started, or was started manually, so IM cannot find the pidfile.
End of 5.0 tests

View File

@ -1,151 +0,0 @@
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
SHOW INSTANCES;
instance_name state
mysqld1 XXXXX
mysqld2 offline
UNSET mysqld1.server_id;
ERROR HY000: The instance is active. Stop the instance first
SET mysqld1.server_id = 11;
ERROR HY000: The instance is active. Stop the instance first
CREATE INSTANCE mysqld3
datadir = '/',
server_id = 3,
socket = "$MYSQL_TMP_DIR/mysqld_3.sock";
START INSTANCE mysqld3;
UNSET mysqld3.server_id;
ERROR HY000: The instance is active. Stop the instance first
SET mysqld3.server_id = 11;
ERROR HY000: The instance is active. Stop the instance first
STOP INSTANCE mysqld3;
SHOW INSTANCE STATUS mysqld3;
instance_name state version_number version mysqld_compatible
mysqld3 offline VERSION_NUMBER VERSION no
UNSET mysqld2.server_id;
UNSET mysqld2.server_id;
SHOW INSTANCE OPTIONS mysqld2;
option_name value
instance_name option_value
socket option_value
pid-file option_value
port option_value
datadir option_value
log option_value
log-error option_value
log-slow-queries option_value
language option_value
character-sets-dir option_value
basedir option_value
shutdown-delay option_value
skip-stack-trace option_value
loose-skip-innodb option_value
loose-skip-ndbcluster option_value
nonguarded option_value
log-output option_value
SET mysqld2.server_id = 2;
SET mysqld2.server_id = 2;
SHOW INSTANCE OPTIONS mysqld2;
option_name value
instance_name option_value
socket option_value
pid-file option_value
port option_value
datadir option_value
log option_value
log-error option_value
log-slow-queries option_value
language option_value
character-sets-dir option_value
basedir option_value
shutdown-delay option_value
skip-stack-trace option_value
loose-skip-innodb option_value
loose-skip-ndbcluster option_value
nonguarded option_value
log-output option_value
server_id option_value
UNSET mysqld2.server_id = 11;
ERROR 42000: 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
SET mysqld2.aaa, mysqld3.bbb, mysqld2.ccc = 0010, mysqld3.ddd = 0020;
--------------------------------------------------------------------
aaa
--------------------------------------------------------------------
bbb
--------------------------------------------------------------------
ccc=0010
--------------------------------------------------------------------
ddd=0020
--------------------------------------------------------------------
UNSET mysqld2.aaa, mysqld3.bbb, mysqld2.ccc, mysqld3.ddd;
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
SET mysqld2.aaa, mysqld3.bbb, mysqld.ccc = 0010;
ERROR HY000: Unknown instance name
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
SET mysqld2.aaa, mysqld3.bbb, mysqld1.ccc = 0010;
ERROR HY000: The instance is active. Stop the instance first
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
UNSET mysqld2.server_id, mysqld3.server_id, mysqld.ccc;
ERROR HY000: Unknown instance name
--------------------------------------------------------------------
server_id = 1
server_id=2
server_id=3
--------------------------------------------------------------------
UNSET mysqld2.server_id, mysqld3.server_id, mysqld1.ccc;
ERROR HY000: The instance is active. Stop the instance first
--------------------------------------------------------------------
server_id = 1
server_id=2
server_id=3
--------------------------------------------------------------------
DROP INSTANCE mysqld3;
SET mysqld2.server_id=222;
SET mysqld2.server_id = 222;
SET mysqld2.server_id = 222 ;
SET mysqld2 . server_id = 222 ;
SET mysqld2 . server_id = 222 , mysqld2 . aaa , mysqld2 . bbb ;
--------------------------------------------------------------------
server_id = 1
server_id=222
--------------------------------------------------------------------
aaa
--------------------------------------------------------------------
bbb
--------------------------------------------------------------------
UNSET mysqld2 . aaa , mysqld2 . bbb ;
--------------------------------------------------------------------
server_id = 1
server_id=222
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
server_id = 1
server_id=222
--------------------------------------------------------------------
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
SHOW INSTANCES;
instance_name state
mysqld1 online
mysqld2 offline
FLUSH INSTANCES;
ERROR HY000: At least one instance is active. Stop all instances first
STOP INSTANCE mysqld1;
SHOW INSTANCES;
instance_name state
mysqld1 offline
mysqld2 offline
FLUSH INSTANCES;

View File

@ -1,96 +0,0 @@
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
SHOW INSTANCES;
instance_name state
mysqld1 XXXXX
mysqld2 offline
SHOW INSTANCE OPTIONS mysqld1;
option_name value
instance_name VALUE
socket VALUE
pid-file VALUE
port VALUE
datadir VALUE
log VALUE
log-error VALUE
log-slow-queries VALUE
language VALUE
character-sets-dir VALUE
basedir VALUE
server_id VALUE
shutdown-delay VALUE
skip-stack-trace VALUE
loose-skip-innodb VALUE
loose-skip-ndbcluster VALUE
log-output VALUE
SHOW INSTANCE OPTIONS mysqld2;
option_name value
instance_name VALUE
socket VALUE
pid-file VALUE
port VALUE
datadir VALUE
log VALUE
log-error VALUE
log-slow-queries VALUE
language VALUE
character-sets-dir VALUE
basedir VALUE
server_id VALUE
shutdown-delay VALUE
skip-stack-trace VALUE
loose-skip-innodb VALUE
loose-skip-ndbcluster VALUE
nonguarded VALUE
log-output VALUE
START INSTANCE mysqld2;
Success: the process has been started.
STOP INSTANCE mysqld2;
Success: the process has been stopped.
SHOW mysqld1 LOG FILES;
Logfile Path File size
ERROR LOG PATH FILE_SIZE
GENERAL LOG PATH FILE_SIZE
SLOW LOG PATH FILE_SIZE
SHOW mysqld2 LOG FILES;
Logfile Path File size
ERROR LOG PATH FILE_SIZE
GENERAL LOG PATH FILE_SIZE
SLOW LOG PATH FILE_SIZE
SHOW mysqld1 LOG ERROR 10;
Log
LOG_DATA
SHOW mysqld1 LOG SLOW 10;
Log
LOG_DATA
SHOW mysqld1 LOG GENERAL 10;
Log
LOG_DATA
SHOW mysqld1 LOG ERROR 10, 2;
Log
LOG_DATA
SHOW mysqld1 LOG SLOW 10, 2;
Log
LOG_DATA
SHOW mysqld1 LOG GENERAL 10, 2;
Log
LOG_DATA
SHOW mysqld2 LOG ERROR 10;
Log
LOG_DATA
SHOW mysqld2 LOG SLOW 10;
Log
LOG_DATA
SHOW mysqld2 LOG GENERAL 10;
Log
LOG_DATA
SHOW mysqld2 LOG ERROR 10, 2;
Log
LOG_DATA
SHOW mysqld2 LOG SLOW 10, 2;
Log
LOG_DATA
SHOW mysqld2 LOG GENERAL 10, 2;
Log
LOG_DATA

View File

@ -1,20 +0,0 @@
##############################################################################
#
# List the test cases that are to be disabled temporarily.
#
# Separate the test case name and the comment with ':'.
#
# <testcasename> : BUG#<xxxx> <date disabled> <disabler> <comment>
#
# Do not use any TAB characters for whitespace.
#
##############################################################################
im_options : Bug#20294 2006-07-24 stewart Instance manager test im_options fails randomly
im_daemon_life_cycle : Bug#20294 2007-05-14 alik Instance manager tests fail randomly
im_cmd_line : Bug#20294 2007-05-14 alik Instance manager tests fail randomly
im_utils : Bug#20294 2007-05-30 alik Instance manager tests fail randomly
im_instance_conf : Bug#20294 2007-05-30 alik Instance manager tests fail randomly
im_life_cycle : BUG#27851 Instance manager dies on ASSERT in ~Thread_registry() or from not being able to close a mysqld instance.
im_instance_conf : BUG#28743 Instance manager generates warnings in test suite
im_utils : BUG#28743 Instance manager generates warnings in test suite

View File

@ -1,26 +0,0 @@
# This file is intended to be used in each IM-test. It contains stamements,
# that ensure that starting conditions (environment) for the IM-test are as
# expected.
# Check the running instances.
--connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
--connection mysql1_con
SHOW VARIABLES LIKE 'server_id';
--source include/not_windows.inc
--connection default
# Let IM detect that mysqld1 is online. This delay should be longer than
# monitoring interval.
--sleep 2
# Check that IM understands that mysqld1 is online, while mysqld2 is
# offline.
--replace_result starting XXXXX online XXXXX
SHOW INSTANCES;

View File

@ -1,68 +0,0 @@
###########################################################################
#
# Tests for user-management command-line options.
#
###########################################################################
--source suite/im/t/im_check_env.inc
###########################################################################
# List users so we are sure about starting conditions.
--echo --> Listing users...
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --list-users 2>&1 >/dev/null
--echo
# Add a new user.
--echo ==> Adding user 'testuser'...
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --add-user --username=testuser --password=abc 2>&1 >/dev/null
--echo
--echo --> IM password file:
--exec cat $IM_PASSWORD_PATH
--echo --> EOF
--echo
--echo --> Printing out line for 'testuser'...
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --print-password-line --username=testuser --password=abc | tail -2 | head -1
--echo
--echo --> Listing users...
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --list-users 2>&1 >/dev/null
--echo
# Edit user's attributes.
--echo ==> Changing the password of 'testuser'...
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --edit-user --username=testuser --password=xyz 2>&1 >/dev/null
--echo
--echo --> IM password file:
--exec cat $IM_PASSWORD_PATH
--echo --> EOF
--echo
--echo --> Printing out line for 'testuser'...
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --print-password-line --username=testuser --password=xyz | tail -2 | head -1
--echo
--echo --> Listing users...
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --list-users 2>&1 >/dev/null
--echo
# Drop user.
--echo ==> Dropping user 'testuser'...
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --drop-user --username=testuser 2>&1 >/dev/null
--echo
--echo --> IM password file:
--exec cat $IM_PASSWORD_PATH
--echo --> EOF
--echo
--echo --> Listing users...
--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --list-users 2>&1 >/dev/null
--echo

View File

@ -1,3 +0,0 @@
--run-as-service
--log=$MYSQLTEST_VARDIR/log/im.log
--monitoring-interval=1

View File

@ -1,100 +0,0 @@
let $UTIL=$MYSQL_TEST_DIR/suite/im/t;
###########################################################################
#
# This file contains test for (1.2) test suite.
#
# Consult WL#2789 for more information.
#
###########################################################################
--exec $UTIL/log.sh im_daemon_life_cycle im_daemon_life_cycle.imtest started.
###########################################################################
--source suite/im/t/im_check_env.inc
# Turn on reconnect, not on by default anymore.
--enable_reconnect
###########################################################################
#
# The main daemon-life-cycle test case -- check that IM-angel will restart
# IM-main if it got killed:
# - kill IM-main and check that IM-angel will restart it;
# - wait for IM-main to start accepting connections before continue test
# case;
#
# NOTE: timeout is 55 seconds. Timeout should be more than shutdown-delay
# specified for managed MySQL instance. Now shutdown-delay is 10 seconds
# (set in mysql-test-run.pl). So, 55 seconds should be enough to make 5
# attempts.
#
###########################################################################
--exec $UTIL/log.sh im_daemon_life_cycle Main-test: starting...
--exec $UTIL/log.sh im_daemon_life_cycle Killing IM-main...
--exec $UTIL/kill_n_check.sh $IM_PATH_PID restarted 55 im_daemon_life_cycle
--exec $UTIL/log.sh im_daemon_life_cycle Waiting for IM-main to start accepting connections...
--exec $UTIL/wait_for_socket.sh $EXE_MYSQL $IM_PATH_SOCK $IM_USERNAME $IM_PASSWORD '' 55 im_daemon_life_cycle
--exec $UTIL/log.sh im_daemon_life_cycle Main-test: done.
###########################################################################
#
# BUG#12751: Instance Manager: client hangs
# - start nonguarded instance (mysqld2);
# - kill IM-main and get it restarted by IM-angel;
# - check that guarded instance (mysqld1) is accepting connections.
# - check that non-guarded instance (mysqld2) were not stopped.
#
###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- Test for BUG#12751
--echo --------------------------------------------------------------------
--exec $UTIL/log.sh im_daemon_life_cycle BUG12751: starting...
# 1. Start mysqld;
--exec $UTIL/log.sh im_daemon_life_cycle mysqld2: starting...
START INSTANCE mysqld2;
--exec $UTIL/log.sh im_daemon_life_cycle mysqld2: waiting to start...
--exec $UTIL/wait_for_process.sh $IM_MYSQLD2_PATH_PID 55 started im_daemon_life_cycle
--exec $UTIL/log.sh im_daemon_life_cycle mysqld2: started.
# 2. Restart IM-main;
--exec $UTIL/log.sh im_daemon_life_cycle Killing IM-main...
--exec $UTIL/kill_n_check.sh $IM_PATH_PID restarted 55 im_daemon_life_cycle
--exec $UTIL/log.sh im_daemon_life_cycle Waiting for IM-main to start accepting connections...
--exec $UTIL/wait_for_socket.sh $EXE_MYSQL $IM_PATH_SOCK $IM_USERNAME $IM_PASSWORD '' 55 im_daemon_life_cycle
# 3. Issue some statement -- connection should be re-established.
--exec $UTIL/log.sh im_daemon_life_cycle Checking that IM-main processing commands...
--replace_column 2 STATE 3 VERSION_NUMBER 4 VERSION
SHOW INSTANCE STATUS mysqld1;
# 4. Stop mysqld2, because it will not be stopped by IM, as it is nonguarded.
# So, if it we do not stop it, it will be stopped by mysql-test-run.pl with
# warning.
--exec $UTIL/log.sh im_daemon_life_cycle mysqld2: stopping...
STOP INSTANCE mysqld2;
--exec $UTIL/log.sh im_daemon_life_cycle mysqld2: waiting to stop...
--exec $UTIL/wait_for_process.sh $IM_MYSQLD2_PATH_PID 55 stopped im_daemon_life_cycle
--exec $UTIL/log.sh im_daemon_life_cycle mysqld2: stopped.
###########################################################################
--exec $UTIL/log.sh im_daemon_life_cycle BUG12751: done.

View File

@ -1 +0,0 @@
--monitoring-interval=1

View File

@ -1,244 +0,0 @@
###########################################################################
#
# This test suite checks the following statements:
# - CREATE INSTANCE <instance_name> [option1[=option1_value], ...];
# - DROP INSTANCE <instance_name>;
#
# For CREATE INSTANCE we check that:
# - CREATE INSTANCE succeeds for non-existing instance;
# - CREATE INSTANCE fails for existing instance;
# - CREATE INSTANCE can get additional options with and w/o values;
# - CREATE INSTANCE parses options and handles grammar errors correctly.
# Check that strings with spaces are handled correctly, unknown (for
# mysqld) options should also be handled;
# - CREATE INSTANCE updates both config file and internal configuration cache;
# - CREATE INSTANCE allows to create instances only with properly formed
# names (mysqld*);
#
# For DROP INSTANCE we check that:
# - DROP INSTANCE succeeds for existing instance;
# - DROP INSTANCE fails for non-existing instance;
# - DROP INSTANCE fails for active instance.
# - DROP INSTANCE updates both config file and internal configuration cache;
#
# NOTE: each CREATE INSTANCE statement must specify socket-file-name, otherwise
# this results of the test can be affected by another running test suite.
#
###########################################################################
--source suite/im/t/im_check_env.inc
###########################################################################
#
# Check starting conditions.
#
###########################################################################
# Check that the configuration file contains only instances that we expect.
--echo --------------------------------------------------------------------
--exec grep '^server_id[^a-zA-Z0-9_\-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
###########################################################################
#
# CREATE INSTANCE tests.
#
###########################################################################
# Check that CREATE INSTANCE succeeds for non-existing instance and also check
# that both config file and internal configuration cache have been updated.
CREATE INSTANCE mysqld3
server_id = 3,
socket = "$MYSQL_TMP_DIR/mysqld_3.sock";
SHOW INSTANCES;
--echo --------------------------------------------------------------------
--exec grep '^server_id[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
# Check that CREATE INSTANCE fails for existing instance. Let's all three
# existing instances (running one, stopped one and just created one). Just in
# case...
--error 3012 # ER_CREATE_EXISTING_INSTANCE
CREATE INSTANCE mysqld1;
--error 3012 # ER_CREATE_EXISTING_INSTANCE
CREATE INSTANCE mysqld2;
--error 3012 # ER_CREATE_EXISTING_INSTANCE
CREATE INSTANCE mysqld3;
# Check that CREATE INSTANCE can get additional options with and w/o values.
# Ensure that config file is updated properly.
# - without values;
--echo --------------------------------------------------------------------
--exec grep "^nonguarded\$" $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
CREATE INSTANCE mysqld4
nonguarded,
server_id = 4,
socket = "$MYSQL_TMP_DIR/mysqld_4.sock";
SHOW INSTANCES;
--echo --------------------------------------------------------------------
--exec grep "^nonguarded\$" $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
# - with value;
--echo --------------------------------------------------------------------
--exec grep '^test-A[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^test-B[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
CREATE INSTANCE mysqld5
test-A = 000,
test-B = test,
server_id = 5,
socket = "$MYSQL_TMP_DIR/mysqld_5.sock";
SHOW INSTANCES;
--echo --------------------------------------------------------------------
--exec grep '^test-A[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
--exec grep '^test-B[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
# Check that CREATE INSTANCE parses options and handles grammar errors
# correctly. Check that strings with spaces are handled correctly,
# unknown (for mysqld) options should also be handled.
# - check handling of extra spaces;
--echo --------------------------------------------------------------------
--exec grep '^test-C1[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^test-C2[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
CREATE INSTANCE mysqld6
test-C1 = 10 ,
test-C2 = 02 ,
server_id = 6,
socket = "$MYSQL_TMP_DIR/mysqld_6.sock";
SHOW INSTANCES;
--echo --------------------------------------------------------------------
--exec grep '^test-C1[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
--exec grep '^test-C2[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
# - check handling of grammar error;
--echo --------------------------------------------------------------------
--exec grep '^test-D[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^test-E[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--error ER_SYNTAX_ERROR
CREATE INSTANCE mysqld7 test-D = test-D-value ;
SHOW INSTANCES;
--error ER_SYNTAX_ERROR
CREATE INSTANCE mysqld8 test-E 0 ;
SHOW INSTANCES;
--error ER_SYNTAX_ERROR
CREATE INSTANCE mysqld8 test-F = ;
SHOW INSTANCES;
--echo --------------------------------------------------------------------
--exec grep '^test-D[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^test-E[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
# - check parsing of string option values
--echo --------------------------------------------------------------------
--exec grep '^test-1[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^test-2[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^test-3[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^test-4[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^test-5[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^test-6[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^test-7[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
CREATE INSTANCE mysqld9
test-1=" hello world ",
test-2=' ',
server_id = 9,
socket = "$MYSQL_TMP_DIR/mysqld_9.sock";
SHOW INSTANCES;
CREATE INSTANCE mysqld10
test-3='\b\babc\sdef',
server_id = 10,
socket = "$MYSQL_TMP_DIR/mysqld_10.sock";
# test-3='abc def'
SHOW INSTANCES;
CREATE INSTANCE mysqld11
test-4='abc\tdef',
test-5='abc\ndef',
server_id = 11,
socket = "$MYSQL_TMP_DIR/mysqld_11.sock";
SHOW INSTANCES;
CREATE INSTANCE mysqld12
test-6="abc\rdef",
test-7="abc\\def",
server_id = 12,
socket = "$MYSQL_TMP_DIR/mysqld_12.sock";
# test-6=abc
SHOW INSTANCES;
--error ER_SYNTAX_ERROR
CREATE INSTANCE mysqld13 test-bad=' \ ';
SHOW INSTANCES;
--echo --------------------------------------------------------------------
--exec grep '^test-1[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
--exec grep '^test-2[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
--exec grep '^test-3[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
--exec grep '^test-4[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
--exec grep '^test-5[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
--exec grep '^test-6[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
--exec grep '^test-7[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
--exec grep '^test-bad' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
# Check that CREATE INSTANCE allows to create instances only with properly
# formed names (mysqld*).
--error 3014 # ER_MALFORMED_INSTANCE_NAME
CREATE INSTANCE qqq1;

View File

@ -1 +0,0 @@
--monitoring-interval=1

View File

@ -1,205 +0,0 @@
let $UTIL=$MYSQL_TEST_DIR/suite/im/t;
###########################################################################
#
# This file contains test for (1.1) test suite.
#
# Consult WL#2789 for more information.
#
###########################################################################
--source suite/im/t/im_check_env.inc
###########################################################################
#
# 1.1.2. Check 'START INSTANCE' command:
# - start the second instance;
# - check that it is reported as online;
# - execute some SQL-statement on mysqld2 to ensure that it is really up and
# running;
#
###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.2.
--echo --------------------------------------------------------------------
START INSTANCE mysqld2;
# FIXME: START INSTANCE should be synchronous.
--exec $UTIL/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started im_life_cycle
# FIXME: Result of SHOW INSTANCES here is not deterministic unless START
# INSTANCE is synchronous. Even waiting for mysqld to start by looking at
# its pid file is not enough, because it is unknown when IM detects that
# mysqld has started.
# SHOW INSTANCES;
--connect (mysql_con,localhost,root,,mysql,$IM_MYSQLD2_PORT,$IM_MYSQLD2_SOCK)
--connection mysql_con
--replace_result $IM_MYSQLD2_PORT IM_MYSQLD2_PORT
SHOW VARIABLES LIKE 'port';
--connection default
--disconnect mysql_con
###########################################################################
#
# 1.1.3. Check 'STOP INSTANCE' command:
# - stop the second instance;
# - check that it is reported as offline;
# - TODO: try to execute some SQL-statement to ensure that it is really down;
#
###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.3.
--echo --------------------------------------------------------------------
STOP INSTANCE mysqld2;
# FIXME: STOP INSTANCE should be synchronous.
--exec $UTIL/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 stopped im_life_cycle
# FIXME: Result of SHOW INSTANCES here is not deterministic unless START
# INSTANCE is synchronous. Even waiting for mysqld to start by looking at
# its pid file is not enough, because it is unknown when IM detects that
# mysqld has started.
# SHOW INSTANCES;
###########################################################################
#
# 1.1.4. Check that Instance Manager reports correct errors for 'START
# INSTANCE' command:
# - if the client tries to start unregistered instance;
# - if the client tries to start already started instance;
# - if the client submits invalid arguments;
#
###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.4.
--echo --------------------------------------------------------------------
--error 3000 # ER_BAD_INSTANCE_NAME
START INSTANCE mysqld3;
--error 3002 # ER_INSTANCE_ALREADY_STARTED
START INSTANCE mysqld1;
###########################################################################
#
# 1.1.5. Check that Instance Manager reports correct errors for
# 'STOP INSTANCE' command:
# - if the client tries to start unregistered instance;
# - if the client tries to start already stopped instance;
# - if the client submits invalid arguments;
#
###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.5.
--echo --------------------------------------------------------------------
--error 3000 # ER_BAD_INSTANCE_NAME
STOP INSTANCE mysqld3;
# TODO: IM should be fixed.
# BUG#12673: Instance Manager allows to stop the instance many times
# --error 3002 # ER_INSTANCE_ALREADY_STARTED
# STOP INSTANCE mysqld2;
###########################################################################
#
# 1.1.6. Check that Instance Manager is able to restart guarded instances.
#
###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.6.
--echo --------------------------------------------------------------------
--exec $UTIL/kill_n_check.sh $IM_MYSQLD1_PATH_PID restarted 30 im_life_cycle
# Give some time to IM to detect that mysqld was restarted. It should be
# longer than monitoring interval.
--sleep 3
SHOW INSTANCES;
###########################################################################
#
# 1.1.7. Check that Instance Manager does not restart non-guarded instance.
#
###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.7.
--echo --------------------------------------------------------------------
START INSTANCE mysqld2;
# FIXME: START INSTANCE should be synchronous.
--exec $UTIL/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started im_life_cycle
# FIXME: Result of SHOW INSTANCES here is not deterministic unless START
# INSTANCE is synchronous. Even waiting for mysqld to start by looking at
# its pid file is not enough, because it is unknown when IM detects that
# mysqld has started.
# SHOW INSTANCES;
--exec $UTIL/kill_n_check.sh $IM_MYSQLD2_PATH_PID killed 10 im_life_cycle
# FIXME: Result of SHOW INSTANCES here is not deterministic unless START
# INSTANCE is synchronous. Even waiting for mysqld to start by looking at
# its pid file is not enough, because it is unknown when IM detects that
# mysqld has started.
# SHOW INSTANCES;
###########################################################################
#
# 1.1.8. Check that Instance Manager returns an error on
# incomplete SHOW INSTANCE STATUS command.
#
###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.8.
--echo --------------------------------------------------------------------
--error ER_SYNTAX_ERROR
SHOW INSTANCE STATUS;
#
# Tests for bug fixes
#
#
# Bug #12813 Instance Manager: START/STOP INSTANCE commands accept
# a list as argument.
#
--echo
--echo --------------------------------------------------------------------
--echo -- BUG#12813
--echo --------------------------------------------------------------------
--error ER_SYNTAX_ERROR
START INSTANCE mysqld1,mysqld2,mysqld3;
--error ER_SYNTAX_ERROR
STOP INSTANCE mysqld1,mysqld2,mysqld3;
#
# Bug #12673: Instance Manager: allows to stop the instance many times
#
--error 3001
STOP INSTANCE mysqld2;
--echo End of 5.0 tests

View File

@ -1 +0,0 @@
--monitoring-interval=1

View File

@ -1,248 +0,0 @@
###########################################################################
#
# This test suite checks the following statements:
# - SET <instance id>.<option name> = <option value>;
# - UNSET <instance id>.<option name> = <option value>;
# - FLUSH INSTANCES;
#
# For SET/UNSET we check that:
# - SET ignores spaces correctly;
# - UNSET does not allow option-value part (= <option value>);
# - SET/UNSET can be applied several times w/o error;
# - SET/UNSET is allowed only for stopped instances;
# - SET/UNSET updates both the configuration cache in IM and
# the configuration file;
#
# For FLUSH INSTANCES we check that:
# - FLUSH INSTANCES is allowed only when all instances are stopped;
#
# According to the IM implementation details, we should play at least with the
# following options:
# - server_id
# - port
# - nonguarded
#
# Let's test SET statement on the option 'server_id'. It's expected that
# originally the instances have the following server ids and states:
# - mysqld1: server_id: 1; running (online)
# - mysqld2: server_id: 2; stopped (offline)
#
# NOTE: each CREATE INSTANCE statement must specify socket-file-name, otherwise
# this results of the test can be affected by another running test suite.
#
###########################################################################
--source suite/im/t/im_check_env.inc
###########################################################################
#
# Check that SET/UNSET is allowed only for stopped instances.
#
###########################################################################
# - check that SET/UNSET is denied for running instances;
--error 3015 # ER_INSTANCE_IS_ACTIVE
UNSET mysqld1.server_id;
--error 3015 # ER_INSTANCE_IS_ACTIVE
SET mysqld1.server_id = 11;
# - check that SET/UNSET is denied for active instances:
# - create dummy misconfigured instance;
# - start it;
# - try to set/unset options;
CREATE INSTANCE mysqld3
datadir = '/',
server_id = 3,
socket = "$MYSQL_TMP_DIR/mysqld_3.sock";
START INSTANCE mysqld3;
# FIXME: START INSTANCE should be synchronous.
--sleep 3
# should be longer than monitoring interval and enough to start instance.
# NOTE: We can not analyze state of the instance here -- it can be Failed or
# Starting because Instance Manager is trying to start the misconfigured
# instance several times.
--error 3015 # ER_INSTANCE_IS_ACTIVE
UNSET mysqld3.server_id;
--error 3015 # ER_INSTANCE_IS_ACTIVE
SET mysqld3.server_id = 11;
STOP INSTANCE mysqld3;
# FIXME: STOP INSTANCE should be synchronous.
--sleep 3
# should be longer than monitoring interval and enough to stop instance.
--replace_column 3 VERSION_NUMBER 4 VERSION
SHOW INSTANCE STATUS mysqld3;
# - check that SET/UNSET succeed for stopped instances;
# - check that SET/UNSET can be applied multiple times;
UNSET mysqld2.server_id;
UNSET mysqld2.server_id;
--replace_column 2 option_value
SHOW INSTANCE OPTIONS mysqld2;
SET mysqld2.server_id = 2;
SET mysqld2.server_id = 2;
--replace_column 2 option_value
SHOW INSTANCE OPTIONS mysqld2;
# - check that UNSET does not allow option-value part (= <option value>);
--error ER_SYNTAX_ERROR
UNSET mysqld2.server_id = 11;
# - check that SET/UNSET working properly with multiple options;
SET mysqld2.aaa, mysqld3.bbb, mysqld2.ccc = 0010, mysqld3.ddd = 0020;
--echo --------------------------------------------------------------------
--exec grep "^aaa\$" $MYSQLTEST_VARDIR/im.cnf ;
--echo --------------------------------------------------------------------
--exec grep "^bbb\$" $MYSQLTEST_VARDIR/im.cnf ;
--echo --------------------------------------------------------------------
--exec grep '^ccc[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf ;
--echo --------------------------------------------------------------------
--exec grep '^ddd[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf ;
--echo --------------------------------------------------------------------
UNSET mysqld2.aaa, mysqld3.bbb, mysqld2.ccc, mysqld3.ddd;
--echo --------------------------------------------------------------------
--exec grep "^aaa\$" $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep "^bbb\$" $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^ccc[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^ddd[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
# - check that if some instance name is invalid or the active is active,
# whole SET-statement will not be executed;
--error 3000 # ER_BAD_INSTANCE_NAME
SET mysqld2.aaa, mysqld3.bbb, mysqld.ccc = 0010;
--echo --------------------------------------------------------------------
--exec grep "^aaa\$" $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep "^bbb\$" $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^ccc[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--error 3015 # ER_INSTANCE_IS_ACTIVE
SET mysqld2.aaa, mysqld3.bbb, mysqld1.ccc = 0010;
--echo --------------------------------------------------------------------
--exec grep "^aaa\$" $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep "^bbb\$" $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep '^ccc[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
# - check that if some instance name is invalid or the active is active,
# whole UNSET-statement will not be executed;
--error 3000 # ER_BAD_INSTANCE_NAME
UNSET mysqld2.server_id, mysqld3.server_id, mysqld.ccc;
--echo --------------------------------------------------------------------
--exec grep '^server_id[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
--error 3015 # ER_INSTANCE_IS_ACTIVE
UNSET mysqld2.server_id, mysqld3.server_id, mysqld1.ccc;
--echo --------------------------------------------------------------------
--exec grep '^server_id[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf;
--echo --------------------------------------------------------------------
DROP INSTANCE mysqld3;
# - check that spaces are handled correctly;
SET mysqld2.server_id=222;
SET mysqld2.server_id = 222;
SET mysqld2.server_id = 222 ;
SET mysqld2 . server_id = 222 ;
SET mysqld2 . server_id = 222 , mysqld2 . aaa , mysqld2 . bbb ;
--echo --------------------------------------------------------------------
--exec grep '^server_id[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf ;
--echo --------------------------------------------------------------------
--exec grep "^aaa\$" $MYSQLTEST_VARDIR/im.cnf ;
--echo --------------------------------------------------------------------
--exec grep "^bbb\$" $MYSQLTEST_VARDIR/im.cnf ;
--echo --------------------------------------------------------------------
UNSET mysqld2 . aaa , mysqld2 . bbb ;
--echo --------------------------------------------------------------------
--exec grep '^server_id[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf ;
--echo --------------------------------------------------------------------
--exec grep "^aaa\$" $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
--exec grep "^bbb\$" $MYSQLTEST_VARDIR/im.cnf || true;
--echo --------------------------------------------------------------------
###########################################################################
#
# Check that SET/UNSET updates both the configuration cache in IM and
# the configuration file.
#
###########################################################################
# - check that the configuration file has been updated (i.e. contains
# server_id=SERVER_ID for mysqld2);
--echo --------------------------------------------------------------------
--exec grep '^server_id[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf ;
--echo --------------------------------------------------------------------
# - (for mysqld1) check that the running instance has not been affected:
# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id''
# returns zero;
--connection mysql1_con
SHOW VARIABLES LIKE 'server_id';
--connection default
# - check that internal cache of Instance Manager has been affected;
# TODO: we should check only server_id option here.
# SHOW INSTANCE OPTIONS mysqld2;
###########################################################################
#
# Check that FLUSH INSTANCES is allowed only when all instances are stopped.
#
###########################################################################
SHOW INSTANCES;
--error 3016 # ER_THERE_IS_ACTIVE_INSTACE
FLUSH INSTANCES;
STOP INSTANCE mysqld1;
# FIXME: STOP INSTANCE should be synchronous.
--sleep 3
# should be longer than monitoring interval and enough to stop instance.
SHOW INSTANCES;
FLUSH INSTANCES;

View File

@ -1 +0,0 @@
--monitoring-interval=1

View File

@ -1,107 +0,0 @@
###########################################################################
#
# This file contains test for (2) test suite.
#
# Consult WL#2789 for more information.
#
###########################################################################
--source suite/im/t/im_check_env.inc
let $UTIL=$MYSQL_TEST_DIR/suite/im/t;
###########################################################################
#
# Check 'SHOW INSTANCE OPTIONS' command.
#
# Since configuration of an mysqld-instance contains directories, we should
# completely ignore the second column (values) in order to make the test
# case produce the same results on different installations;
# TODO: ignore values of only directory-specific options.
#
--replace_column 2 VALUE
SHOW INSTANCE OPTIONS mysqld1;
--replace_column 2 VALUE
SHOW INSTANCE OPTIONS mysqld2;
#
# Before checking log files, we should start the second instance (mysqld2) to
# give it a chance to create log files.
#
START INSTANCE mysqld2;
--exec $UTIL/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started im_utils
STOP INSTANCE mysqld2;
--exec $UTIL/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 stopped im_utils
#
# Check 'SHOW LOG FILES' command:
# - check that log files of both offline and online instances are accessible;
# - since placement of the log files is installation-specific, we should
# ignore it in comparisson;
# - also, we should ignore log file size, since it may depend on the version
# being tested;
#
--replace_column 2 PATH 3 FILE_SIZE
SHOW mysqld1 LOG FILES;
--replace_column 2 PATH 3 FILE_SIZE
SHOW mysqld2 LOG FILES;
#
# Check 'SHOW LOG' command:
# - check that all three kinds of logs are available for both offline and
# online instances;
# - we should ignore the value, because it is very specific and depends on
# many factors; we only check that Instance Manager is able to provide log
# files.
#
# mysqld1 (online) w/o the optional argument.
--replace_column 1 LOG_DATA
SHOW mysqld1 LOG ERROR 10;
--replace_column 1 LOG_DATA
SHOW mysqld1 LOG SLOW 10;
--replace_column 1 LOG_DATA
SHOW mysqld1 LOG GENERAL 10;
# mysqld1 (online) with the optional argument.
--replace_column 1 LOG_DATA
SHOW mysqld1 LOG ERROR 10, 2;
--replace_column 1 LOG_DATA
SHOW mysqld1 LOG SLOW 10, 2;
--replace_column 1 LOG_DATA
SHOW mysqld1 LOG GENERAL 10, 2;
# mysqld2 (offline) w/o the optional argument.
--replace_column 1 LOG_DATA
SHOW mysqld2 LOG ERROR 10;
--replace_column 1 LOG_DATA
SHOW mysqld2 LOG SLOW 10;
--replace_column 1 LOG_DATA
SHOW mysqld2 LOG GENERAL 10;
# mysqld2 (offline) with the optional argument.
--replace_column 1 LOG_DATA
SHOW mysqld2 LOG ERROR 10, 2;
--replace_column 1 LOG_DATA
SHOW mysqld2 LOG SLOW 10, 2;
--replace_column 1 LOG_DATA
SHOW mysqld2 LOG GENERAL 10, 2;

View File

@ -1,147 +0,0 @@
#!/bin/sh
###########################################################################
# NOTE: this script returns 0 (success) even in case of failure (except for
# usage-error). This is because this script is executed under
# mysql-test-run[.pl] and it's better to examine particular problem in log
# file, than just having said that the test case has failed.
###########################################################################
basename=`basename "$0"`
dirname=`dirname "$0"`
###########################################################################
. "$dirname/utils.sh"
###########################################################################
check_restart()
{
if [ ! -r "$pid_path" ]; then
log_debug "No '$pid_path' found."
user_msg='the process was killed'
return 1
fi
new_pid=`cat "$pid_path" 2>/dev/null`
err_code=$?
log_debug "err_code: $err_code; original_pid: $original_pid; new_pid: $new_pid."
if [ $err_code -eq 0 -a "$original_pid" = "$new_pid" ]; then
log_debug "The process was not restarted."
user_msg='the process was not restarted'
return 1
fi
log_debug "The process was restarted."
user_msg='the process was restarted'
return 0
}
###########################################################################
if [ $# -ne 4 ]; then
echo "Usage: $basename <pid file path> killed|restarted <timeout> <test id>"
exit 1
fi
pid_path="$1"
expected_result="$2"
total_timeout="$3"
test_id="$4"
log_file="$MYSQLTEST_VARDIR/log/$test_id.script.log"
log_debug "-- $basename: starting --"
log_debug "pid_path: '$pid_path'"
log_debug "expected_result: '$expected_result'"
log_debug "total_timeout: '$total_timeout'"
log_debug "test_id: '$test_id'"
log_debug "log_file: '$log_file'"
###########################################################################
if [ "$expected_result" != 'killed' -a \
"$expected_result" != 'restarted' ]; then
log_error "Invalid second argument ($expected_result): 'killed' or 'restarted' expected."
quit 0
fi
if [ -z "$pid_path" ]; then
log_error "Invalid PID path ($pid_path)."
quit 0
fi
if [ ! -r "$pid_path" ]; then
log_error "PID file ($pid_path) does not exist."
quit 0
fi
if [ -z "$total_timeout" ]; then
log_error "Timeout is not specified."
quit 0
fi
###########################################################################
original_pid=`cat "$pid_path"`
log_debug "original_pid: $original_pid."
log_info "Killing the process..."
kill -9 $original_pid
###########################################################################
log_info "Waiting..."
if [ "$expected_result" = "restarted" ]; then
# Wait for the process to restart.
cur_attempt=1
while true; do
log_debug "cur_attempt: $cur_attempt."
if check_restart; then
log_info "Success: $user_msg."
quit 0
fi
[ $cur_attempt -ge $total_timeout ] && break
log_debug "Sleeping for 1 second..."
sleep 1
cur_attempt=`expr $cur_attempt + 1`
done
log_error "$user_msg."
quit 0
else # $expected_result == killed
# Here we have to sleep for some long time to ensure that the process will
# not be restarted.
log_debug "Sleeping for $total_timeout seconds..."
sleep $total_timeout
new_pid=`cat "$pid_path" 2>/dev/null`
log_debug "new_pid: $new_pid."
if [ "$new_pid" -a "$new_pid" -ne "$original_pid" ]; then
log_error "The process was restarted."
else
log_info "Success: the process was killed."
fi
quit 0
fi

View File

@ -1,24 +0,0 @@
#!/bin/sh
###########################################################################
basename=`basename "$0"`
dirname=`dirname "$0"`
###########################################################################
. "$dirname/utils.sh"
###########################################################################
if [ $# -lt 2 ]; then
echo "Usage: $basename <test id> log message ..."
exit 1
fi
test_id="$1"
log_file="$MYSQLTEST_VARDIR/log/$test_id.script.log"
shift
log_debug "$*"

View File

@ -1,55 +0,0 @@
###########################################################################
#
# This file provides utility functions and is included by other scripts.
#
# The following global variables must be set before calling functions from this
# file:
# - basename -- base name of the calling script (main application);
# - log_file -- where to store log records;
#
###########################################################################
log()
{
[ -z "$log_file" ] && return;
log_level="$1"
log_msg="$2"
ts=`date`
echo "[$ts] [$basename] [$log_level] $log_msg" >> "$log_file";
}
###########################################################################
log_debug()
{
log 'DEBUG' "$1"
}
###########################################################################
log_info()
{
log 'INFO' "$1"
echo "$1"
}
###########################################################################
log_error()
{
log 'ERROR' "$1"
echo "Error: $1"
}
###########################################################################
quit()
{
exit_status="$1"
log_debug "-- $basename: finished (exit_status: $exit_status) --"
exit $exit_status
}

View File

@ -1,114 +0,0 @@
#!/bin/sh
###########################################################################
# NOTE: this script returns 0 (success) even in case of failure (except for
# usage-error). This is because this script is executed under
# mysql-test-run[.pl] and it's better to examine particular problem in log
# file, than just having said that the test case has failed.
###########################################################################
basename=`basename "$0"`
dirname=`dirname "$0"`
###########################################################################
. "$dirname/utils.sh"
###########################################################################
check_started()
{
if [ ! -r "$pid_path" ]; then
log_debug "No PID-file ($pid_path) found -- not started."
return 1
fi
new_pid=`cat "$pid_path" 2>/dev/null`
err_code=$?
log_debug "err_code: $err_code; new_pid: $new_pid."
if [ $? -ne 0 -o -z "$new_pid" ]; then
log_debug "The process was not started."
return 1
fi
log_debug "The process was started."
return 0
}
###########################################################################
check_stopped()
{
if [ -r "$pid_path" ]; then
log_debug "PID-file '$pid_path' exists -- not stopped."
return 1
fi
log_debug "No PID-file ($pid_path) found -- stopped."
return 0
}
###########################################################################
if [ $# -ne 4 ]; then
echo "Usage: $basename <pid file path> <total attempts> started|stopped <test id>"
exit 1
fi
pid_path="$1"
total_attempts="$2"
event="$3"
test_id="$4"
log_file="$MYSQLTEST_VARDIR/log/$test_id.script.log"
log_debug "-- $basename: starting --"
log_debug "pid_path: '$pid_path'"
log_debug "total_attempts: '$total_attempts'"
log_debug "event: '$event'"
log_debug "test_id: '$test_id'"
log_debug "log_file: '$log_file'"
###########################################################################
case "$event" in
started)
check_fn='check_started';
;;
stopped)
check_fn='check_stopped';
;;
*)
log_error "Invalid third argument ('started' or 'stopped' expected)."
quit 0
esac
###########################################################################
cur_attempt=1
while true; do
log_debug "cur_attempt: $cur_attempt."
if ( eval $check_fn ); then
log_info "Success: the process has been $event."
quit 0
fi
[ $cur_attempt -ge $total_attempts ] && break
log_debug "Sleeping for 1 second..."
sleep 1
cur_attempt=`expr $cur_attempt + 1`
done
log_error "The process has not been $event in $total_attempts secs."
quit 0

View File

@ -1,94 +0,0 @@
#!/bin/sh
###########################################################################
# NOTE: this script returns 0 (success) even in case of failure (except for
# usage-error). This is because this script is executed under
# mysql-test-run[.pl] and it's better to examine particular problem in log
# file, than just having said that the test case has failed.
###########################################################################
basename=`basename "$0"`
dirname=`dirname "$0"`
###########################################################################
. "$dirname/utils.sh"
###########################################################################
if [ $# -ne 7 ]; then
echo "Usage: wait_for_socket.sh <executable path> <socket path> <username> <password> <db> <timeout> <test id>"
exit 1
fi
client_exe="$1"
socket_path="$2"
username="$3"
password="$4"
db="$5"
total_timeout="$6"
test_id="$7"
log_file="$MYSQLTEST_VARDIR/log/$test_id.script.log"
log_debug "-- $basename: starting --"
log_debug "client_exe: '$client_exe'"
log_debug "socket_path: '$socket_path'"
log_debug "username: '$username'"
log_debug "password: '$password'"
log_debug "db: '$db'"
log_debug "total_timeout: '$total_timeout'"
log_debug "test_id: '$test_id'"
log_debug "log_file: '$log_file'"
###########################################################################
if [ -z "$client_exe" ]; then
log_error "Invalid path to client executable ($client_exe)."
quit 0;
fi
if [ ! -x "$client_exe" ]; then
log_error "Client by path '$client_exe' is not available."
quit 0;
fi
if [ -z "$socket_path" ]; then
log_error "Invalid socket patch ($socket_path)."
quit 0
fi
###########################################################################
client_args="--no-defaults --silent --socket=$socket_path --connect_timeout=1 "
[ -n "$username" ] && client_args="$client_args --user=$username "
[ -n "$password" ] && client_args="$client_args --password=$password "
[ -n "$db" ] && client_args="$client_args $db"
log_debug "client_args: '$client_args'"
###########################################################################
cur_attempt=1
while true; do
log_debug "cur_attempt: $cur_attempt."
if ( echo 'quit' | "$client_exe" $client_args >/dev/null 2>&1 ); then
log_info "Success: server is ready to accept connection on socket."
quit 0
fi
[ $cur_attempt -ge $total_timeout ] && break
sleep 1
cur_attempt=`expr $cur_attempt + 1`
done
log_error "Server does not accept connections after $total_timeout seconds."
quit 0

View File

@ -22,6 +22,5 @@ base_configs=" \
--enable-local-infile \
--with-extra-charsets=all \
--prefix=N:/mysql \
--without-mysqlmanager \
--without-man \
"

View File

@ -1,33 +0,0 @@
# 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)

View File

@ -1,20 +0,0 @@
# 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.%

View File

@ -1,38 +0,0 @@
# 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)

View File

@ -1,124 +0,0 @@
/* 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;
}

View File

@ -1,32 +0,0 @@
/* 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);
};

View File

@ -1,103 +0,0 @@
# Copyright (C) 2004 MySQL AB
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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.%

View File

@ -1,11 +0,0 @@
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.

View File

@ -1,231 +0,0 @@
/* 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);
}

View File

@ -1,56 +0,0 @@
/* 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);
};

View File

@ -1,407 +0,0 @@
/* 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__

View File

@ -1,34 +0,0 @@
/* 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__

View File

@ -1,110 +0,0 @@
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "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;
}

View File

@ -1,65 +0,0 @@
#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 */

View File

@ -1,30 +0,0 @@
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "manager.h"
#include "command.h"
Command::Command()
:guardian(Manager::get_guardian()),
instance_map(Manager::get_instance_map())
{}
Command::~Command()
{}

View File

@ -1,60 +0,0 @@
#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 */

File diff suppressed because it is too large Load Diff

View File

@ -1,393 +0,0 @@
#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 */

View File

@ -1,40 +0,0 @@
#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

View File

@ -1,496 +0,0 @@
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "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);
}

View File

@ -1,110 +0,0 @@
#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 */

View File

@ -1,944 +0,0 @@
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include <my_global.h>
#include <mysql.h>
#include <signal.h>
#ifndef __WIN__
#include <sys/wait.h>
#endif
#include "manager.h"
#include "guardian.h"
#include "instance.h"
#include "log.h"
#include "mysql_manager_error.h"
#include "portability.h"
#include "priv.h"
#include "thread_registry.h"
#include "instance_map.h"
/*************************************************************************
{{{ Platform-specific functions.
*************************************************************************/
#ifndef __WIN__
typedef pid_t My_process_info;
#else
typedef PROCESS_INFORMATION My_process_info;
#endif
/*
Wait for an instance
SYNOPSIS
wait_process()
pi Pointer to the process information structure
(platform-dependent).
RETURN
0 - Success
1 - Error
*/
#ifndef __WIN__
static int wait_process(My_process_info *pi)
{
/*
Here we wait for the child created. This process differs for systems
running LinuxThreads and POSIX Threads compliant systems. This is because
according to POSIX we could wait() for a child in any thread of the
process. While LinuxThreads require that wait() is called by the thread,
which created the child.
On the other hand we could not expect mysqld to return the pid, we
got in from fork(), to wait4() fucntion when running on LinuxThreads.
This is because MySQL shutdown thread is not the one, which was created
by our fork() call.
So basically we have two options: whether the wait() call returns only in
the creator thread, but we cannot use waitpid() since we have no idea
which pid we should wait for (in fact it should be the pid of shutdown
thread, but we don't know this one). Or we could use waitpid(), but
couldn't use wait(), because it could return in any wait() in the program.
*/
if (Manager::is_linux_threads())
wait(NULL); /* LinuxThreads were detected */
else
waitpid(*pi, NULL, 0);
return 0;
}
#else
static int wait_process(My_process_info *pi)
{
/* Wait until child process exits. */
WaitForSingleObject(pi->hProcess, INFINITE);
DWORD exitcode;
::GetExitCodeProcess(pi->hProcess, &exitcode);
/* Close process and thread handles. */
CloseHandle(pi->hProcess);
CloseHandle(pi->hThread);
/*
GetExitCodeProces returns zero on failure. We should revert this value
to report an error.
*/
return (!exitcode);
}
#endif
/*
Launch an instance
SYNOPSIS
start_process()
instance_options Pointer to the options of the instance to be
launched.
pi Pointer to the process information structure
(platform-dependent).
RETURN
FALSE - Success
TRUE - Cannot create an instance
*/
#ifndef __WIN__
static bool start_process(Instance_options *instance_options,
My_process_info *pi)
{
#ifndef __QNX__
*pi= fork();
#else
/*
On QNX one cannot use fork() in multithreaded environment and we
should use spawn() or one of it's siblings instead.
Here we use spawnv(), which is a combination of fork() and execv()
in one call. It returns the pid of newly created process (>0) or -1
*/
*pi= spawnv(P_NOWAIT, instance_options->mysqld_path, instance_options->argv);
#endif
switch (*pi) {
case 0: /* never happens on QNX */
execv(instance_options->mysqld_path.str, instance_options->argv);
/* exec never returns */
exit(1);
case -1:
log_error("Instance '%s': can not start mysqld: fork() failed.",
(const char *) instance_options->instance_name.str);
return TRUE;
}
return FALSE;
}
#else
static bool start_process(Instance_options *instance_options,
My_process_info *pi)
{
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb= sizeof(STARTUPINFO);
ZeroMemory(pi, sizeof(PROCESS_INFORMATION));
int cmdlen= 0;
for (int i= 0; instance_options->argv[i] != 0; i++)
cmdlen+= (uint) strlen(instance_options->argv[i]) + 3;
cmdlen++; /* make room for the null */
char *cmdline= new char[cmdlen];
if (cmdline == NULL)
return TRUE;
cmdline[0]= 0;
for (int i= 0; instance_options->argv[i] != 0; i++)
{
strcat(cmdline, "\"");
strcat(cmdline, instance_options->argv[i]);
strcat(cmdline, "\" ");
}
/* Start the child process */
BOOL result=
CreateProcess(NULL, /* Put it all in cmdline */
cmdline, /* Command line */
NULL, /* Process handle not inheritable */
NULL, /* Thread handle not inheritable */
FALSE, /* Set handle inheritance to FALSE */
0, /* No creation flags */
NULL, /* Use parent's environment block */
NULL, /* Use parent's starting directory */
&si, /* Pointer to STARTUPINFO structure */
pi); /* Pointer to PROCESS_INFORMATION structure */
delete cmdline;
return !result;
}
#endif
#ifdef __WIN__
BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode)
{
DWORD dwTID, dwCode, dwErr= 0;
HANDLE hProcessDup= INVALID_HANDLE_VALUE;
HANDLE hRT= NULL;
HINSTANCE hKernel= GetModuleHandle("Kernel32");
BOOL bSuccess= FALSE;
BOOL bDup= DuplicateHandle(GetCurrentProcess(),
hProcess, GetCurrentProcess(), &hProcessDup,
PROCESS_ALL_ACCESS, FALSE, 0);
// Detect the special case where the process is
// already dead...
if (GetExitCodeProcess((bDup) ? hProcessDup : hProcess, &dwCode) &&
(dwCode == STILL_ACTIVE))
{
FARPROC pfnExitProc;
pfnExitProc= GetProcAddress(hKernel, "ExitProcess");
hRT= CreateRemoteThread((bDup) ? hProcessDup : hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)pfnExitProc,
(PVOID)uExitCode, 0, &dwTID);
if (hRT == NULL)
dwErr= GetLastError();
}
else
dwErr= ERROR_PROCESS_ABORTED;
if (hRT)
{
// Must wait process to terminate to
// guarantee that it has exited...
WaitForSingleObject((bDup) ? hProcessDup : hProcess, INFINITE);
CloseHandle(hRT);
bSuccess= TRUE;
}
if (bDup)
CloseHandle(hProcessDup);
if (!bSuccess)
SetLastError(dwErr);
return bSuccess;
}
int kill(pid_t pid, int signum)
{
HANDLE processhandle= ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (signum == SIGTERM)
::SafeTerminateProcess(processhandle, 0);
else
::TerminateProcess(processhandle, -1);
return 0;
}
#endif
/*************************************************************************
}}}
*************************************************************************/
/*************************************************************************
{{{ Static constants.
*************************************************************************/
const LEX_STRING
Instance::DFLT_INSTANCE_NAME= { C_STRING_WITH_LEN("mysqld") };
/*************************************************************************
}}}
*************************************************************************/
/*************************************************************************
{{{ Instance Monitor thread.
*************************************************************************/
/**
Proxy thread is a simple way to avoid all pitfalls of the threads
implementation in the OS (e.g. LinuxThreads). With such a thread we
don't have to process SIGCHLD, which is a tricky business if we want
to do it in a portable way.
Instance Monitor Thread forks a child process, execs mysqld and waits for
the child to die.
Instance Monitor assumes that the monitoring instance will not be dropped.
This is guaranteed by having flag monitoring_thread_active and
Instance::is_active() operation.
*/
class Instance_monitor: public Thread
{
public:
Instance_monitor(Instance *instance_arg) :instance(instance_arg) {}
protected:
virtual void run();
void start_and_monitor_instance();
private:
Instance *instance;
};
void Instance_monitor::run()
{
start_and_monitor_instance();
delete this;
}
void Instance_monitor::start_and_monitor_instance()
{
Thread_registry *thread_registry= Manager::get_thread_registry();
Guardian *guardian= Manager::get_guardian();
My_process_info mysqld_process_info;
Thread_info monitor_thread_info;
log_info("Instance '%s': Monitor: started.",
(const char *) instance->get_name()->str);
/*
For guarded instance register the thread in Thread_registry to wait for
the thread to stop on shutdown (nonguarded instances are not stopped on
shutdown, so the thread will no finish).
*/
if (instance->is_guarded())
{
thread_registry->register_thread(&monitor_thread_info, FALSE);
}
/* Starting mysqld. */
log_info("Instance '%s': Monitor: starting mysqld...",
(const char *) instance->get_name()->str);
if (start_process(&instance->options, &mysqld_process_info))
{
instance->lock();
instance->monitoring_thread_active= FALSE;
instance->unlock();
return;
}
/* Waiting for mysqld to die. */
log_info("Instance '%s': Monitor: waiting for mysqld to stop...",
(const char *) instance->get_name()->str);
wait_process(&mysqld_process_info); /* Don't check for return value. */
log_info("Instance '%s': Monitor: mysqld stopped.",
(const char *) instance->get_name()->str);
/* Update instance status. */
instance->lock();
if (instance->is_guarded())
thread_registry->unregister_thread(&monitor_thread_info);
instance->crashed= TRUE;
instance->monitoring_thread_active= FALSE;
log_info("Instance '%s': Monitor: finished.",
(const char *) instance->get_name()->str);
instance->unlock();
/* Wake up guardian. */
guardian->ping();
}
/**************************************************************************
}}}
**************************************************************************/
/**************************************************************************
{{{ Static operations.
**************************************************************************/
/**
The operation is intended to check whether string is a well-formed
instance name or not.
SYNOPSIS
is_name_valid()
name string to check
RETURN
TRUE string is a valid instance name
FALSE string is not a valid instance name
TODO: Move to Instance_name class: Instance_name::is_valid().
*/
bool Instance::is_name_valid(const LEX_STRING *name)
{
const char *name_suffix= name->str + DFLT_INSTANCE_NAME.length;
if (strncmp(name->str, Instance::DFLT_INSTANCE_NAME.str,
Instance::DFLT_INSTANCE_NAME.length) != 0)
return FALSE;
return *name_suffix == 0 || my_isdigit(default_charset_info, *name_suffix);
}
/**
The operation is intended to check if the given instance name is
mysqld-compatible or not.
SYNOPSIS
is_mysqld_compatible_name()
name name to check
RETURN
TRUE name is mysqld-compatible
FALSE otherwise
TODO: Move to Instance_name class: Instance_name::is_mysqld_compatible().
*/
bool Instance::is_mysqld_compatible_name(const LEX_STRING *name)
{
return strcmp(name->str, DFLT_INSTANCE_NAME.str) == 0;
}
/**
Return client state name. Must not be used outside the class.
Use Instance::get_state_name() instead.
*/
const char * Instance::get_instance_state_name(enum_instance_state state)
{
switch (state) {
case STOPPED:
return "offline";
case NOT_STARTED:
return "not started";
case STARTING:
return "starting";
case STARTED:
return "online";
case JUST_CRASHED:
return "failed";
case CRASHED:
return "crashed";
case CRASHED_AND_ABANDONED:
return "abandoned";
case STOPPING:
return "stopping";
}
return NULL; /* just to ignore compiler warning. */
}
/**************************************************************************
}}}
**************************************************************************/
/**************************************************************************
{{{ Initialization & deinitialization.
**************************************************************************/
Instance::Instance()
:monitoring_thread_active(FALSE),
crashed(FALSE),
configured(FALSE),
/* mysqld_compatible is initialized in init() */
state(NOT_STARTED),
restart_counter(0),
crash_moment(0),
last_checked(0)
{
pthread_mutex_init(&LOCK_instance, 0);
}
Instance::~Instance()
{
log_info("Instance '%s': destroying...", (const char *) get_name()->str);
pthread_mutex_destroy(&LOCK_instance);
}
/**
Initialize instance options.
SYNOPSIS
init()
name_arg name of the instance
RETURN:
FALSE - ok
TRUE - error
*/
bool Instance::init(const LEX_STRING *name_arg)
{
mysqld_compatible= is_mysqld_compatible_name(name_arg);
return options.init(name_arg);
}
/**
@brief Complete instance options initialization.
@return Error status.
@retval FALSE ok
@retval TRUE error
*/
bool Instance::complete_initialization()
{
configured= ! options.complete_initialization();
return !configured;
}
/**************************************************************************
}}}
**************************************************************************/
/**************************************************************************
{{{ Instance: public interface implementation.
**************************************************************************/
/**
Determine if there is some activity with the instance.
SYNOPSIS
is_active()
DESCRIPTION
An instance is active if one of the following conditions is true:
- Instance-monitoring thread is running;
- Instance is guarded and its state is other than STOPPED;
- Corresponding mysqld-server accepts connections.
MT-NOTE: instance must be locked before calling the operation.
RETURN
TRUE - instance is active
FALSE - otherwise.
*/
bool Instance::is_active()
{
if (monitoring_thread_active)
return TRUE;
if (is_guarded() && get_state() != STOPPED)
return TRUE;
return is_mysqld_running();
}
/**
Determine if mysqld is accepting connections.
SYNOPSIS
is_mysqld_running()
DESCRIPTION
Try to connect to mysqld with fake login/password to check whether it is
accepting connections or not.
MT-NOTE: instance must be locked before calling the operation.
RETURN
TRUE - mysqld is alive and accept connections
FALSE - otherwise.
*/
bool Instance::is_mysqld_running()
{
MYSQL mysql;
uint port= options.get_mysqld_port(); /* 0 if not specified. */
const char *socket= NULL;
static const char *password= "check_connection";
static const char *username= "MySQL_Instance_Manager";
static const char *access_denied_message= "Access denied for user";
bool return_val;
if (options.mysqld_socket)
socket= options.mysqld_socket;
/* no port was specified => instance falled back to default value */
if (!port && !options.mysqld_socket)
port= SERVER_DEFAULT_PORT;
mysql_init(&mysql);
/* try to connect to a server with a fake username/password pair */
if (mysql_real_connect(&mysql, LOCAL_HOST, username,
password,
NullS, port,
socket, 0))
{
/*
We have successfully connected to the server using fake
username/password. Write a warning to the logfile.
*/
log_error("Instance '%s': was able to log into mysqld.",
(const char *) get_name()->str);
return_val= TRUE; /* server is alive */
}
else
return_val= test(!strncmp(access_denied_message, mysql_error(&mysql),
sizeof(access_denied_message) - 1));
mysql_close(&mysql);
return return_val;
}
/**
@brief Start mysqld.
Reset flags and start Instance Monitor thread, which will start mysqld.
@note Instance must be locked before calling the operation.
@return Error status code
@retval FALSE Ok
@retval TRUE Could not start instance
*/
bool Instance::start_mysqld()
{
Instance_monitor *instance_monitor;
if (!configured)
return TRUE;
/*
Prepare instance to start Instance Monitor thread.
NOTE: It's important to set these actions here in order to avoid
race conditions -- these actions must be done under acquired lock on
Instance.
*/
crashed= FALSE;
monitoring_thread_active= TRUE;
remove_pid();
/* Create and start the Instance Monitor thread. */
instance_monitor= new Instance_monitor(this);
if (instance_monitor == NULL || instance_monitor->start(Thread::DETACHED))
{
delete instance_monitor;
monitoring_thread_active= FALSE;
log_error("Instance '%s': can not create instance monitor thread.",
(const char *) get_name()->str);
return TRUE;
}
++restart_counter;
/* The Instance Monitor thread will delete itself when it's finished. */
return FALSE;
}
/**
Stop mysqld.
SYNOPSIS
stop_mysqld()
DESCRIPTION
Try to stop mysqld gracefully. Otherwise kill it with SIGKILL.
MT-NOTE: instance must be locked before calling the operation.
RETURN
FALSE - ok
TRUE - could not stop the instance
*/
bool Instance::stop_mysqld()
{
log_info("Instance '%s': stopping mysqld...",
(const char *) get_name()->str);
kill_mysqld(SIGTERM);
if (!wait_for_stop())
{
log_info("Instance '%s': mysqld stopped gracefully.",
(const char *) get_name()->str);
return FALSE;
}
log_info("Instance '%s': mysqld failed to stop gracefully within %d seconds.",
(const char *) get_name()->str,
(int) options.get_shutdown_delay());
log_info("Instance'%s': killing mysqld...",
(const char *) get_name()->str);
kill_mysqld(SIGKILL);
if (!wait_for_stop())
{
log_info("Instance '%s': mysqld has been killed.",
(const char *) get_name()->str);
return FALSE;
}
log_info("Instance '%s': can not kill mysqld within %d seconds.",
(const char *) get_name()->str,
(int) options.get_shutdown_delay());
return TRUE;
}
/**
Send signal to mysqld.
SYNOPSIS
kill_mysqld()
DESCRIPTION
Load pid from the pid file and send the given signal to that process.
If the signal is SIGKILL, remove the pid file after sending the signal.
MT-NOTE: instance must be locked before calling the operation.
TODO
This too low-level and OS-specific operation for public interface.
Also, it has some implicit behaviour for SIGKILL signal. Probably, we
should have the following public operations instead:
- start_mysqld() -- as is;
- stop_mysqld -- request mysqld to shutdown gracefully (send SIGTERM);
don't wait for complete shutdown;
- wait_for_stop() (or join_mysqld()) -- wait for mysqld to stop within
time interval;
- kill_mysqld() -- request to terminate mysqld; don't wait for
completion.
These operations should also be used in Guardian to manage instances.
*/
bool Instance::kill_mysqld(int signum)
{
pid_t mysqld_pid= options.load_pid();
if (mysqld_pid == 0)
{
log_info("Instance '%s': no pid file to send a signal (%d).",
(const char *) get_name()->str,
(int) signum);
return TRUE;
}
log_info("Instance '%s': sending %d to %d...",
(const char *) get_name()->str,
(int) signum,
(int) mysqld_pid);
if (kill(mysqld_pid, signum))
{
log_info("Instance '%s': kill() failed.",
(const char *) get_name()->str);
return TRUE;
}
/* Kill suceeded */
if (signum == SIGKILL) /* really killed instance with SIGKILL */
{
log_error("Instance '%s': killed.",
(const char *) options.instance_name.str);
/* After sucessful hard kill the pidfile need to be removed */
options.unlink_pidfile();
}
return FALSE;
}
/**
Lock instance.
*/
void Instance::lock()
{
pthread_mutex_lock(&LOCK_instance);
}
/**
Unlock instance.
*/
void Instance::unlock()
{
pthread_mutex_unlock(&LOCK_instance);
}
/**
Return instance state name.
SYNOPSIS
get_state_name()
DESCRIPTION
The operation returns user-friendly state name. The operation can be
used both for guarded and non-guarded instances.
MT-NOTE: instance must be locked before calling the operation.
TODO: Replace with the static get_state_name(state_code) function.
*/
const char *Instance::get_state_name()
{
if (!is_configured())
return "misconfigured";
if (is_guarded())
{
/* The instance is managed by Guardian: we can report precise state. */
return get_instance_state_name(get_state());
}
/* The instance is not managed by Guardian: we can report status only. */
return is_active() ? "online" : "offline";
}
/**
Reset statistics.
SYNOPSIS
reset_stat()
DESCRIPTION
The operation resets statistics used for guarding the instance.
MT-NOTE: instance must be locked before calling the operation.
TODO: Make private.
*/
void Instance::reset_stat()
{
restart_counter= 0;
crash_moment= 0;
last_checked= 0;
}
/**************************************************************************
}}}
**************************************************************************/
/**************************************************************************
{{{ Instance: implementation of private operations.
**************************************************************************/
/**
Remove pid file.
*/
void Instance::remove_pid()
{
int mysqld_pid= options.load_pid();
if (mysqld_pid == 0)
return;
if (options.unlink_pidfile())
{
log_error("Instance '%s': can not unlink pid file.",
(const char *) options.instance_name.str);
}
}
/**
Wait for mysqld to stop within shutdown interval.
*/
bool Instance::wait_for_stop()
{
int start_time= (int) time(NULL);
int finish_time= start_time + options.get_shutdown_delay();
log_info("Instance '%s': waiting for mysqld to stop "
"(timeout: %d seconds)...",
(const char *) get_name()->str,
(int) options.get_shutdown_delay());
while (true)
{
if (options.load_pid() == 0 && !is_mysqld_running())
return FALSE;
if (time(NULL) >= finish_time)
return TRUE;
/* Sleep for 0.3 sec and check again. */
my_sleep(300000);
}
}
/**************************************************************************
}}}
**************************************************************************/

View File

@ -1,273 +0,0 @@
#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 */

View File

@ -1,649 +0,0 @@
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "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;
}

View File

@ -1,102 +0,0 @@
#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 */

View File

@ -1,753 +0,0 @@
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif
#include "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;
}

View File

@ -1,126 +0,0 @@
#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 */

View File

@ -1,337 +0,0 @@
/* 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 */
}

View File

@ -1,61 +0,0 @@
/* 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

View File

@ -1,196 +0,0 @@
/* 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);
}

View File

@ -1,59 +0,0 @@
/* 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

View File

@ -1,526 +0,0 @@
/* 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;
}

View File

@ -1,71 +0,0 @@
/* 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

View File

@ -1,104 +0,0 @@
/* 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);
}

View File

@ -1,23 +0,0 @@
/* 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

View File

@ -1,376 +0,0 @@
/* 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
*/

View File

@ -1,74 +0,0 @@
/* 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

View File

@ -1,40 +0,0 @@
#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 */

View File

@ -1,232 +0,0 @@
/* 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

View File

@ -1,558 +0,0 @@
/* 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("\nThe 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

View File

@ -1,108 +0,0 @@
/* 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

View File

@ -1,509 +0,0 @@
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#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;
}

View File

@ -1,212 +0,0 @@
#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 */

View File

@ -1,407 +0,0 @@
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#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);
}

View File

@ -1,33 +0,0 @@
#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 */

View File

@ -1,65 +0,0 @@
/* 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 */

View File

@ -1,76 +0,0 @@
/* 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;
}

View File

@ -1,99 +0,0 @@
/* 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

View File

@ -1,217 +0,0 @@
/* 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;
}

View File

@ -1,47 +0,0 @@
/* 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 */

View File

@ -1,419 +0,0 @@
/* 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);
}

View File

@ -1,176 +0,0 @@
/* 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 */

View File

@ -1,421 +0,0 @@
/* 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;
}

View File

@ -1,167 +0,0 @@
#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

View File

@ -1,395 +0,0 @@
/* 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;
}

View File

@ -1,103 +0,0 @@
/* 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

View File

@ -7499,10 +7499,7 @@ SHOW_VAR status_vars[]= {
static void print_version(void)
{
set_server_version();
/*
Note: the instance manager keys off the string 'Ver' so it can find the
version from the output of 'mysqld --version', so don't change it!
*/
printf("%s Ver %s for %s on %s (%s)\n",my_progname,
server_version,SYSTEM_TYPE,MACHINE_TYPE, MYSQL_COMPILATION_COMMENT);
}

View File

@ -53,13 +53,14 @@ datadir=
# Negative numbers mean to wait indefinitely
service_startup_timeout=900
# Lock directory for RedHat / SuSE.
lockdir='/var/lock/subsys'
lock_file_path="$lockdir/mysql"
# The following variables are only set for letting mysql.server find things.
# Set some defaults
pid_file=
server_pid_file=
use_mysqld_safe=1
user=@MYSQLD_USER@
mysqld_pid_file_path=
if test -z "$basedir"
then
basedir=@prefix@
@ -101,11 +102,14 @@ else
}
fi
PATH=/sbin:/usr/sbin:/bin:/usr/bin:$basedir/bin
PATH="/sbin:/usr/sbin:/bin:/usr/bin:$basedir/bin"
export PATH
mode=$1 # start or stop
shift
[ $# -ge 1 ] && shift
other_args="$*" # uncommon, but needed when called from an RPM upgrade action
# Expected: "--skip-networking --skip-grant-tables"
# They are not checked here, intentionally, as it is the resposibility
@ -131,59 +135,50 @@ parse_server_arguments() {
--datadir=*) datadir=`echo "$arg" | sed -e 's/^[^=]*=//'`
datadir_set=1
;;
--user=*) user=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
--pid-file=*) server_pid_file=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
--pid-file=*) mysqld_pid_file_path=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
--service-startup-timeout=*) service_startup_timeout=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
--use-mysqld_safe) use_mysqld_safe=1;;
--use-manager) use_mysqld_safe=0;;
esac
done
}
parse_manager_arguments() {
for arg do
case "$arg" in
--pid-file=*) pid_file=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
--user=*) user=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
esac
done
}
wait_for_pid () {
verb="$1"
manager_pid="$2" # process ID of the program operating on the pid-file
verb="$1" # created | removed
pid="$2" # process ID of the program operating on the pid-file
pid_file_path="$3" # path to the PID file.
i=0
avoid_race_condition="by checking again"
while test $i -ne $service_startup_timeout ; do
case "$verb" in
'created')
# wait for a PID-file to pop into existence.
test -s $pid_file && i='' && break
test -s "$pid_file_path" && i='' && break
;;
'removed')
# wait for this PID-file to disappear
test ! -s $pid_file && i='' && break
test ! -s "$pid_file_path" && i='' && break
;;
*)
echo "wait_for_pid () usage: wait_for_pid created|removed manager_pid"
echo "wait_for_pid () usage: wait_for_pid created|removed pid pid_file_path"
exit 1
;;
esac
# if manager isn't running, then pid-file will never be updated
if test -n "$manager_pid"; then
if kill -0 "$manager_pid" 2>/dev/null; then
: # the manager still runs
# if server isn't running, then pid-file will never be updated
if test -n "$pid"; then
if kill -0 "$pid" 2>/dev/null; then
: # the server still runs
else
# The manager may have exited between the last pid-file check and now.
# The server may have exited between the last pid-file check and now.
if test -n "$avoid_race_condition"; then
avoid_race_condition=""
continue # Check again.
fi
# there's nothing that will affect the file.
log_failure_msg "Manager of pid-file quit without updating file."
log_failure_msg "The server quit without updating PID file ($pid_file_path)."
return 1 # not waiting any more.
fi
fi
@ -191,6 +186,7 @@ wait_for_pid () {
echo $echo_n ".$echo_c"
i=`expr $i + 1`
sleep 1
done
if test -z "$i" ; then
@ -259,28 +255,16 @@ fi
parse_server_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server`
# Look for the pidfile
parse_manager_arguments `$print_defaults $extra_args manager`
#
# Set pid file if not given
#
if test -z "$pid_file"
if test -z "$mysqld_pid_file_path"
then
pid_file=$datadir/mysqlmanager-`@HOSTNAME@`.pid
mysqld_pid_file_path=$datadir/`@HOSTNAME@`.pid
else
case "$pid_file" in
case "$mysqld_pid_file_path" in
/* ) ;;
* ) pid_file="$datadir/$pid_file" ;;
esac
fi
if test -z "$server_pid_file"
then
server_pid_file=$datadir/`@HOSTNAME@`.pid
else
case "$server_pid_file" in
/* ) ;;
* ) server_pid_file="$datadir/$server_pid_file" ;;
* ) mysqld_pid_file_path="$datadir/$mysqld_pid_file_path" ;;
esac
fi
@ -291,53 +275,23 @@ case "$mode" in
# Safeguard (relative paths, core dumps..)
cd $basedir
manager=$bindir/mysqlmanager
if test -x $libexecdir/mysqlmanager
then
manager=$libexecdir/mysqlmanager
elif test -x $sbindir/mysqlmanager
then
manager=$sbindir/mysqlmanager
fi
echo $echo_n "Starting MySQL"
if test -x $manager -a "$use_mysqld_safe" = "0"
then
if test -n "$other_args"
then
log_failure_msg "MySQL manager does not support options '$other_args'"
exit 1
fi
# Give extra arguments to mysqld with the my.cnf file. This script may
# be overwritten at next upgrade.
"$manager" \
--mysqld-safe-compatible \
--user="$user" \
--pid-file="$pid_file" >/dev/null 2>&1 &
wait_for_pid created $!; return_value=$?
# Make lock for RedHat / SuSE
if test -w /var/lock/subsys
then
touch /var/lock/subsys/mysqlmanager
fi
exit $return_value
elif test -x $bindir/mysqld_safe
if test -x $bindir/mysqld_safe
then
# Give extra arguments to mysqld with the my.cnf file. This script
# may be overwritten at next upgrade.
pid_file=$server_pid_file
$bindir/mysqld_safe --datadir=$datadir --pid-file=$server_pid_file $other_args >/dev/null 2>&1 &
wait_for_pid created $!; return_value=$?
$bindir/mysqld_safe --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &
wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$?
# Make lock for RedHat / SuSE
if test -w /var/lock/subsys
if test -w "$lockdir"
then
touch /var/lock/subsys/mysql
touch "$lock_file_path"
fi
exit $return_value
else
log_failure_msg "Couldn't find MySQL manager ($manager) or server ($bindir/mysqld_safe)"
log_failure_msg "Couldn't find MySQL server ($bindir/mysqld_safe)"
fi
;;
@ -345,39 +299,29 @@ case "$mode" in
# Stop daemon. We use a signal here to avoid having to know the
# root password.
# The RedHat / SuSE lock directory to remove
lock_dir=/var/lock/subsys/mysqlmanager
# If the manager pid_file doesn't exist, try the server's
if test ! -s "$pid_file"
if test -s "$mysqld_pid_file_path"
then
pid_file=$server_pid_file
lock_dir=/var/lock/subsys/mysql
fi
mysqld_pid=`cat "$mysqld_pid_file_path"`
if test -s "$pid_file"
then
mysqlmanager_pid=`cat $pid_file`
if (kill -0 $mysqlmanager_pid 2>/dev/null)
if (kill -9 $mysqld_pid 2>/dev/null)
then
echo $echo_n "Shutting down MySQL"
kill $mysqlmanager_pid
# mysqlmanager should remove the pid_file when it exits, so wait for it.
wait_for_pid removed "$mysqlmanager_pid"; return_value=$?
kill $mysqld_pid
# mysqld should remove the pid file when it exits, so wait for it.
wait_for_pid removed "$mysqld_pid" "$mysqld_pid_file_path"; return_value=$?
else
log_failure_msg "MySQL manager or server process #$mysqlmanager_pid is not running!"
rm $pid_file
log_failure_msg "MySQL server process #$mysqld_pid is not running!"
rm "$mysqld_pid_file_path"
fi
# delete lock for RedHat / SuSE
if test -f $lock_dir
# Delete lock for RedHat / SuSE
if test -f "$lock_file_path"
then
rm -f $lock_dir
rm -f "$lock_file_path"
fi
exit $return_value
else
log_failure_msg "MySQL manager or server PID file could not be found!"
log_failure_msg "MySQL server PID file could not be found!"
fi
;;
@ -393,10 +337,10 @@ case "$mode" in
;;
'reload'|'force-reload')
if test -s "$server_pid_file" ; then
read mysqld_pid < $server_pid_file
if test -s "$mysqld_pid_file_path" ; then
read mysqld_pid < "$mysqld_pid_file_path"
kill -HUP $mysqld_pid && log_success_msg "Reloading service MySQL"
touch $server_pid_file
touch "$mysqld_pid_file_path"
else
log_failure_msg "MySQL PID file could not be found!"
exit 1
@ -404,8 +348,8 @@ case "$mode" in
;;
'status')
# First, check to see if pid file exists
if test -s "$server_pid_file" ; then
read mysqld_pid < $server_pid_file
if test -s "$mysqld_pid_file_path" ; then
read mysqld_pid < "$mysqld_pid_file_path"
if kill -0 $mysqld_pid 2>/dev/null ; then
log_success_msg "MySQL running ($mysqld_pid)"
exit 0
@ -417,13 +361,8 @@ case "$mode" in
# Try to find appropriate mysqld process
mysqld_pid=`pidof $libexecdir/mysqld`
if test -z $mysqld_pid ; then
if test "$use_mysqld_safe" = "0" ; then
lockfile=/var/lock/subsys/mysqlmanager
else
lockfile=/var/lock/subsys/mysql
fi
if test -f $lockfile ; then
log_failure_msg "MySQL is not running, but lock exists"
if test -f "$lock_file_path" ; then
log_failure_msg "MySQL is not running, but lock file ($lock_file_path) exists"
exit 2
fi
log_failure_msg "MySQL is not running"
@ -436,7 +375,8 @@ case "$mode" in
;;
*)
# usage
echo "Usage: $0 {start|stop|restart|reload|force-reload|status} [ MySQL server options ]"
basename=`basename "$0"`
echo "Usage: $basename {start|stop|restart|reload|force-reload|status} [ MySQL server options ]"
exit 1
;;
esac

View File

@ -465,8 +465,8 @@ rm -fr $RBR%{_datadir}/sql-bench
# will appreciate that, as all services usually offer this.
ln -s %{_sysconfdir}/init.d/mysql $RPM_BUILD_ROOT%{_sbindir}/rcmysql
# Touch the place where the my.cnf config file might be located
# Just to make sure it's in the file list and marked as a config file
# Touch the place where the my.cnf config file might be located.
# Just to make sure it's in the file list and marked as a config file.
touch $RBR%{_sysconfdir}/my.cnf
%pre server
@ -867,6 +867,10 @@ fi
# itself - note that they must be ordered by date (important when
# merging BK trees)
%changelog
* Fri Oct 02 2009 Alexander Nozdrin <alexander.nozdrin@sun.com>
- "mysqlmanager" got removed from version 5.4, all references deleted.
* Fri Aug 28 2009 Joerg Bruehe <joerg.bruehe@sun.com>
- Merge up from 5.1 to 5.4: Remove handling for the InnoDB plugin.