summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/get_password.c4
-rw-r--r--include/m_string.h12
-rw-r--r--include/my_sys.h4
-rw-r--r--include/mysql_com.h8
-rw-r--r--libmysql/get_password.c4
-rwxr-xr-xmysql-test/mysql-test-run.pl3
-rw-r--r--mysql-test/r/im_cmd_line.result40
-rw-r--r--mysql-test/r/im_daemon_life_cycle.result2
-rw-r--r--mysql-test/r/im_instance_conf.result196
-rw-r--r--mysql-test/r/im_life_cycle.result72
-rw-r--r--mysql-test/r/im_options.result150
-rw-r--r--mysql-test/r/im_options_set.result20
-rw-r--r--mysql-test/r/im_options_unset.result15
-rw-r--r--mysql-test/r/im_utils.result6
-rw-r--r--mysql-test/t/im_cmd_line.imtest68
-rw-r--r--mysql-test/t/im_daemon_life_cycle-im.opt1
-rw-r--r--mysql-test/t/im_daemon_life_cycle.imtest3
-rw-r--r--mysql-test/t/im_instance_conf-im.opt1
-rw-r--r--mysql-test/t/im_instance_conf.imtest228
-rw-r--r--mysql-test/t/im_life_cycle-im.opt1
-rw-r--r--mysql-test/t/im_life_cycle.imtest95
-rw-r--r--mysql-test/t/im_options.imtest268
-rw-r--r--mysql-test/t/im_options_set.imtest142
-rw-r--r--mysql-test/t/im_options_unset.imtest150
-rw-r--r--mysql-test/t/im_utils-im.opt1
-rw-r--r--mysql-test/t/im_utils.imtest8
-rw-r--r--mysys/default.c10
-rw-r--r--mysys/default_modify.c58
-rw-r--r--server-tools/instance-manager/IMService.cpp25
-rw-r--r--server-tools/instance-manager/IMService.h20
-rw-r--r--server-tools/instance-manager/Makefile.am8
-rw-r--r--server-tools/instance-manager/WindowsService.cpp33
-rw-r--r--server-tools/instance-manager/WindowsService.h18
-rw-r--r--server-tools/instance-manager/command.h18
-rw-r--r--server-tools/instance-manager/commands.cc1855
-rw-r--r--server-tools/instance-manager/commands.h303
-rw-r--r--server-tools/instance-manager/exit_codes.h41
-rw-r--r--server-tools/instance-manager/guardian.cc133
-rw-r--r--server-tools/instance-manager/guardian.h34
-rw-r--r--server-tools/instance-manager/instance.cc122
-rw-r--r--server-tools/instance-manager/instance.h118
-rw-r--r--server-tools/instance-manager/instance_map.cc437
-rw-r--r--server-tools/instance-manager/instance_map.h62
-rw-r--r--server-tools/instance-manager/instance_options.cc309
-rw-r--r--server-tools/instance-manager/instance_options.h56
-rw-r--r--server-tools/instance-manager/listener.cc25
-rw-r--r--server-tools/instance-manager/listener.h4
-rw-r--r--server-tools/instance-manager/log.cc10
-rw-r--r--server-tools/instance-manager/manager.cc127
-rw-r--r--server-tools/instance-manager/manager.h4
-rw-r--r--server-tools/instance-manager/messages.cc24
-rw-r--r--server-tools/instance-manager/mysql_connection.cc46
-rw-r--r--server-tools/instance-manager/mysql_manager_error.h8
-rw-r--r--server-tools/instance-manager/mysqlmanager.cc77
-rw-r--r--server-tools/instance-manager/options.cc398
-rw-r--r--server-tools/instance-manager/options.h93
-rw-r--r--server-tools/instance-manager/parse.cc351
-rw-r--r--server-tools/instance-manager/parse.h159
-rw-r--r--server-tools/instance-manager/parse_output.cc8
-rw-r--r--server-tools/instance-manager/parse_output.h2
-rw-r--r--server-tools/instance-manager/portability.h14
-rw-r--r--server-tools/instance-manager/priv.cc8
-rw-r--r--server-tools/instance-manager/priv.h30
-rw-r--r--server-tools/instance-manager/protocol.cc8
-rw-r--r--server-tools/instance-manager/protocol.h5
-rw-r--r--server-tools/instance-manager/thread_registry.cc11
-rw-r--r--server-tools/instance-manager/user_management_commands.cc408
-rw-r--r--server-tools/instance-manager/user_management_commands.h167
-rw-r--r--server-tools/instance-manager/user_map.cc324
-rw-r--r--server-tools/instance-manager/user_map.h63
-rw-r--r--sql/sp.cc10
-rw-r--r--sql/sp_head.cc4
-rw-r--r--sql/spatial.cc5
-rw-r--r--sql/spatial.h2
-rw-r--r--sql/sql_string.h2
-rw-r--r--sql/sql_trigger.cc4
-rw-r--r--sql/structs.h16
-rw-r--r--support-files/mysql.server.sh5
78 files changed, 5807 insertions, 1777 deletions
diff --git a/client/get_password.c b/client/get_password.c
index 1b7b4e65a9f..b643b760718 100644
--- a/client/get_password.c
+++ b/client/get_password.c
@@ -64,7 +64,7 @@
/* were just going to fake it here and get input from
the keyboard */
-char *get_tty_password(char *opt_message)
+char *get_tty_password(const char *opt_message)
{
char to[80];
char *pos=to,*end=to+sizeof(to)-1;
@@ -150,7 +150,7 @@ static void get_password(char *to,uint length,int fd,bool echo)
#endif /* ! HAVE_GETPASS */
-char *get_tty_password(char *opt_message)
+char *get_tty_password(const char *opt_message)
{
#ifdef HAVE_GETPASS
char *passbuff;
diff --git a/include/m_string.h b/include/m_string.h
index e73f5c11487..2fddf4a83dd 100644
--- a/include/m_string.h
+++ b/include/m_string.h
@@ -246,4 +246,16 @@ extern int my_snprintf(char* to, size_t n, const char* fmt, ...);
#if defined(__cplusplus)
}
#endif
+
+/* LEX_STRING */
+
+typedef struct LEX_STRING
+{
+ char *str;
+ uint length;
+};
+
+#define STRING_WITH_LEN(X) (X), ((uint) (sizeof(X) - 1))
+#define C_STRING_WITH_SIZE(X) ((char *) (X)), ((uint) (sizeof(X) - 1))
+
#endif
diff --git a/include/my_sys.h b/include/my_sys.h
index 51883e8d6f9..50bd4702a29 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -76,6 +76,10 @@ extern int NEAR my_errno; /* Last error in mysys */
#define MY_CHECK_ERROR 1 /* Params to my_end; Check open-close */
#define MY_GIVE_INFO 2 /* Give time info about process*/
+#define MY_REMOVE_NONE 0 /* Params for modify_defaults_file */
+#define MY_REMOVE_OPTION 1
+#define MY_REMOVE_SECTION 2
+
#define ME_HIGHBYTE 8 /* Shift for colours */
#define ME_NOCUR 1 /* Don't use curses message */
#define ME_OLDWIN 2 /* Use old window */
diff --git a/include/mysql_com.h b/include/mysql_com.h
index 2af0fb86906..bff5fcc47d2 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -423,17 +423,11 @@ char *octet2hex(char *to, const char *str, unsigned int len);
/* end of password.c */
-char *get_tty_password(char *opt_message);
+char *get_tty_password(const char *opt_message);
const char *mysql_errno_to_sqlstate(unsigned int mysql_errno);
/* Some other useful functions */
-my_bool my_init(void);
-extern int modify_defaults_file(const char *file_location, const char *option,
- const char *option_value,
- const char *section_name, int remove_option);
-int load_defaults(const char *conf_file, const char **groups,
- int *argc, char ***argv);
my_bool my_thread_init(void);
void my_thread_end(void);
diff --git a/libmysql/get_password.c b/libmysql/get_password.c
index a48cb6d7a6e..4c251677a66 100644
--- a/libmysql/get_password.c
+++ b/libmysql/get_password.c
@@ -75,7 +75,7 @@
#define _cputs(A) putstring(A)
#endif
-char *get_tty_password(char *opt_message)
+char *get_tty_password(const char *opt_message)
{
char to[80];
char *pos=to,*end=to+sizeof(to)-1;
@@ -159,7 +159,7 @@ static void get_password(char *to,uint length,int fd,bool echo)
#endif /* ! HAVE_GETPASS */
-char *get_tty_password(char *opt_message)
+char *get_tty_password(const char *opt_message)
{
#ifdef HAVE_GETPASS
char *passbuff;
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
index 40b3954b533..5e2c408b0f5 100755
--- a/mysql-test/mysql-test-run.pl
+++ b/mysql-test/mysql-test-run.pl
@@ -1212,8 +1212,11 @@ sub environment_setup () {
$ENV{'NDBCLUSTER_PORT'}= $opt_ndbcluster_port;
$ENV{'NDBCLUSTER_PORT_SLAVE'}=$opt_ndbcluster_port_slave;
+ $ENV{'IM_EXE'}= $exe_im;
$ENV{'IM_PATH_PID'}= $instance_manager->{path_pid};
$ENV{'IM_PORT'}= $instance_manager->{port};
+ $ENV{'IM_DEFAULTS_PATH'}= $instance_manager->{defaults_file};
+ $ENV{'IM_PASSWORD_PATH'}= $instance_manager->{password_file};
$ENV{'IM_MYSQLD1_SOCK'}= $instance_manager->{instances}->[0]->{path_sock};
$ENV{'IM_MYSQLD1_PORT'}= $instance_manager->{instances}->[0]->{port};
diff --git a/mysql-test/r/im_cmd_line.result b/mysql-test/r/im_cmd_line.result
new file mode 100644
index 00000000000..5b289549a3f
--- /dev/null
+++ b/mysql-test/r/im_cmd_line.result
@@ -0,0 +1,40 @@
+--> 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
+
diff --git a/mysql-test/r/im_daemon_life_cycle.result b/mysql-test/r/im_daemon_life_cycle.result
index d0a76b450fe..29c9ea2047d 100644
--- a/mysql-test/r/im_daemon_life_cycle.result
+++ b/mysql-test/r/im_daemon_life_cycle.result
@@ -1,5 +1,5 @@
SHOW INSTANCES;
-instance_name status
+instance_name state
mysqld1 online
mysqld2 offline
Killing the process...
diff --git a/mysql-test/r/im_instance_conf.result b/mysql-test/r/im_instance_conf.result
new file mode 100644
index 00000000000..3ea1e2bd824
--- /dev/null
+++ b/mysql-test/r/im_instance_conf.result
@@ -0,0 +1,196 @@
+--------------------------------------------------------------------
+server_id =1
+server_id =2
+--------------------------------------------------------------------
+SHOW INSTANCES;
+instance_name state
+mysqld1 online
+mysqld2 offline
+
+---> connection: mysql1_con
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 1
+
+---> connection: default
+CREATE INSTANCE mysqld3;
+SHOW INSTANCES;
+instance_name state
+mysqld3 offline
+mysqld2 offline
+mysqld1 online
+--------------------------------------------------------------------
+server_id =1
+server_id =2
+--------------------------------------------------------------------
+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;
+SHOW INSTANCES;
+instance_name state
+mysqld3 offline
+mysqld4 offline
+mysqld1 online
+mysqld2 offline
+--------------------------------------------------------------------
+nonguarded
+nonguarded
+--------------------------------------------------------------------
+--------------------------------------------------------------------
+--------------------------------------------------------------------
+--------------------------------------------------------------------
+CREATE INSTANCE mysqld5 test-A = 000, test-B = test;
+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 ;
+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=' ';
+SHOW INSTANCES;
+instance_name state
+mysqld1 online
+mysqld2 offline
+mysqld5 offline
+mysqld6 offline
+mysqld3 offline
+mysqld4 offline
+mysqld9 offline
+CREATE INSTANCE mysqld9a test-3='\b\babc\sdef';
+SHOW INSTANCES;
+instance_name state
+mysqld1 online
+mysqld9a offline
+mysqld5 offline
+mysqld6 offline
+mysqld3 offline
+mysqld4 offline
+mysqld9 offline
+mysqld2 offline
+CREATE INSTANCE mysqld9b test-4='abc\tdef', test-5='abc\ndef';
+SHOW INSTANCES;
+instance_name state
+mysqld9b offline
+mysqld9a offline
+mysqld5 offline
+mysqld6 offline
+mysqld3 offline
+mysqld4 offline
+mysqld9 offline
+mysqld2 offline
+mysqld1 online
+CREATE INSTANCE mysqld9c test-6="abc\rdef", test-7="abc\\def";
+SHOW INSTANCES;
+instance_name state
+mysqld9b offline
+mysqld6 offline
+mysqld5 offline
+mysqld9c offline
+mysqld3 offline
+mysqld4 offline
+mysqld9 offline
+mysqld2 offline
+mysqld1 online
+mysqld9a offline
+CREATE INSTANCE mysqld10 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
+mysqld9b offline
+mysqld6 offline
+mysqld5 offline
+mysqld9c offline
+mysqld3 offline
+mysqld4 offline
+mysqld9 offline
+mysqld2 offline
+mysqld1 online
+mysqld9a 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.
diff --git a/mysql-test/r/im_life_cycle.result b/mysql-test/r/im_life_cycle.result
index e208ccb9f00..876fbb38eee 100644
--- a/mysql-test/r/im_life_cycle.result
+++ b/mysql-test/r/im_life_cycle.result
@@ -1,69 +1,93 @@
+
+--------------------------------------------------------------------
+-- 1.1.1.
+--------------------------------------------------------------------
SHOW INSTANCES;
-instance_name status
+instance_name state
mysqld1 online
mysqld2 offline
-SHOW INSTANCE STATUS mysqld1;
-instance_name status version_number version
-mysqld1 online VERSION_NUMBER VERSION
-SHOW INSTANCE STATUS mysqld2;
-instance_name status version_number version
-mysqld2 offline VERSION_NUMBER VERSION
+
+--------------------------------------------------------------------
+-- 1.1.2.
+--------------------------------------------------------------------
START INSTANCE mysqld2;
SHOW INSTANCES;
-instance_name status
+instance_name state
mysqld1 online
mysqld2 online
-SHOW INSTANCE STATUS mysqld1;
-instance_name status version_number version
-mysqld1 online VERSION_NUMBER VERSION
-SHOW INSTANCE STATUS mysqld2;
-instance_name status version_number version
-mysqld2 online VERSION_NUMBER VERSION
SHOW VARIABLES LIKE 'port';
Variable_name Value
-port IM_MYSQLD1_PORT
+port IM_MYSQLD2_PORT
+
+--------------------------------------------------------------------
+-- 1.1.3.
+--------------------------------------------------------------------
STOP INSTANCE mysqld2;
SHOW INSTANCES;
-instance_name status
+instance_name state
mysqld1 online
mysqld2 offline
SHOW INSTANCE STATUS mysqld1;
-instance_name status version_number version
-mysqld1 online VERSION_NUMBER VERSION
+instance_name state version_number version mysqld_compatible
+mysqld1 online VERSION_NUMBER VERSION no
SHOW INSTANCE STATUS mysqld2;
-instance_name status version_number version
-mysqld2 offline VERSION_NUMBER VERSION
+instance_name state version_number version mysqld_compatible
+mysqld2 offline VERSION_NUMBER VERSION no
+
+--------------------------------------------------------------------
+-- 1.1.4.
+--------------------------------------------------------------------
START INSTANCE mysqld3;
ERROR HY000: Bad instance name. Check that the instance with such a name exists
START INSTANCE mysqld1;
ERROR HY000: The instance is already started
+
+--------------------------------------------------------------------
+-- 1.1.5.
+--------------------------------------------------------------------
STOP INSTANCE mysqld3;
ERROR HY000: Bad instance name. Check that the instance with such a name exists
+
+--------------------------------------------------------------------
+-- 1.1.6.
+--------------------------------------------------------------------
SHOW INSTANCES;
-instance_name status
+instance_name state
mysqld1 online
mysqld2 offline
Killing the process...
Sleeping...
Success: the process was restarted.
+
+--------------------------------------------------------------------
+-- 1.1.7.
+--------------------------------------------------------------------
SHOW INSTANCES;
-instance_name status
+instance_name state
mysqld1 online
mysqld2 offline
START INSTANCE mysqld2;
SHOW INSTANCES;
-instance_name status
+instance_name state
mysqld1 online
mysqld2 online
Killing the process...
Sleeping...
Success: the process was killed.
SHOW INSTANCES;
-instance_name status
+instance_name state
mysqld1 online
mysqld2 offline
+
+--------------------------------------------------------------------
+-- 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;
diff --git a/mysql-test/r/im_options.result b/mysql-test/r/im_options.result
new file mode 100644
index 00000000000..ed5b2ee5091
--- /dev/null
+++ b/mysql-test/r/im_options.result
@@ -0,0 +1,150 @@
+--------------------------------------------------------------------
+server_id =1
+server_id =2
+--------------------------------------------------------------------
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 1
+SHOW INSTANCES;
+instance_name state
+mysqld1 starting
+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 = '/';
+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
+skip-stack-trace option_value
+skip-innodb option_value
+skip-bdb option_value
+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
+skip-stack-trace option_value
+skip-innodb option_value
+skip-bdb option_value
+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: Bad instance name. Check that the instance with such a name exists
+--------------------------------------------------------------------
+--------------------------------------------------------------------
+--------------------------------------------------------------------
+--------------------------------------------------------------------
+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: Bad instance name. Check that the instance with such a name exists
+--------------------------------------------------------------------
+server_id =1
+server_id=2
+--------------------------------------------------------------------
+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
+--------------------------------------------------------------------
+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;
diff --git a/mysql-test/r/im_options_set.result b/mysql-test/r/im_options_set.result
deleted file mode 100644
index 0d2fa699fc7..00000000000
--- a/mysql-test/r/im_options_set.result
+++ /dev/null
@@ -1,20 +0,0 @@
-server_id =1
-server_id =2
-SHOW VARIABLES LIKE 'server_id';
-Variable_name Value
-server_id 1
-SET mysqld1.server_id = 11;
-server_id =11
-server_id =2
-SHOW VARIABLES LIKE 'server_id';
-Variable_name Value
-server_id 1
-SET mysqld2.server_id = 12;
-server_id =11
-server_id =12
-FLUSH INSTANCES;
-server_id =11
-server_id =12
-SHOW VARIABLES LIKE 'server_id';
-Variable_name Value
-server_id 1
diff --git a/mysql-test/r/im_options_unset.result b/mysql-test/r/im_options_unset.result
deleted file mode 100644
index 834152c35d2..00000000000
--- a/mysql-test/r/im_options_unset.result
+++ /dev/null
@@ -1,15 +0,0 @@
-server_id =1
-server_id =2
-SHOW VARIABLES LIKE 'server_id';
-Variable_name Value
-server_id 1
-UNSET mysqld1.server_id;
-server_id =2
-SHOW VARIABLES LIKE 'server_id';
-Variable_name Value
-server_id 1
-UNSET mysqld2.server_id;
-FLUSH INSTANCES;
-SHOW VARIABLES LIKE 'server_id';
-Variable_name Value
-server_id 1
diff --git a/mysql-test/r/im_utils.result b/mysql-test/r/im_utils.result
index 504b2efe4af..ae8e03bf8ea 100644
--- a/mysql-test/r/im_utils.result
+++ b/mysql-test/r/im_utils.result
@@ -1,11 +1,10 @@
SHOW INSTANCES;
-instance_name status
+instance_name state
mysqld1 online
mysqld2 offline
SHOW INSTANCE OPTIONS mysqld1;
option_name value
instance_name VALUE
-mysqld-path VALUE
socket VALUE
pid-file VALUE
port VALUE
@@ -25,8 +24,6 @@ log-output VALUE
SHOW INSTANCE OPTIONS mysqld2;
option_name value
instance_name VALUE
-mysqld-path VALUE
-nonguarded VALUE
socket VALUE
pid-file VALUE
port VALUE
@@ -42,6 +39,7 @@ skip-stack-trace VALUE
skip-innodb VALUE
skip-bdb VALUE
skip-ndbcluster VALUE
+nonguarded VALUE
log-output VALUE
START INSTANCE mysqld2;
STOP INSTANCE mysqld2;
diff --git a/mysql-test/t/im_cmd_line.imtest b/mysql-test/t/im_cmd_line.imtest
new file mode 100644
index 00000000000..00e8351535e
--- /dev/null
+++ b/mysql-test/t/im_cmd_line.imtest
@@ -0,0 +1,68 @@
+###########################################################################
+#
+# Tests for user-management command-line options.
+#
+###########################################################################
+
+--source include/im_check_os.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" --passwd --username=testuser --password=abc | tail -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" --passwd --username=testuser --password=xyz | tail -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
diff --git a/mysql-test/t/im_daemon_life_cycle-im.opt b/mysql-test/t/im_daemon_life_cycle-im.opt
index 21c01191e4c..3a45c7a41f7 100644
--- a/mysql-test/t/im_daemon_life_cycle-im.opt
+++ b/mysql-test/t/im_daemon_life_cycle-im.opt
@@ -1,2 +1,3 @@
--run-as-service
--log=$MYSQLTEST_VARDIR/log/im.log
+--monitoring-interval=1
diff --git a/mysql-test/t/im_daemon_life_cycle.imtest b/mysql-test/t/im_daemon_life_cycle.imtest
index 87388d7c1e6..d173ce2a6e2 100644
--- a/mysql-test/t/im_daemon_life_cycle.imtest
+++ b/mysql-test/t/im_daemon_life_cycle.imtest
@@ -10,6 +10,9 @@
###########################################################################
+--sleep 3
+# should be longer than monitoring interval and enough to start instance.
+
SHOW INSTANCES;
--exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted
diff --git a/mysql-test/t/im_instance_conf-im.opt b/mysql-test/t/im_instance_conf-im.opt
new file mode 100644
index 00000000000..34b74ce0c95
--- /dev/null
+++ b/mysql-test/t/im_instance_conf-im.opt
@@ -0,0 +1 @@
+--monitoring-interval=1
diff --git a/mysql-test/t/im_instance_conf.imtest b/mysql-test/t/im_instance_conf.imtest
new file mode 100644
index 00000000000..17703fdd303
--- /dev/null
+++ b/mysql-test/t/im_instance_conf.imtest
@@ -0,0 +1,228 @@
+###########################################################################
+#
+# 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;
+#
+###########################################################################
+
+--source include/im_check_os.inc
+
+###########################################################################
+#
+# Check starting conditions.
+#
+###########################################################################
+
+# Check that the configuration file contains only instances that we expect.
+
+--echo --------------------------------------------------------------------
+--exec grep server_id $MYSQLTEST_VARDIR/im.cnf;
+--echo --------------------------------------------------------------------
+
+# Check that mysqld1 is reported as running.
+
+--sleep 3
+# should be longer than monitoring interval and enough to start instance.
+
+SHOW INSTANCES;
+
+# Check that the expected mysqld instance is actually run (check that we can
+# connect and execute something).
+
+--echo
+--echo ---> connection: mysql1_con
+--connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
+--connection mysql1_con
+
+SHOW VARIABLES LIKE 'server_id';
+
+--disconnect mysql1_con
+
+--echo
+--echo ---> connection: default
+--connection default
+
+###########################################################################
+#
+# 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;
+
+SHOW INSTANCES;
+
+--echo --------------------------------------------------------------------
+--exec grep server_id $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;
+
+SHOW INSTANCES;
+
+--echo --------------------------------------------------------------------
+--exec grep nonguarded $MYSQLTEST_VARDIR/im.cnf;
+--echo --------------------------------------------------------------------
+
+# - with value;
+
+--echo --------------------------------------------------------------------
+--exec grep test-A $MYSQLTEST_VARDIR/im.cnf || true;
+--echo --------------------------------------------------------------------
+--exec grep test-B $MYSQLTEST_VARDIR/im.cnf || true;
+--echo --------------------------------------------------------------------
+
+CREATE INSTANCE mysqld5 test-A = 000, test-B = test;
+
+SHOW INSTANCES;
+
+--echo --------------------------------------------------------------------
+--exec grep test-A $MYSQLTEST_VARDIR/im.cnf;
+--echo --------------------------------------------------------------------
+--exec grep test-B $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-C $MYSQLTEST_VARDIR/im.cnf || true;
+--echo --------------------------------------------------------------------
+
+CREATE INSTANCE mysqld6 test-C1 = 10 , test-C2 = 02 ;
+
+SHOW INSTANCES;
+
+--echo --------------------------------------------------------------------
+--exec grep test-C1 $MYSQLTEST_VARDIR/im.cnf;
+--echo --------------------------------------------------------------------
+--exec grep test-C2 $MYSQLTEST_VARDIR/im.cnf;
+--echo --------------------------------------------------------------------
+
+# - check handling of grammar error;
+
+--echo --------------------------------------------------------------------
+--exec grep test-D $MYSQLTEST_VARDIR/im.cnf || true;
+--echo --------------------------------------------------------------------
+--exec grep test-E $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 $MYSQLTEST_VARDIR/im.cnf || true;
+--echo --------------------------------------------------------------------
+--exec grep test-E $MYSQLTEST_VARDIR/im.cnf || true;
+--echo --------------------------------------------------------------------
+
+# - check parsing of string option values
+
+--echo --------------------------------------------------------------------
+--exec grep test-1 $MYSQLTEST_VARDIR/im.cnf || true;
+--echo --------------------------------------------------------------------
+--exec grep test-2 $MYSQLTEST_VARDIR/im.cnf || true;
+--echo --------------------------------------------------------------------
+--exec grep test-3 $MYSQLTEST_VARDIR/im.cnf || true;
+--echo --------------------------------------------------------------------
+--exec grep test-4 $MYSQLTEST_VARDIR/im.cnf || true;
+--echo --------------------------------------------------------------------
+
+CREATE INSTANCE mysqld9 test-1=" hello world ", test-2=' ';
+SHOW INSTANCES;
+
+CREATE INSTANCE mysqld9a test-3='\b\babc\sdef';
+# test-3='abc def'
+SHOW INSTANCES;
+
+CREATE INSTANCE mysqld9b test-4='abc\tdef', test-5='abc\ndef';
+SHOW INSTANCES;
+
+CREATE INSTANCE mysqld9c test-6="abc\rdef", test-7="abc\\def";
+# test-6=abc
+SHOW INSTANCES;
+
+--error ER_SYNTAX_ERROR
+CREATE INSTANCE mysqld10 test-bad=' \ ';
+SHOW INSTANCES;
+
+--echo --------------------------------------------------------------------
+--exec grep test-1 $MYSQLTEST_VARDIR/im.cnf;
+--echo --------------------------------------------------------------------
+--exec grep test-2 $MYSQLTEST_VARDIR/im.cnf;
+--echo --------------------------------------------------------------------
+--exec grep test-3 $MYSQLTEST_VARDIR/im.cnf;
+--echo --------------------------------------------------------------------
+--exec grep test-4 $MYSQLTEST_VARDIR/im.cnf;
+--echo --------------------------------------------------------------------
+--exec grep test-5 $MYSQLTEST_VARDIR/im.cnf;
+--echo --------------------------------------------------------------------
+--exec grep test-6 $MYSQLTEST_VARDIR/im.cnf;
+--echo --------------------------------------------------------------------
+--exec grep test-7 $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;
+
diff --git a/mysql-test/t/im_life_cycle-im.opt b/mysql-test/t/im_life_cycle-im.opt
new file mode 100644
index 00000000000..34b74ce0c95
--- /dev/null
+++ b/mysql-test/t/im_life_cycle-im.opt
@@ -0,0 +1 @@
+--monitoring-interval=1
diff --git a/mysql-test/t/im_life_cycle.imtest b/mysql-test/t/im_life_cycle.imtest
index 246843a022b..d71cdc86624 100644
--- a/mysql-test/t/im_life_cycle.imtest
+++ b/mysql-test/t/im_life_cycle.imtest
@@ -17,11 +17,15 @@
#
###########################################################################
+--echo
+--echo --------------------------------------------------------------------
+--echo -- 1.1.1.
+--echo --------------------------------------------------------------------
+
+--sleep 3
+# should be longer than monitoring interval and enough to start instance.
+
SHOW INSTANCES;
---replace_column 3 VERSION_NUMBER 4 VERSION
-SHOW INSTANCE STATUS mysqld1;
---replace_column 3 VERSION_NUMBER 4 VERSION
-SHOW INSTANCE STATUS mysqld2;
###########################################################################
#
@@ -33,20 +37,22 @@ SHOW INSTANCE STATUS mysqld2;
#
###########################################################################
+--echo
+--echo --------------------------------------------------------------------
+--echo -- 1.1.2.
+--echo --------------------------------------------------------------------
+
START INSTANCE mysqld2;
-# FIXME
+# FIXME: START INSTANCE should be synchronous.
--sleep 3
+# should be longer than monitoring interval and enough to start instance.
SHOW INSTANCES;
---replace_column 3 VERSION_NUMBER 4 VERSION
-SHOW INSTANCE STATUS mysqld1;
---replace_column 3 VERSION_NUMBER 4 VERSION
-SHOW INSTANCE STATUS mysqld2;
---connect (mysql_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
+--connect (mysql_con,localhost,root,,mysql,$IM_MYSQLD2_PORT,$IM_MYSQLD2_SOCK)
--connection mysql_con
---replace_result $IM_MYSQLD1_PORT IM_MYSQLD1_PORT
+--replace_result $IM_MYSQLD2_PORT IM_MYSQLD2_PORT
SHOW VARIABLES LIKE 'port';
--connection default
@@ -61,9 +67,15 @@ SHOW VARIABLES LIKE 'port';
#
###########################################################################
+--echo
+--echo --------------------------------------------------------------------
+--echo -- 1.1.3.
+--echo --------------------------------------------------------------------
+
STOP INSTANCE mysqld2;
-# FIXME
+# FIXME: STOP INSTANCE should be synchronous.
--sleep 3
+# should be longer than monitoring interval and enough to stop instance.
SHOW INSTANCES;
--replace_column 3 VERSION_NUMBER 4 VERSION
@@ -81,16 +93,17 @@ SHOW INSTANCE STATUS mysqld2;
#
###########################################################################
---error 3000
+--echo
+--echo --------------------------------------------------------------------
+--echo -- 1.1.4.
+--echo --------------------------------------------------------------------
+
+--error 3000 # ER_BAD_INSTANCE_NAME
START INSTANCE mysqld3;
---error 3002
+--error 3002 # ER_INSTANCE_ALREADY_STARTED
START INSTANCE mysqld1;
-# FIXME TODO
-# BUG#12813: START/STOP INSTANCE commands accept a list as argument
-# START INSTANCE mysqld1, mysqld2;
-
###########################################################################
#
# 1.1.5. Check that Instance Manager reports correct errors for 'STOP INSTANCE'
@@ -101,39 +114,54 @@ START INSTANCE mysqld1;
#
###########################################################################
---error 3000
+--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
+# --error 3002 # ER_INSTANCE_ALREADY_STARTED
# STOP INSTANCE mysqld2;
-# FIXME TODO
-# BUG#12813: START/STOP INSTANCE commands accept a list as argument
-# STOP INSTANCE mysqld1, mysqld2;
-
###########################################################################
#
# 1.1.6. Check that Instance Manager is able to restart guarded instances.
#
###########################################################################
+--echo
+--echo --------------------------------------------------------------------
+--echo -- 1.1.6.
+--echo --------------------------------------------------------------------
+
SHOW INSTANCES;
--exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_MYSQLD1_PATH_PID restarted
+--sleep 3
+# should be longer than monitoring interval and enough to start instance.
+
###########################################################################
#
# 1.1.7. Check that Instance Manager does not restart non-guarded instance.
#
###########################################################################
+--echo
+--echo --------------------------------------------------------------------
+--echo -- 1.1.7.
+--echo --------------------------------------------------------------------
+
SHOW INSTANCES;
START INSTANCE mysqld2;
-# FIXME
+# FIXME: START INSTANCE should be synchronous.
--sleep 3
+# should be longer than monitoring interval and enough to start instance.
SHOW INSTANCES;
@@ -147,7 +175,13 @@ SHOW INSTANCES;
# incomplete SHOW INSTANCE STATUS command.
#
###########################################################################
---error 1149
+
+--echo
+--echo --------------------------------------------------------------------
+--echo -- 1.1.8.
+--echo --------------------------------------------------------------------
+
+--error ER_SYNTAX_ERROR
SHOW INSTANCE STATUS;
#
@@ -159,8 +193,13 @@ SHOW INSTANCE STATUS;
# a list as argument.
#
---error 1149
+--echo
+--echo --------------------------------------------------------------------
+--echo -- BUG#12813
+--echo --------------------------------------------------------------------
+
+--error ER_SYNTAX_ERROR
START INSTANCE mysqld1,mysqld2,mysqld3;
---error 1149
+--error ER_SYNTAX_ERROR
STOP INSTANCE mysqld1,mysqld2,mysqld3;
diff --git a/mysql-test/t/im_options.imtest b/mysql-test/t/im_options.imtest
new file mode 100644
index 00000000000..cd905416cda
--- /dev/null
+++ b/mysql-test/t/im_options.imtest
@@ -0,0 +1,268 @@
+###########################################################################
+#
+# 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)
+#
+###########################################################################
+
+--source include/im_check_os.inc
+
+###########################################################################
+#
+# Check starting conditions.
+#
+###########################################################################
+
+# - check the configuration file;
+
+--echo --------------------------------------------------------------------
+--exec grep server_id $MYSQLTEST_VARDIR/im.cnf ;
+--echo --------------------------------------------------------------------
+
+# - check the running instances.
+
+--connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
+
+--connection mysql1_con
+
+SHOW VARIABLES LIKE 'server_id';
+
+--connection default
+
+# - check the internal cache.
+
+SHOW INSTANCES;
+
+###########################################################################
+#
+# 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 = '/';
+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 $MYSQLTEST_VARDIR/im.cnf ;
+--echo --------------------------------------------------------------------
+--exec grep ddd $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 $MYSQLTEST_VARDIR/im.cnf || true;
+--echo --------------------------------------------------------------------
+--exec grep ddd $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 $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 $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 $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 $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 $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 $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 $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;
diff --git a/mysql-test/t/im_options_set.imtest b/mysql-test/t/im_options_set.imtest
deleted file mode 100644
index a9b64861f99..00000000000
--- a/mysql-test/t/im_options_set.imtest
+++ /dev/null
@@ -1,142 +0,0 @@
-###########################################################################
-#
-# This file contains test for (3) test suite.
-#
-# Consult WL#2789 for more information.
-#
-###########################################################################
-
-#
-# Check the options-management commands:
-# - SET;
-# - FLUSH INSTANCES;
-#
-# Let's test the commands on the option 'server_id'. It's expected that
-# originally the instances have the following server ids:
-# - mysqld1: 1
-# - mysqld2: 2
-#
-# 1. SET <instance_id>.server_id= SERVER_ID); where SERVER_ID is 11 or 12.
-# 1.1. check that the configuration file has been updated (i.e. contains
-# server_id=SERVER_ID for the instance);
-# 1.2. (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;
-# 1.3. check that internal cache of Instance Manager has not been affected
-# (i.e. SHOW INSTANCE OPTIONS <instance> does not contain updated value).
-#
-# 2. FLUSH INSTANCES;
-# 2.1. check that the configuration file has not been updated;
-# 2.2. (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 value;
-# 2.3. check that internal cache of Instance Manager has been updated (i.e.
-# SHOW INSTANCE OPTIONS <instance> contains 'server_id=SERVER_ID' line).
-#
-# 3. Restore options.
-#
-
-###########################################################################
-
---source include/im_check_os.inc
-
-###########################################################################
-#
-# 0. Check starting conditions.
-#
-###########################################################################
-
-# - check the configuration file;
-
---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ;
-
-# - check the running instances.
-
---connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
-
---connection mysql1_con
-
-SHOW VARIABLES LIKE 'server_id';
-
---connection default
-
-# - check the internal cache.
-# TODO: we should check only server_id option here.
-
-# SHOW INSTANCE OPTIONS mysqld1;
-# SHOW INSTANCE OPTIONS mysqld2;
-
-###########################################################################
-#
-# 1. SET <instance_id>.server_id= SERVER_ID); where SERVER_ID is 11 or 12.
-#
-###########################################################################
-
-# * mysqld1
-
-SET mysqld1.server_id = 11;
-
-# - check that the configuration file has been updated (i.e. contains
-# server_id=SERVER_ID for the instance);
-
---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ;
-
-# - (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 not been affected
-# (i.e. SHOW INSTANCE OPTIONS <instance> does not contain updated value).
-# TODO: we should check only server_id option here.
-
-# SHOW INSTANCE OPTIONS mysqld1;
-
-# * mysqld2
-
-SET mysqld2.server_id = 12;
-
-# - check that the configuration file has been updated (i.e. contains
-# server_id=SERVER_ID for the instance);
-
---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ;
-
-# - check that internal cache of Instance Manager has not been affected
-# (i.e. SHOW INSTANCE OPTIONS <instance> does not contain updated value).
-# TODO: we should check only server_id option here.
-
-# SHOW INSTANCE OPTIONS mysqld2;
-
-###########################################################################
-#
-# 2. FLUSH INSTANCES;
-#
-###########################################################################
-
-FLUSH INSTANCES;
-
-# - check that the configuration file has not been updated;
-
---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ;
-
-# - (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 value;
-
---connection mysql1_con
-
-SHOW VARIABLES LIKE 'server_id';
-
---connection default
-
-# - check that internal cache of Instance Manager has been updated (i.e.
-# SHOW INSTANCE OPTIONS <instance> contains 'server_id=' line).
-# TODO: we should check only server_id option here.
-
-# SHOW INSTANCE OPTIONS mysqld1;
-# SHOW INSTANCE OPTIONS mysqld2;
diff --git a/mysql-test/t/im_options_unset.imtest b/mysql-test/t/im_options_unset.imtest
deleted file mode 100644
index 40629805d45..00000000000
--- a/mysql-test/t/im_options_unset.imtest
+++ /dev/null
@@ -1,150 +0,0 @@
-###########################################################################
-#
-# This file contains test for (3) test suite.
-#
-# Consult WL#2789 for more information.
-#
-###########################################################################
-
-#
-# Check the options-management commands:
-# - UNSET;
-# - FLUSH INSTANCES;
-#
-# Let's test the commands on the option 'server_id'. It's expected that
-# originally the instances have the following server ids:
-# - mysqld1: 1
-# - mysqld2: 2
-#
-# The test case:
-#
-# 1. UNSET <instance_id>.server_id;
-#
-# Do the step for both instances.
-#
-# 1.1. check that the configuration file has been updated (i.e. does not
-# contain 'server_id=' line for the instance);
-# 1.2. (for mysqld1) check that the running instance has not been affected:
-# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id''
-# returns non-zero value;
-# 1.3. check that internal cache of Instance Manager is not affected (i.e.
-# SHOW INSTANCE OPTIONS <instance> contains non-zero value for server_id);
-#
-# 2. FLUSH INSTANCES;
-#
-# Do the step for both instances.
-#
-# 2.1. check that the configuration file has not been updated (i.e. does not
-# contain 'server_id=' for the instance);
-# 2.2. (for mysqld1) check that the running instance has not been affected:
-# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id''
-# returns non-zero value;
-# 2.3. check that internal cache of Instance Manager has been updated (i.e.
-# SHOW INSTANCE OPTIONS <instance> does not contain 'server_id=' line).
-#
-
-###########################################################################
-
---source include/im_check_os.inc
-
-###########################################################################
-#
-# 0. Check starting conditions.
-#
-###########################################################################
-
-# - check the configuration file;
-
---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ;
-
-# - check the running instances.
-
---connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
-
---connection mysql1_con
-
-SHOW VARIABLES LIKE 'server_id';
-
---connection default
-
-# - check the internal cache.
-# TODO: we should check only server_id option here.
-
-# SHOW INSTANCE OPTIONS mysqld1;
-# SHOW INSTANCE OPTIONS mysqld2;
-
-###########################################################################
-#
-# 1. UNSET <instance_id>.server_id;
-#
-###########################################################################
-
-# * mysqld1
-
-UNSET mysqld1.server_id;
-
-# - check that the configuration file has been updated (i.e. does not
-# contain 'server_id=' line for the instance);
-
---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ;
-
-# - check that the running instance has not been affected: connect to the
-# instance and check that 'SHOW VARIABLES LIKE 'server_id'' returns non-zero
-# value;
-
---connection mysql1_con
-
-SHOW VARIABLES LIKE 'server_id';
-
---connection default
-
-# - check that internal cache of Instance Manager is not affected (i.e. SHOW
-# INSTANCE OPTIONS <instance> contains non-zero value for server_id);
-# TODO: we should check only server_id option here.
-
-# SHOW INSTANCE OPTIONS mysqld1;
-
-# * mysqld2
-
-UNSET mysqld2.server_id;
-
-# - check that the configuration file has been updated (i.e. does not
-# contain 'server_id=' line for the instance);
-
---exec grep server_id $MYSQLTEST_VARDIR/im.cnf || true;
-
-# - check that internal cache of Instance Manager is not affected (i.e. SHOW
-# INSTANCE OPTIONS <instance> contains non-zero value for server_id);
-# TODO: we should check only server_id option here.
-
-# SHOW INSTANCE OPTIONS mysqld2;
-
-###########################################################################
-#
-# 2. FLUSH INSTANCES;
-#
-###########################################################################
-
-FLUSH INSTANCES;
-
-# - check that the configuration file has not been updated (i.e. does not
-# contain 'server_id=' for the instance);
-
---exec grep server_id $MYSQLTEST_VARDIR/im.cnf || true;
-
-# - (for mysqld1) check that the running instance has not been affected:
-# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id''
-# returns non-zero value;
-
---connection mysql1_con
-
-SHOW VARIABLES LIKE 'server_id';
-
---connection default
-
-# - check that internal cache of Instance Manager has been updated (i.e.
-# SHOW INSTANCE OPTIONS <instance> does not contain 'server_id=' line).
-# TODO: we should check only server_id option here.
-
-# SHOW INSTANCE OPTIONS mysqld1;
-# SHOW INSTANCE OPTIONS mysqld2;
diff --git a/mysql-test/t/im_utils-im.opt b/mysql-test/t/im_utils-im.opt
new file mode 100644
index 00000000000..34b74ce0c95
--- /dev/null
+++ b/mysql-test/t/im_utils-im.opt
@@ -0,0 +1 @@
+--monitoring-interval=1
diff --git a/mysql-test/t/im_utils.imtest b/mysql-test/t/im_utils.imtest
index dc6fb93c4ff..8e8d475cfee 100644
--- a/mysql-test/t/im_utils.imtest
+++ b/mysql-test/t/im_utils.imtest
@@ -17,6 +17,9 @@
# - the second instance is offline;
#
+--sleep 3
+# should be longer than monitoring interval and enough to start instance.
+
SHOW INSTANCES;
#
@@ -41,8 +44,9 @@ SHOW INSTANCE OPTIONS mysqld2;
START INSTANCE mysqld2;
-# FIXME
--- sleep 3
+# FIXME: START INSTANCE should be synchronous.
+--sleep 3
+# should be longer than monitoring interval and enough to start instance.
STOP INSTANCE mysqld2;
diff --git a/mysys/default.c b/mysys/default.c
index 580bcc19eca..3a80d7b37b9 100644
--- a/mysys/default.c
+++ b/mysys/default.c
@@ -244,7 +244,8 @@ err:
handle_option_ctx 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)
+ prepared to be used in argv (has -- prefix). If it
+ is NULL, we are handling a new group (section).
DESCRIPTION
This handler checks whether a group is one of the listed and adds an option
@@ -263,6 +264,9 @@ static int handle_default_option(void *in_ctx, const char *group_name,
char *tmp;
struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx;
+ if (!option)
+ return 0;
+
if (find_type((char *)group_name, ctx->group, 3))
{
if (!(tmp= alloc_root(ctx->alloc, (uint) strlen(option) + 1)))
@@ -719,6 +723,10 @@ static int search_default_file_with_ext(Process_option_func opt_handler,
end[0]=0;
strnmov(curr_gr, ptr, min((uint) (end-ptr)+1, 4096));
+
+ /* signal that a new group is found */
+ opt_handler(handler_ctx, curr_gr, NULL);
+
continue;
}
if (!found_group)
diff --git a/mysys/default_modify.c b/mysys/default_modify.c
index 0f58b8a930c..8dbcac699ea 100644
--- a/mysys/default_modify.c
+++ b/mysys/default_modify.c
@@ -40,11 +40,13 @@ static char *add_option(char *dst, const char *option_value,
SYNOPSYS
modify_defaults_file()
file_location The location of configuration file to edit
- option option to look for
- option value The value of the option we would like to set
- section_name the name of the section
- remove_option This is true if we want to remove the option.
- False otherwise.
+ option The name of the option to look for (can be NULL)
+ option value The value of the option we would like to set (can be NULL)
+ section_name The name of the section (must be NOT NULL)
+ remove_option This defines what we want to remove:
+ - MY_REMOVE_NONE -- nothing to remove;
+ - MY_REMOVE_OPTION -- remove the specified option;
+ - MY_REMOVE_SECTION -- remove the specified section;
IMPLEMENTATION
We open the option file first, then read the file line-by-line,
looking for the section we need. At the same time we put these lines
@@ -67,7 +69,9 @@ int modify_defaults_file(const char *file_location, const char *option,
FILE *cnf_file;
MY_STAT file_stat;
char linebuff[BUFF_SIZE], *src_ptr, *dst_ptr, *file_buffer;
- uint opt_len, optval_len, sect_len, nr_newlines= 0, buffer_size;
+ uint opt_len= 0;
+ uint optval_len= 0;
+ uint sect_len, nr_newlines= 0, buffer_size;
my_bool in_section= FALSE, opt_applied= 0;
uint reserve_extended;
uint new_opt_len;
@@ -81,8 +85,11 @@ int modify_defaults_file(const char *file_location, const char *option,
if (my_fstat(fileno(cnf_file), &file_stat, MYF(0)))
goto malloc_err;
- opt_len= (uint) strlen(option);
- optval_len= (uint) strlen(option_value);
+ if (option && option_value)
+ {
+ opt_len= (uint) strlen(option);
+ optval_len= (uint) strlen(option_value);
+ }
new_opt_len= opt_len + 1 + optval_len + NEWLINE_LEN;
@@ -119,8 +126,8 @@ int modify_defaults_file(const char *file_location, const char *option,
continue;
}
- /* correct the option */
- if (in_section && !strncmp(src_ptr, option, opt_len) &&
+ /* correct the option (if requested) */
+ if (option && in_section && !strncmp(src_ptr, option, opt_len) &&
(*(src_ptr + opt_len) == '=' ||
my_isspace(&my_charset_latin1, *(src_ptr + opt_len)) ||
*(src_ptr + opt_len) == '\0'))
@@ -143,7 +150,12 @@ int modify_defaults_file(const char *file_location, const char *option,
}
else
{
- /* If going to new group and we have option to apply, do it now */
+ /*
+ If we are going to the new group and have an option to apply, do
+ it now. If we are removing a single option or the whole section
+ this will only trigger opt_applied flag.
+ */
+
if (in_section && !opt_applied && *src_ptr == '[')
{
dst_ptr= add_option(dst_ptr, option_value, option, remove_option);
@@ -153,7 +165,10 @@ int modify_defaults_file(const char *file_location, const char *option,
for (; nr_newlines; nr_newlines--)
dst_ptr= strmov(dst_ptr, NEWLINE);
- dst_ptr= strmov(dst_ptr, linebuff);
+
+ /* Skip the section if MY_REMOVE_SECTION was given */
+ if (!in_section || remove_option != MY_REMOVE_SECTION)
+ dst_ptr= strmov(dst_ptr, linebuff);
}
/* Look for a section */
if (*src_ptr == '[')
@@ -167,18 +182,31 @@ int modify_defaults_file(const char *file_location, const char *option,
{}
if (*src_ptr != ']')
+ {
+ in_section= FALSE;
continue; /* Missing closing parenthesis. Assume this was no group */
+ }
+
+ if (remove_option == MY_REMOVE_SECTION)
+ dst_ptr= dst_ptr - strlen(linebuff);
+
in_section= TRUE;
}
else
in_section= FALSE; /* mark that this section is of no interest to us */
}
}
- /* File ended. */
- if (!opt_applied && !remove_option && in_section)
+
+ /*
+ File ended. Apply an option or set opt_applied flag (in case of
+ MY_REMOVE_SECTION) so that the changes are saved. Do not do anything
+ if we are removing non-existent option.
+ */
+
+ if (!opt_applied && in_section && (remove_option != MY_REMOVE_OPTION))
{
/* New option still remains to apply at the end */
- if (*(dst_ptr - 1) != '\n')
+ if (!remove_option && *(dst_ptr - 1) != '\n')
dst_ptr= strmov(dst_ptr, NEWLINE);
dst_ptr= add_option(dst_ptr, option_value, option, remove_option);
opt_applied= 1;
diff --git a/server-tools/instance-manager/IMService.cpp b/server-tools/instance-manager/IMService.cpp
index b7ea8e7eb81..3093147bccf 100644
--- a/server-tools/instance-manager/IMService.cpp
+++ b/server-tools/instance-manager/IMService.cpp
@@ -20,7 +20,7 @@ IMService::~IMService(void)
void IMService::Stop()
{
ReportStatus(SERVICE_STOP_PENDING);
-
+
// stop the IM work
raise(SIGTERM);
}
@@ -32,7 +32,7 @@ void IMService::Run(DWORD argc, LPTSTR *argv)
Options o;
o.load(argc, argv);
-
+
// init goes here
ReportStatus((DWORD)SERVICE_RUNNING);
@@ -46,13 +46,13 @@ void IMService::Log(const char *msg)
log_info(msg);
}
-int HandleServiceOptions(Options options)
+int HandleServiceOptions()
{
int ret_val= 0;
IMService winService;
- if (options.install_as_service)
+ if (Options::Service::install_as_service)
{
if (winService.IsInstalled())
log_info("Service is already installed");
@@ -64,7 +64,7 @@ int HandleServiceOptions(Options options)
ret_val= 1;
}
}
- else if (options.remove_service)
+ else if (Options::Service::remove_service)
{
if (! winService.IsInstalled())
log_info("Service is not installed");
@@ -77,6 +77,19 @@ int HandleServiceOptions(Options options)
}
}
else
- ret_val= !winService.Init();
+ {
+ log_info("Initializing Instance Manager service...");
+
+ if (!winService.Init())
+ {
+ log_info("Service failed to initialize.");
+ fprintf(stderr,
+ "The service should be started by Windows Service Manager.\n"
+ "The MySQL Manager should be started with '--standalone'\n"
+ "to run from command line.");
+ ret_val= 1;
+ }
+ }
+
return ret_val;
}
diff --git a/server-tools/instance-manager/IMService.h b/server-tools/instance-manager/IMService.h
index cad38bebdaf..94d59c2af31 100644
--- a/server-tools/instance-manager/IMService.h
+++ b/server-tools/instance-manager/IMService.h
@@ -1,3 +1,21 @@
+/*
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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
+*/
+
#pragma once
#include "windowsservice.h"
@@ -12,3 +30,5 @@ protected:
void Stop();
void Run(DWORD argc, LPTSTR *argv);
};
+
+extern int HandleServiceOptions();
diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am
index 462d7984aa4..59f057648ed 100644
--- a/server-tools/instance-manager/Makefile.am
+++ b/server-tools/instance-manager/Makefile.am
@@ -76,7 +76,10 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
guardian.cc guardian.h \
parse_output.cc parse_output.h \
mysql_manager_error.h \
- portability.h
+ portability.h \
+ exit_codes.h \
+ user_management_commands.h \
+ user_management_commands.cc
mysqlmanager_LDADD= @CLIENT_EXTRA_LDFLAGS@ \
liboptions.la \
@@ -90,6 +93,9 @@ mysqlmanager_LDADD= @CLIENT_EXTRA_LDFLAGS@ \
EXTRA_DIST = WindowsService.cpp WindowsService.h IMService.cpp \
IMService.h cmakelists.txt
+AM_CFLAGS = -Werror
+AM_CXXFLAGS = -Werror
+
tags:
ctags -R *.h *.cc
diff --git a/server-tools/instance-manager/WindowsService.cpp b/server-tools/instance-manager/WindowsService.cpp
index 192045b7a4c..8a36a2f2fdd 100644
--- a/server-tools/instance-manager/WindowsService.cpp
+++ b/server-tools/instance-manager/WindowsService.cpp
@@ -7,9 +7,9 @@ static WindowsService *gService;
WindowsService::WindowsService(void) :
statusCheckpoint(0),
serviceName(NULL),
- inited(false),
+ inited(FALSE),
dwAcceptedControls(SERVICE_ACCEPT_STOP),
- debugging(false)
+ debugging(FALSE)
{
gService= this;
status.dwServiceType= SERVICE_WIN32_OWN_PROCESS;
@@ -22,11 +22,12 @@ WindowsService::~WindowsService(void)
BOOL WindowsService::Install()
{
- bool ret_val= false;
+ bool ret_val= FALSE;
SC_HANDLE newService;
SC_HANDLE scm;
- if (IsInstalled()) return true;
+ if (IsInstalled())
+ return TRUE;
// determine the name of the currently executing file
char szFilePath[_MAX_PATH];
@@ -34,7 +35,7 @@ BOOL WindowsService::Install()
// open a connection to the SCM
if (!(scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
- return false;
+ return FALSE;
newService= CreateService(scm, serviceName, displayName,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
@@ -45,7 +46,7 @@ BOOL WindowsService::Install()
if (newService)
{
CloseServiceHandle(newService);
- ret_val= true;
+ ret_val= TRUE;
}
CloseServiceHandle(scm);
@@ -56,34 +57,35 @@ BOOL WindowsService::Init()
{
assert(serviceName != NULL);
- if (inited) return true;
+ if (inited)
+ return TRUE;
SERVICE_TABLE_ENTRY stb[] =
{
{ (LPSTR)serviceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{ NULL, NULL }
};
- inited= true;
+ inited= TRUE;
return StartServiceCtrlDispatcher(stb); //register with the Service Manager
}
BOOL WindowsService::Remove()
{
- bool ret_val= false;
+ bool ret_val= FALSE;
- if (! IsInstalled())
- return true;
+ if (!IsInstalled())
+ return TRUE;
// open a connection to the SCM
SC_HANDLE scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE);
- if (! scm)
- return false;
+ if (!scm)
+ return FALSE;
SC_HANDLE service= OpenService(scm, serviceName, DELETE);
if (service)
{
if (DeleteService(service))
- ret_val= true;
+ ret_val= TRUE;
DWORD dw= ::GetLastError();
CloseServiceHandle(service);
}
@@ -116,7 +118,8 @@ void WindowsService::SetAcceptedControls(DWORD acceptedControls)
BOOL WindowsService::ReportStatus(DWORD currentState, DWORD waitHint,
DWORD dwError)
{
- if(debugging) return TRUE;
+ if (debugging)
+ return TRUE;
if(currentState == SERVICE_START_PENDING)
status.dwControlsAccepted= 0;
diff --git a/server-tools/instance-manager/WindowsService.h b/server-tools/instance-manager/WindowsService.h
index 1a034ce1351..3af7cdf39a7 100644
--- a/server-tools/instance-manager/WindowsService.h
+++ b/server-tools/instance-manager/WindowsService.h
@@ -1,3 +1,21 @@
+/*
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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
+*/
+
#pragma once
class WindowsService
diff --git a/server-tools/instance-manager/command.h b/server-tools/instance-manager/command.h
index b84cc6a8e9e..f31ea404867 100644
--- a/server-tools/instance-manager/command.h
+++ b/server-tools/instance-manager/command.h
@@ -22,10 +22,12 @@
#pragma interface
#endif
-/* Class responsible for allocation of im commands. */
+/* Class responsible for allocation of IM commands. */
class Instance_map;
+struct st_net;
+
/*
Command - entry point for any command.
GangOf4: 'Command' design pattern
@@ -37,8 +39,18 @@ public:
Command(Instance_map *instance_map_arg= 0);
virtual ~Command();
- /* method of executing: */
- virtual int execute(struct st_net *net, ulong connection_id) = 0;
+ /*
+ This operation incapsulates behaviour of the command.
+
+ SYNOPSYS
+ 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:
Instance_map *instance_map;
diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc
index 7b999f61503..83ed9cf6536 100644
--- a/server-tools/instance-manager/commands.cc
+++ b/server-tools/instance-manager/commands.cc
@@ -14,36 +14,54 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
+#pragma implementation
+#endif
+
#include "commands.h"
+#include <my_global.h>
+#include <m_string.h>
+#include <m_ctype.h>
+#include <mysql.h>
+#include <my_dir.h>
+
+#include "buffer.h"
+#include "guardian.h"
#include "instance_map.h"
+#include "log.h"
+#include "manager.h"
#include "messages.h"
#include "mysqld_error.h"
#include "mysql_manager_error.h"
-#include "protocol.h"
-#include "buffer.h"
#include "options.h"
+#include "priv.h"
+#include "protocol.h"
-#include <m_string.h>
-#include <m_ctype.h>
-#include <mysql.h>
-#include <my_dir.h>
+
+/*
+ modify_defaults_to_im_error -- a map of error codes of
+ mysys::modify_defaults_file() into Instance Manager error codes.
+*/
+
+static const int modify_defaults_to_im_error[]= { 0, ER_OUT_OF_RESOURCES,
+ ER_ACCESS_OPTION_FILE };
/*
- Add a string to a buffer
+ Add a string to a buffer.
SYNOPSYS
put_to_buff()
buff buffer to add the string
str string to add
- uint offset in the buff to add a string
+ position offset in the buff to add a string
DESCRIPTION
Function to add a string to the buffer. It is different from
- store_to_protocol_packet, which is used in the protocol.cc. The last
- one also stores the length of the string in a special way.
+ store_to_protocol_packet, which is used in the protocol.cc.
+ The last one also stores the length of the string in a special way.
This is required for MySQL client/server protocol support only.
RETURN
@@ -51,7 +69,6 @@
1 - error occured
*/
-
static inline int put_to_buff(Buffer *buff, const char *str, uint *position)
{
uint len= strlen(str);
@@ -88,749 +105,1615 @@ static int parse_version_number(const char *version_str, char *version,
}
-/* implementation for Show_instances: */
+/**************************************************************************
+ Implementation of Instance_name.
+**************************************************************************/
+Instance_name::Instance_name(const LEX_STRING *name)
+{
+ str.str= str_buffer;
+ str.length= name->length;
-/*
- The method sends a list of instances in the instance map to the client.
+ if (str.length > MAX_INSTANCE_NAME_SIZE - 1)
+ str.length= MAX_INSTANCE_NAME_SIZE - 1;
- SYNOPSYS
- Show_instances::execute()
- net The network connection to the client.
- connection_id Client connection ID
+ strmake(str.str, name->str, str.length);
+}
- RETURN
- 0 - ok
- 1 - error occured
+/**************************************************************************
+ Implementation of Show_instances.
+**************************************************************************/
+
+/*
+ Implementation of SHOW INSTANCES statement.
+
+ Possible error codes:
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
-int Show_instances::execute(struct st_net *net, ulong connection_id)
+int Show_instances::execute(st_net *net, ulong connection_id)
{
- Buffer send_buff; /* buffer for packets */
- LIST name, status;
- NAME_WITH_LENGTH name_field, status_field;
+ int err_code;
+
+ if ((err_code= write_header(net)) ||
+ (err_code= write_data(net)))
+ return err_code;
+
+ if (send_eof(net) || net_flush(net))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+int Show_instances::write_header(st_net *net)
+{
+ LIST name, state;
+ LEX_STRING name_field, state_field;
LIST *field_list;
- uint position=0;
- name_field.name= (char*) "instance_name";
+ name_field.str= (char *) "instance_name";
name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field;
- status_field.name= (char*) "status";
- status_field.length= DEFAULT_FIELD_LENGTH;
- status.data= &status_field;
- field_list= list_add(NULL, &status);
+
+ state_field.str= (char *) "state";
+ state_field.length= DEFAULT_FIELD_LENGTH;
+ state.data= &state_field;
+
+ field_list= list_add(NULL, &state);
field_list= list_add(field_list, &name);
- send_fields(net, field_list);
+ return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0;
+}
+
+
+int Show_instances::write_data(st_net *net)
+{
+ my_bool err_status= FALSE;
+
+ Instance *instance;
+ Instance_map::Iterator iterator(instance_map);
+ instance_map->guardian->lock();
+ instance_map->lock();
+
+ while ((instance= iterator.next()))
{
- Instance *instance;
- Instance_map::Iterator iterator(instance_map);
+ Buffer send_buf; /* buffer for packets */
+ uint pos= 0;
+
+ const char *instance_name= instance->options.instance_name.str;
+ const char *state_name= instance_map->get_instance_state_name(instance);
- instance_map->lock();
- while ((instance= iterator.next()))
+ if (store_to_protocol_packet(&send_buf, instance_name, &pos) ||
+ store_to_protocol_packet(&send_buf, state_name, &pos) ||
+ my_net_write(net, send_buf.buffer, pos))
{
- position= 0;
- store_to_protocol_packet(&send_buff, instance->options.instance_name,
- &position);
- if (instance->is_running())
- store_to_protocol_packet(&send_buff, (char*) "online", &position);
- else
- store_to_protocol_packet(&send_buff, (char*) "offline", &position);
- if (my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
+ err_status= TRUE;
+ break;
}
- instance_map->unlock();
}
- if (send_eof(net))
- goto err;
- if (net_flush(net))
- goto err;
- return 0;
-err:
- return ER_OUT_OF_RESOURCES;
+ instance_map->unlock();
+ instance_map->guardian->unlock();
+
+ return err_status ? ER_OUT_OF_RESOURCES : 0;
}
-/* implementation for Flush_instances: */
+/**************************************************************************
+ Implementation of Flush_instances.
+**************************************************************************/
+
+/*
+ Implementation of FLUSH INSTANCES statement.
+
+ Possible error codes:
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+ ER_THERE_IS_ACTIVE_INSTACE If there is an active instance
+*/
-int Flush_instances::execute(struct st_net *net, ulong connection_id)
+int Flush_instances::execute(st_net *net, ulong connection_id)
{
- if (instance_map->flush_instances() ||
- net_send_ok(net, connection_id, NULL))
+ instance_map->guardian->lock();
+ instance_map->lock();
+
+ if (instance_map->is_there_active_instance())
+ {
+ instance_map->unlock();
+ instance_map->guardian->unlock();
+ return ER_THERE_IS_ACTIVE_INSTACE;
+ }
+
+ if (instance_map->flush_instances())
+ {
+ instance_map->unlock();
+ instance_map->guardian->unlock();
return ER_OUT_OF_RESOURCES;
+ }
- return 0;
+ instance_map->unlock();
+ instance_map->guardian->unlock();
+
+ return net_send_ok(net, connection_id, NULL) ? ER_OUT_OF_RESOURCES : 0;
}
-/* implementation for Show_instance_status: */
+/**************************************************************************
+ Implementation of Abstract_instance_cmd.
+**************************************************************************/
-Show_instance_status::Show_instance_status(Instance_map *instance_map_arg,
- const char *name, uint len)
- :Command(instance_map_arg)
+Abstract_instance_cmd::Abstract_instance_cmd(
+ Instance_map *instance_map_arg, const LEX_STRING *instance_name_arg)
+ :Command(instance_map_arg),
+ instance_name(instance_name_arg)
{
- Instance *instance;
+ /*
+ MT-NOTE: we can not make a search for Instance object here,
+ because it can dissappear after releasing the lock.
+ */
+}
+
+
+int Abstract_instance_cmd::execute(st_net *net, ulong connection_id)
+{
+ int err_code;
+
+ instance_map->lock();
+
+ {
+ Instance *instance= instance_map->find(get_instance_name());
+
+ if (!instance)
+ {
+ instance_map->unlock();
+ return ER_BAD_INSTANCE_NAME;
+ }
+
+ err_code= execute_impl(net, instance);
+ }
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
- else
- instance_name= NULL;
+ instance_map->unlock();
+
+ if (!err_code)
+ err_code= send_ok_response(net, connection_id);
+
+ return err_code;
}
-/*
- The method sends a table with a status of requested instance to the client.
+/**************************************************************************
+ Implementation of Show_instance_status.
+**************************************************************************/
- SYNOPSYS
- Show_instance_status::do_command()
- net The network connection to the client.
- instance_name The name of the instance.
+Show_instance_status::Show_instance_status(Instance_map *instance_map_arg,
+ const LEX_STRING *instance_name_arg)
+ :Abstract_instance_cmd(instance_map_arg, instance_name_arg)
+{
+}
- RETURN
- 0 - ok
- 1 - error occured
+
+/*
+ Implementation of SHOW INSTANCE STATUS statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
+int Show_instance_status::execute_impl(st_net *net, Instance *instance)
+{
+ int err_code;
+
+ if ((err_code= write_header(net)) ||
+ (err_code= write_data(net, instance)))
+ return err_code;
-int Show_instance_status::execute(struct st_net *net,
- ulong connection_id)
+ return 0;
+}
+
+
+int Show_instance_status::send_ok_response(st_net *net, ulong connection_id)
{
- enum { MAX_VERSION_LENGTH= 40 };
- Buffer send_buff; /* buffer for packets */
- LIST name, status, version, version_number;
+ if (send_eof(net) || net_flush(net))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+int Show_instance_status::write_header(st_net *net)
+{
+ LIST name, state, version, version_number, mysqld_compatible;
LIST *field_list;
- NAME_WITH_LENGTH name_field, status_field, version_field,
- version_number_field;
- uint position=0;
+ LEX_STRING name_field, state_field, version_field,
+ version_number_field, mysqld_compatible_field;
- if (!instance_name)
- return ER_BAD_INSTANCE_NAME;
+ /* Create list of the fileds to be passed to send_fields(). */
- /* create list of the fileds to be passed to send_fields */
- name_field.name= (char*) "instance_name";
+ name_field.str= (char *) "instance_name";
name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field;
- status_field.name= (char*) "status";
- status_field.length= DEFAULT_FIELD_LENGTH;
- status.data= &status_field;
- version_field.name= (char*) "version";
+
+ state_field.str= (char *) "state";
+ state_field.length= DEFAULT_FIELD_LENGTH;
+ state.data= &state_field;
+
+ version_field.str= (char *) "version";
version_field.length= MAX_VERSION_LENGTH;
version.data= &version_field;
- version_number_field.name= (char*) "version_number";
+
+ version_number_field.str= (char *) "version_number";
version_number_field.length= MAX_VERSION_LENGTH;
version_number.data= &version_number_field;
- field_list= list_add(NULL, &version);
+
+ mysqld_compatible_field.str= (char *) "mysqld_compatible";
+ mysqld_compatible_field.length= DEFAULT_FIELD_LENGTH;
+ mysqld_compatible.data= &mysqld_compatible_field;
+
+ field_list= list_add(NULL, &mysqld_compatible);
+ field_list= list_add(field_list, &version);
field_list= list_add(field_list, &version_number);
- field_list= list_add(field_list, &status);
+ field_list= list_add(field_list, &state);
field_list= list_add(field_list, &name);
- send_fields(net, field_list);
+ return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0;
+}
- {
- Instance *instance;
- store_to_protocol_packet(&send_buff, (char*) instance_name, &position);
- if (!(instance= instance_map->find(instance_name, strlen(instance_name))))
- goto err;
- if (instance->is_running())
- store_to_protocol_packet(&send_buff, (char*) "online", &position);
- else
- store_to_protocol_packet(&send_buff, (char*) "offline", &position);
+int Show_instance_status::write_data(st_net *net, Instance *instance)
+{
+ Buffer send_buf; /* buffer for packets */
+ char version_num_buf[MAX_VERSION_LENGTH];
+ uint pos= 0;
- if (instance->options.mysqld_version)
- {
- char parsed_version[MAX_VERSION_LENGTH];
+ const char *state_name;
+ const char *version_tag= "unknown";
+ const char *version_num= "unknown";
+ const char *mysqld_compatible_status;
- parse_version_number(instance->options.mysqld_version, parsed_version,
- sizeof(parsed_version));
- store_to_protocol_packet(&send_buff, parsed_version, &position);
+ instance_map->guardian->lock();
+ state_name= instance_map->get_instance_state_name(instance);
+ mysqld_compatible_status= instance->is_mysqld_compatible() ? "yes" : "no";
+ instance_map->guardian->unlock();
- store_to_protocol_packet(&send_buff, instance->options.mysqld_version,
- &position);
- }
- else
- {
- store_to_protocol_packet(&send_buff, (char*) "unknown", &position);
- store_to_protocol_packet(&send_buff, (char*) "unknown", &position);
- }
+ if (instance->options.mysqld_version)
+ {
+ if (parse_version_number(instance->options.mysqld_version, version_num_buf,
+ sizeof(version_num_buf)))
+ return ER_OUT_OF_RESOURCES;
- if (send_buff.is_error() ||
- my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
+ version_num= version_num_buf;
+ version_tag= instance->options.mysqld_version;
}
- if (send_eof(net) || net_flush(net))
- goto err;
+ if (store_to_protocol_packet(&send_buf, get_instance_name()->str, &pos) ||
+ store_to_protocol_packet(&send_buf, state_name, &pos) ||
+ store_to_protocol_packet(&send_buf, version_num, &pos) ||
+ store_to_protocol_packet(&send_buf, version_tag, &pos) ||
+ store_to_protocol_packet(&send_buf, mysqld_compatible_status, &pos) ||
+ my_net_write(net, send_buf.buffer, (uint) pos))
+ {
+ return ER_OUT_OF_RESOURCES;
+ }
return 0;
+}
+
+
+/**************************************************************************
+ Implementation of Show_instance_options.
+**************************************************************************/
-err:
- return ER_OUT_OF_RESOURCES;
+Show_instance_options::Show_instance_options(
+ Instance_map *instance_map_arg, const LEX_STRING *instance_name_arg)
+ :Abstract_instance_cmd(instance_map_arg, instance_name_arg)
+{
}
-/* Implementation for Show_instance_options */
+/*
+ Implementation of SHOW INSTANCE OPTIONS statement.
-Show_instance_options::Show_instance_options(Instance_map *instance_map_arg,
- const char *name, uint len):
- Command(instance_map_arg)
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Show_instance_options::execute_impl(st_net *net, Instance *instance)
{
- Instance *instance;
+ int err_code;
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
- else
- instance_name= NULL;
+ if ((err_code= write_header(net)) ||
+ (err_code= write_data(net, instance)))
+ return err_code;
+
+ return 0;
}
-int Show_instance_options::execute(struct st_net *net, ulong connection_id)
+int Show_instance_options::send_ok_response(st_net *net, ulong connection_id)
+{
+ if (send_eof(net) || net_flush(net))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+int Show_instance_options::write_header(st_net *net)
{
- Buffer send_buff; /* buffer for packets */
LIST name, option;
LIST *field_list;
- NAME_WITH_LENGTH name_field, option_field;
- uint position=0;
+ LEX_STRING name_field, option_field;
- if (!instance_name)
- return ER_BAD_INSTANCE_NAME;
+ /* Create list of the fileds to be passed to send_fields(). */
- /* create list of the fileds to be passed to send_fields */
- name_field.name= (char*) "option_name";
+ name_field.str= (char *) "option_name";
name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field;
- option_field.name= (char*) "value";
+
+ option_field.str= (char *) "value";
option_field.length= DEFAULT_FIELD_LENGTH;
option.data= &option_field;
+
field_list= list_add(NULL, &option);
field_list= list_add(field_list, &name);
- send_fields(net, field_list);
+ return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0;
+}
+
+int Show_instance_options::write_data(st_net *net, Instance *instance)
+{
+ Buffer send_buff; /* buffer for packets */
+ uint pos= 0;
+
+ if (store_to_protocol_packet(&send_buff, "instance_name", &pos) ||
+ store_to_protocol_packet(&send_buff, get_instance_name()->str, &pos) ||
+ my_net_write(net, send_buff.buffer, pos))
{
- Instance *instance;
-
- if (!(instance= instance_map->find(instance_name, strlen(instance_name))))
- goto err;
- store_to_protocol_packet(&send_buff, (char*) "instance_name", &position);
- store_to_protocol_packet(&send_buff, (char*) instance_name, &position);
- if (my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
- if ((instance->options.mysqld_path))
- {
- position= 0;
- store_to_protocol_packet(&send_buff, (char*) "mysqld-path", &position);
- store_to_protocol_packet(&send_buff,
- (char*) instance->options.mysqld_path,
- &position);
- if (send_buff.is_error() ||
- my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
- }
+ return ER_OUT_OF_RESOURCES;
+ }
+
+ /* Loop through the options. */
+
+ for (int i= 0; i < instance->options.get_num_options(); i++)
+ {
+ Named_value option= instance->options.get_option(i);
+ const char *option_value= option.get_value()[0] ? option.get_value() : "";
+
+ pos= 0;
- if ((instance->options.nonguarded))
+ if (store_to_protocol_packet(&send_buff, option.get_name(), &pos) ||
+ store_to_protocol_packet(&send_buff, option_value, &pos) ||
+ my_net_write(net, send_buff.buffer, pos))
{
- position= 0;
- store_to_protocol_packet(&send_buff, (char*) "nonguarded", &position);
- store_to_protocol_packet(&send_buff, "", &position);
- if (send_buff.is_error() ||
- my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
+ return ER_OUT_OF_RESOURCES;
}
+ }
+
+ return 0;
+}
+
- /* loop through the options stored in DYNAMIC_ARRAY */
- for (uint i= 0; i < instance->options.options_array.elements; i++)
+/**************************************************************************
+ Implementation of Start_instance.
+**************************************************************************/
+
+Start_instance::Start_instance(Instance_map *instance_map_arg,
+ const LEX_STRING *instance_name_arg)
+ :Abstract_instance_cmd(instance_map_arg, instance_name_arg)
+{
+}
+
+
+/*
+ Implementation of START INSTANCE statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Start_instance::execute_impl(st_net *net, Instance *instance)
+{
+ int err_code;
+
+ if ((err_code= instance->start()))
+ return err_code;
+
+ if (!(instance->options.nonguarded))
+ instance_map->guardian->guard(instance);
+
+ return 0;
+}
+
+
+int Start_instance::send_ok_response(st_net *net, ulong connection_id)
+{
+ if (net_send_ok(net, connection_id, "Instance started"))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+/**************************************************************************
+ Implementation of Stop_instance.
+**************************************************************************/
+
+Stop_instance::Stop_instance(Instance_map *instance_map_arg,
+ const LEX_STRING *instance_name_arg)
+ :Abstract_instance_cmd(instance_map_arg, instance_name_arg)
+{
+}
+
+
+/*
+ Implementation of STOP INSTANCE statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Stop_instance::execute_impl(st_net *net, Instance *instance)
+{
+ int err_code;
+
+ if (!(instance->options.nonguarded))
+ instance_map->guardian->stop_guard(instance);
+
+ if ((err_code= instance->stop()))
+ return err_code;
+
+ return 0;
+}
+
+
+int Stop_instance::send_ok_response(st_net *net, ulong connection_id)
+{
+ if (net_send_ok(net, connection_id, NULL))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+/**************************************************************************
+ Implementation for Create_instance.
+**************************************************************************/
+
+Create_instance::Create_instance(Instance_map *instance_map_arg,
+ const LEX_STRING *instance_name_arg)
+ :Command(instance_map_arg),
+ instance_name(instance_name_arg)
+{
+}
+
+
+/*
+ This operation initializes Create_instance object.
+
+ SYNOPSYS
+ text [IN/OUT] a pointer to the text containing instance options.
+
+ RETURN
+ FALSE On success.
+ TRUE On error.
+*/
+
+bool Create_instance::init(const char **text)
+{
+ return options.init() || parse_args(text);
+}
+
+
+/*
+ This operation parses CREATE INSTANCE options.
+
+ SYNOPSYS
+ text [IN/OUT] a pointer to the text containing instance options.
+
+ RETURN
+ FALSE On success.
+ TRUE On syntax error.
+*/
+
+bool Create_instance::parse_args(const char **text)
+{
+ uint len;
+
+ /* Check if we have something (and trim leading spaces). */
+
+ get_word(text, &len, NONSPACE);
+
+ if (len == 0)
+ return FALSE; /* OK: no option. */
+
+ /* Main parsing loop. */
+
+ while (TRUE)
+ {
+ LEX_STRING option_name;
+ char *option_name_str;
+ char *option_value_str= NULL;
+
+ /* Looking for option name. */
+
+ get_word(text, &option_name.length, OPTION_NAME);
+
+ if (option_name.length == 0)
+ return TRUE; /* Syntax error: option name expected. */
+
+ option_name.str= (char *) *text;
+ *text+= option_name.length;
+
+ /* Looking for equal sign. */
+
+ skip_spaces(text);
+
+ if (**text == '=')
{
- char *tmp_option, *option_value;
- get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i);
- option_value= strchr(tmp_option, '=');
- /* split the option string into two parts if it has a value */
+ ++(*text); /* Skip an equal sign. */
- position= 0;
- if (option_value != NULL)
+ /* Looking for option value. */
+
+ skip_spaces(text);
+
+ if (!**text)
+ return TRUE; /* Syntax error: EOS when option value expected. */
+
+ if (**text != '\'' && **text != '"')
{
- *option_value= 0;
- store_to_protocol_packet(&send_buff, tmp_option + 2, &position);
- store_to_protocol_packet(&send_buff, option_value + 1, &position);
- /* join name and the value into the same option again */
- *option_value= '=';
+ /* Option value is a simple token. */
+
+ LEX_STRING option_value;
+
+ get_word(text, &option_value.length, ALPHANUM);
+
+ if (option_value.length == 0)
+ return TRUE; /* internal parser error. */
+
+ option_value.str= (char *) *text;
+ *text+= option_value.length;
+
+ if (!(option_value_str= Named_value::alloc_str(&option_value)))
+ return TRUE; /* out of memory during parsing. */
}
else
{
- store_to_protocol_packet(&send_buff, tmp_option + 2, &position);
- store_to_protocol_packet(&send_buff, "", &position);
+ /* Option value is a string. */
+
+ if (parse_option_value(*text, &len, &option_value_str))
+ return TRUE; /* Syntax error: invalid string specification. */
+
+ *text+= len;
}
+ }
+
+ if (!option_value_str)
+ {
+ LEX_STRING empty_str= { C_STRING_WITH_SIZE("") };
+
+ if (!(option_value_str= Named_value::alloc_str(&empty_str)))
+ return TRUE; /* out of memory during parsing. */
+ }
+
+ if (!(option_name_str= Named_value::alloc_str(&option_name)))
+ {
+ Named_value::free_str(&option_value_str);
+ return TRUE; /* out of memory during parsing. */
+ }
+
+ {
+ Named_value option(option_name_str, option_value_str);
- if (send_buff.is_error() ||
- my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
+ if (options.add_element(&option))
+ {
+ option.free();
+ return TRUE; /* out of memory during parsing. */
+ }
}
+
+ skip_spaces(text);
+
+ if (!**text)
+ return FALSE; /* OK: end of options. */
+
+ if (**text != ',')
+ return TRUE; /* Syntax error: comma expected. */
+
+ ++(*text);
}
+}
- if (send_eof(net) || net_flush(net))
- goto err;
- return 0;
+/*
+ Implementation of CREATE INSTANCE statement.
-err:
- return ER_OUT_OF_RESOURCES;
+ Possible error codes:
+ ER_MALFORMED_INSTANCE_NAME Instance name is malformed
+ ER_CREATE_EXISTING_INSTANCE There is an instance with the given name
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Create_instance::execute(st_net *net, ulong connection_id)
+{
+ int err_code;
+
+ /* Check that the name is valid and there is no instance with such name. */
+
+ if (!Instance::is_name_valid(get_instance_name()))
+ return ER_MALFORMED_INSTANCE_NAME;
+
+ /*
+ NOTE: In order to prevent race condition, we should perform all operations
+ on under acquired lock.
+ */
+
+ instance_map->lock();
+
+ if (instance_map->find(get_instance_name()))
+ {
+ instance_map->unlock();
+ return ER_CREATE_EXISTING_INSTANCE;
+ }
+
+ if ((err_code= instance_map->create_instance(get_instance_name(), &options)))
+ {
+ instance_map->unlock();
+ return err_code;
+ }
+
+ if ((err_code= create_instance_in_file(get_instance_name(), &options)))
+ {
+ Instance *instance= instance_map->find(get_instance_name());
+
+ if (instance)
+ instance_map->remove_instance(instance); /* instance is deleted here. */
+
+ instance_map->unlock();
+ return err_code;
+ }
+
+ /* That's all. */
+
+ instance_map->unlock();
+
+ /* Send the result. */
+
+ if (net_send_ok(net, connection_id, NULL))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
}
-/* Implementation for Start_instance */
+/**************************************************************************
+ Implementation for Drop_instance.
+**************************************************************************/
-Start_instance::Start_instance(Instance_map *instance_map_arg,
- const char *name, uint len)
- :Command(instance_map_arg)
+Drop_instance::Drop_instance(Instance_map *instance_map_arg,
+ const LEX_STRING *instance_name_arg)
+ :Abstract_instance_cmd(instance_map_arg, instance_name_arg)
{
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
}
-int Start_instance::execute(struct st_net *net, ulong connection_id)
+/*
+ Implementation of DROP INSTANCE statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_DROP_ACTIVE_INSTANCE The specified instance is active
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Drop_instance::execute_impl(st_net *net, Instance *instance)
{
- uint err_code;
- if (instance == 0)
- return ER_BAD_INSTANCE_NAME; /* haven't found an instance */
- else
- {
- if ((err_code= instance->start()))
- return err_code;
+ int err_code;
+
+ /* Check that the instance is offline. */
- if (!(instance->options.nonguarded))
- instance_map->guardian->guard(instance);
+ if (instance_map->guardian->is_active(instance))
+ return ER_DROP_ACTIVE_INSTANCE;
- net_send_ok(net, connection_id, "Instance started");
- return 0;
+ err_code= modify_defaults_file(Options::Main::config_file, NULL, NULL,
+ get_instance_name()->str, MY_REMOVE_SECTION);
+ DBUG_ASSERT(err_code >= 0 && err_code <= 2);
+
+ if (err_code)
+ {
+ log_error("Can not remove instance '%s' from defaults file (%s). "
+ "Original error code: %d.",
+ (const char *) get_instance_name()->str,
+ (const char *) Options::Main::config_file,
+ (int) err_code);
}
+
+ if (err_code)
+ return modify_defaults_to_im_error[err_code];
+
+ /* Remove instance from the instance map hash and Guardian's list. */
+
+ if (!instance->options.nonguarded)
+ instance_map->guardian->stop_guard(instance);
+
+ if ((err_code= instance->stop()))
+ return err_code;
+
+ instance_map->remove_instance(instance);
+
+ return 0;
}
-/* implementation for Show_instance_log: */
+int Drop_instance::send_ok_response(st_net *net, ulong connection_id)
+{
+ if (net_send_ok(net, connection_id, "Instance dropped"))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+/**************************************************************************
+ Implementation for Show_instance_log.
+**************************************************************************/
Show_instance_log::Show_instance_log(Instance_map *instance_map_arg,
- const char *name, uint len,
+ const LEX_STRING *instance_name_arg,
Log_type log_type_arg,
- const char *size_arg,
- const char *offset_arg)
- :Command(instance_map_arg)
+ uint size_arg, uint offset_arg)
+ :Abstract_instance_cmd(instance_map_arg, instance_name_arg),
+ log_type(log_type_arg),
+ size(size_arg),
+ offset(offset_arg)
{
- Instance *instance;
+}
+
+
+/*
+ Implementation of SHOW INSTANCE LOG statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_OFFSET_ERROR We were requested to read negative number of
+ bytes from the log
+ ER_NO_SUCH_LOG The specified type of log is not available for
+ the given instance
+ ER_GUESS_LOGFILE IM wasn't able to figure out the log
+ placement, while it is enabled. Probably user
+ should specify the path to the logfile
+ explicitly.
+ ER_OPEN_LOGFILE Cannot open the logfile
+ ER_READ_FILE Cannot read the logfile
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Show_instance_log::execute_impl(st_net *net, Instance *instance)
+{
+ int err_code;
+
+ if ((err_code= check_params(instance)))
+ return err_code;
+
+ if ((err_code= write_header(net)) ||
+ (err_code= write_data(net, instance)))
+ return err_code;
+
+ return 0;
+}
- if (offset_arg != NULL)
- offset= atoi(offset_arg);
- else
- offset= 0;
- size= atoi(size_arg);
- log_type= log_type_arg;
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
- else
- instance_name= NULL;
+int Show_instance_log::send_ok_response(st_net *net, ulong connection_id)
+{
+ if (send_eof(net) || net_flush(net))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
}
+int Show_instance_log::check_params(Instance *instance)
+{
+ const char *logpath= instance->options.logs[log_type];
-/*
- Open the logfile, read requested part of the log and send the info
- to the client.
+ /* Cannot read negative number of bytes. */
- SYNOPSYS
- Show_instance_log::execute()
- net The network connection to the client.
- connection_id Client connection ID
+ if (offset > size)
+ return ER_OFFSET_ERROR;
- DESCRIPTION
+ /* Instance has no such log. */
- Send a table with the content of the log requested. The function also
- deals with errro handling, to be verbose.
+ if (logpath == NULL)
+ return ER_NO_SUCH_LOG;
+
+ if (*logpath == '\0')
+ return ER_GUESS_LOGFILE;
+
+ return 0;
+}
- RETURN
- ER_OFFSET_ERROR We were requested to read negative number of bytes
- from the log
- ER_NO_SUCH_LOG The kind log being read is not enabled in the instance
- ER_GUESS_LOGFILE IM wasn't able to figure out the log placement, while
- it is enabled. Probably user should specify the path
- to the logfile explicitly.
- ER_OPEN_LOGFILE Cannot open the logfile
- ER_READ_FILE Cannot read the logfile
- ER_OUT_OF_RESOURCES We weren't able to allocate some resources
-*/
-int Show_instance_log::execute(struct st_net *net, ulong connection_id)
+int Show_instance_log::write_header(st_net *net)
{
- Buffer send_buff; /* buffer for packets */
LIST name;
LIST *field_list;
- NAME_WITH_LENGTH name_field;
- uint position= 0;
+ LEX_STRING name_field;
+
+ /* Create list of the fields to be passed to send_fields(). */
- /* create list of the fileds to be passed to send_fields */
- name_field.name= (char*) "Log";
+ name_field.str= (char *) "Log";
name_field.length= DEFAULT_FIELD_LENGTH;
- name.data= &name_field;
- field_list= list_add(NULL, &name);
- if (!instance_name)
- return ER_BAD_INSTANCE_NAME;
+ name.data= &name_field;
- /* cannot read negative number of bytes */
- if (offset > size)
- return ER_OFFSET_ERROR;
+ field_list= list_add(NULL, &name);
- send_fields(net, field_list);
+ return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0;
+}
- {
- Instance *instance;
- const char *logpath;
- File fd;
- if ((instance= instance_map->find(instance_name,
- strlen(instance_name))) == NULL)
- goto err;
+int Show_instance_log::write_data(st_net *net, Instance *instance)
+{
+ Buffer send_buff; /* buffer for packets */
+ uint pos= 0;
- logpath= instance->options.logs[log_type];
+ const char *logpath= instance->options.logs[log_type];
+ File fd;
- /* Instance has no such log */
- if (logpath == NULL)
- return ER_NO_SUCH_LOG;
+ size_t buff_size;
+ int read_len;
- if (*logpath == '\0')
- return ER_GUESS_LOGFILE;
+ MY_STAT file_stat;
+ Buffer read_buff;
+ if ((fd= my_open(logpath, O_RDONLY | O_BINARY, MYF(MY_WME))) <= 0)
+ return ER_OPEN_LOGFILE;
- if ((fd= my_open(logpath, O_RDONLY | O_BINARY, MYF(MY_WME))) >= 0)
- {
- size_t buff_size;
- int read_len;
- /* calculate buffer size */
- MY_STAT file_stat;
- Buffer read_buff;
+ /* my_fstat doesn't use the flag parameter */
+ if (my_fstat(fd, &file_stat, MYF(0)))
+ {
+ close(fd);
+ return ER_OUT_OF_RESOURCES;
+ }
- /* my_fstat doesn't use the flag parameter */
- if (my_fstat(fd, &file_stat, MYF(0)))
- goto err;
+ /* calculate buffer size */
+ buff_size= (size - offset);
- buff_size= (size - offset);
+ read_buff.reserve(0, buff_size);
- read_buff.reserve(0, buff_size);
+ /* read in one chunk */
+ read_len= (int)my_seek(fd, file_stat.st_size - size, MY_SEEK_SET, MYF(0));
- /* read in one chunk */
- read_len= (int)my_seek(fd, file_stat.st_size - size, MY_SEEK_SET, MYF(0));
+ if ((read_len= my_read(fd, (byte*) read_buff.buffer,
+ buff_size, MYF(0))) < 0)
+ {
+ close(fd);
+ return ER_READ_FILE;
+ }
- if ((read_len= my_read(fd, (byte*) read_buff.buffer,
- buff_size, MYF(0))) < 0)
- return ER_READ_FILE;
- store_to_protocol_packet(&send_buff, read_buff.buffer,
- &position, read_len);
- close(fd);
- }
- else
- return ER_OPEN_LOGFILE;
+ close(fd);
- if (my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
+ if (store_to_protocol_packet(&send_buff, read_buff.buffer, &pos, read_len) ||
+ my_net_write(net, send_buff.buffer, pos))
+ {
+ return ER_OUT_OF_RESOURCES;
}
- if (send_eof(net) || net_flush(net))
- goto err;
-
return 0;
-
-err:
- return ER_OUT_OF_RESOURCES;
}
-/* implementation for Show_instance_log_files: */
+/**************************************************************************
+ Implementation of Show_instance_log_files.
+**************************************************************************/
Show_instance_log_files::Show_instance_log_files
- (Instance_map *instance_map_arg, const char *name, uint len)
- :Command(instance_map_arg)
+ (Instance_map *instance_map_arg,
+ const LEX_STRING *instance_name_arg)
+ :Abstract_instance_cmd(instance_map_arg, instance_name_arg)
{
- Instance *instance;
-
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
- else
- instance_name= NULL;
}
/*
- The method sends a table with a list of log files
- used by the instance.
-
- SYNOPSYS
- Show_instance_log_files::execute()
- net The network connection to the client.
- connection_id The ID of the client connection
+ Implementation of SHOW INSTANCE LOG FILES statement.
- RETURN
- ER_BAD_INSTANCE_NAME The instance name specified is not valid
- ER_OUT_OF_RESOURCES some error occured
- 0 - ok
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
-int Show_instance_log_files::execute(struct st_net *net, ulong connection_id)
+int Show_instance_log_files::execute_impl(st_net *net, Instance *instance)
+{
+ int err_code;
+
+ if ((err_code= write_header(net)) ||
+ (err_code= write_data(net, instance)))
+ return err_code;
+
+ return 0;
+}
+
+
+int Show_instance_log_files::send_ok_response(st_net *net, ulong connection_id)
+{
+ if (send_eof(net) || net_flush(net))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+int Show_instance_log_files::write_header(st_net *net)
{
- Buffer send_buff; /* buffer for packets */
LIST name, path, size;
LIST *field_list;
- NAME_WITH_LENGTH name_field, path_field, size_field;
- uint position= 0;
+ LEX_STRING name_field, path_field, size_field;
- if (!instance_name)
- return ER_BAD_INSTANCE_NAME;
+ /* Create list of the fileds to be passed to send_fields(). */
- /* create list of the fileds to be passed to send_fields */
- name_field.name= (char*) "Logfile";
+ name_field.str= (char *) "Logfile";
name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field;
- path_field.name= (char*) "Path";
+
+ path_field.str= (char *) "Path";
path_field.length= DEFAULT_FIELD_LENGTH;
path.data= &path_field;
- size_field.name= (char*) "File size";
+
+ size_field.str= (char *) "File size";
size_field.length= DEFAULT_FIELD_LENGTH;
size.data= &size_field;
+
field_list= list_add(NULL, &size);
field_list= list_add(field_list, &path);
field_list= list_add(field_list, &name);
- send_fields(net, field_list);
+ return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0;
+}
- Instance *instance;
- if ((instance= instance_map->
- find(instance_name, strlen(instance_name))) == NULL)
- goto err;
+int Show_instance_log_files::write_data(st_net *net, Instance *instance)
+{
+ Buffer send_buff; /* buffer for packets */
+ /*
+ We have alike structure in instance_options.cc. We use such to be able
+ to loop through the options, which we need to handle in some common way.
+ */
+ struct log_files_st
+ {
+ const char *name;
+ const char *value;
+ } logs[]=
{
+ {"ERROR LOG", instance->options.logs[IM_LOG_ERROR]},
+ {"GENERAL LOG", instance->options.logs[IM_LOG_GENERAL]},
+ {"SLOW LOG", instance->options.logs[IM_LOG_SLOW]},
+ {NULL, NULL}
+ };
+ struct log_files_st *log_files;
+
+ for (log_files= logs; log_files->name; log_files++)
+ {
+ if (!log_files->value)
+ continue;
+
+ struct stat file_stat;
/*
- We have alike structure in instance_options.cc. We use such to be able
- to loop through the options, which we need to handle in some common way.
+ Save some more space for the log file names. In fact all
+ we need is strlen("GENERAL_LOG") + 1
*/
- struct log_files_st
- {
- const char *name;
- const char *value;
- } logs[]=
- {
- {"ERROR LOG", instance->options.logs[IM_LOG_ERROR]},
- {"GENERAL LOG", instance->options.logs[IM_LOG_GENERAL]},
- {"SLOW LOG", instance->options.logs[IM_LOG_SLOW]},
- {NULL, NULL}
- };
- struct log_files_st *log_files;
-
- for (log_files= logs; log_files->name; log_files++)
+ enum { LOG_NAME_BUFFER_SIZE= 20 };
+ char buff[LOG_NAME_BUFFER_SIZE];
+
+ uint pos= 0;
+
+ const char *log_path= "";
+ const char *log_size= "0";
+
+ if (!stat(log_files->value, &file_stat) &&
+ MY_S_ISREG(file_stat.st_mode))
{
- if (log_files->value != NULL)
- {
- struct stat file_stat;
- /*
- Save some more space for the log file names. In fact all
- we need is srtlen("GENERAL_LOG") + 1
- */
- enum { LOG_NAME_BUFFER_SIZE= 20 };
- char buff[LOG_NAME_BUFFER_SIZE];
-
- position= 0;
- /* store the type of the log in the send buffer */
- store_to_protocol_packet(&send_buff, log_files->name, &position);
- if (stat(log_files->value, &file_stat))
- {
- store_to_protocol_packet(&send_buff, "", &position);
- store_to_protocol_packet(&send_buff, (char*) "0", &position);
- }
- else if (MY_S_ISREG(file_stat.st_mode))
- {
- store_to_protocol_packet(&send_buff,
- (char*) log_files->value,
- &position);
- int10_to_str(file_stat.st_size, buff, 10);
- store_to_protocol_packet(&send_buff, (char*) buff, &position);
- }
-
- if (my_net_write(net, send_buff.buffer, (uint) position))
- goto err;
- }
+ int10_to_str(file_stat.st_size, buff, 10);
+
+ log_path= log_files->value;
+ log_size= buff;
}
- }
- if (send_eof(net) || net_flush(net))
- goto err;
+ if (store_to_protocol_packet(&send_buff, log_files->name, &pos) ||
+ store_to_protocol_packet(&send_buff, log_path, &pos) ||
+ store_to_protocol_packet(&send_buff, log_size, &pos) ||
+ my_net_write(net, send_buff.buffer, pos))
+ return ER_OUT_OF_RESOURCES;
+ }
return 0;
-
-err:
- return ER_OUT_OF_RESOURCES;
}
-/* implementation for SET instance_name.option=option_value: */
+/**************************************************************************
+ Implementation of Abstract_option_cmd.
+**************************************************************************/
+
+/*
+ Instance_options_list -- a data class representing a list of options for
+ some instance.
+*/
-Set_option::Set_option(Instance_map *instance_map_arg,
- const char *name, uint len,
- const char *option_arg, uint option_len_arg,
- const char *option_value_arg, uint option_value_len_arg)
- :Command(instance_map_arg)
+class Instance_options_list
{
- Instance *instance;
+public:
+ Instance_options_list(const LEX_STRING *instance_name_arg);
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- {
- instance_name= instance->options.instance_name;
+public:
+ bool init();
- /* add prefix for add_option */
- if ((option_len_arg < MAX_OPTION_LEN - 1) ||
- (option_value_len_arg < MAX_OPTION_LEN - 1))
- {
- strmake(option, option_arg, option_len_arg);
- strmake(option_value, option_value_arg, option_value_len_arg);
- }
- else
- {
- option[0]= 0;
- option_value[0]= 0;
- }
- instance_name_len= len;
- }
- else
+ const LEX_STRING *get_instance_name() const
{
- instance_name= NULL;
- instance_name_len= 0;
+ return instance_name.get_str();
}
+
+public:
+ /*
+ This member is set and used only in Abstract_option_cmd::execute_impl().
+ Normally it is not used (and should not).
+
+ The problem is that construction and execution of commands are made not
+ in one transaction (not under one lock session). So, we can not initialize
+ instance in constructor and use it in execution.
+ */
+ Instance *instance;
+
+ Named_value_arr options;
+
+private:
+ Instance_name instance_name;
+};
+
+
+/**************************************************************************/
+
+Instance_options_list::Instance_options_list(
+ const LEX_STRING *instance_name_arg)
+ :instance(NULL),
+ instance_name(instance_name_arg)
+{
}
-/*
- The method sends a table with a list of log files
- used by the instance.
+bool Instance_options_list::init()
+{
+ return options.init();
+}
- SYNOPSYS
- Set_option::correct_file()
- skip Skip the option, being searched while writing the result file.
- That is, to delete it.
- DESCRIPTION
+/**************************************************************************/
+
+C_MODE_START
+
+static byte* get_item_key(const byte* item, uint* len,
+ my_bool __attribute__((unused)) t)
+{
+ const Instance_options_list *lst= (const Instance_options_list *) item;
+ *len= lst->get_instance_name()->length;
+ return (byte *) lst->get_instance_name()->str;
+}
+
+static void delete_item(void *item)
+{
+ delete (Instance_options_list *) item;
+}
+
+C_MODE_END
+
+
+/**************************************************************************/
+
+Abstract_option_cmd::Abstract_option_cmd(Instance_map *instance_map_arg)
+ :Command(instance_map_arg),
+ initialized(FALSE)
+{
+}
+
+
+Abstract_option_cmd::~Abstract_option_cmd()
+{
+ if (initialized)
+ hash_free(&instance_options_map);
+}
+
+
+bool Abstract_option_cmd::add_option(const LEX_STRING *instance_name,
+ Named_value *option)
+{
+ Instance_options_list *lst= get_instance_options_list(instance_name);
+ if (!lst)
+ return TRUE;
+
+ lst->options.add_element(option);
+
+ return FALSE;
+}
+
+
+bool Abstract_option_cmd::init(const char **text)
+{
+ static const int INITIAL_HASH_SIZE= 16;
+
+ if (hash_init(&instance_options_map, default_charset_info,
+ INITIAL_HASH_SIZE, 0, 0, get_item_key, delete_item, 0))
+ return TRUE;
+
+ if (parse_args(text))
+ return TRUE;
+
+ initialized= TRUE;
+
+ return FALSE;
+}
+
+
+/*
Correct the option file. The "skip" option is used to remove the found
option.
+ SYNOPSYS
+ Abstract_option_cmd::correct_file()
+ skip Skip the option, being searched while writing the result file.
+ That is, to delete it.
+
RETURN
- ER_OUT_OF_RESOURCES out of resources
+ 0 Success
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
ER_ACCESS_OPTION_FILE Cannot access the option file
- 0 - ok
*/
-int Set_option::correct_file(int skip)
+int Abstract_option_cmd::correct_file(Instance *instance, Named_value *option,
+ bool skip)
+{
+ int err_code= modify_defaults_file(Options::Main::config_file,
+ option->get_name(),
+ option->get_value(),
+ instance->get_name()->str,
+ skip);
+
+ DBUG_ASSERT(err_code >= 0 && err_code <= 2);
+
+ if (err_code)
+ {
+ log_error("Can not modify option (%s) in defaults file (%s). "
+ "Original error code: %d.",
+ (const char *) option->get_name(),
+ (const char *) Options::Main::config_file,
+ (int) err_code);
+ }
+
+ return modify_defaults_to_im_error[err_code];
+}
+
+
+/*
+ Implementation of SET statement.
+
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance with the given name does not exist
+ ER_INCOMPATIBLE_OPTION The specified option can not be set for
+ mysqld-compatible instance
+ ER_INSTANCE_IS_ACTIVE The specified instance is active
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
+
+int Abstract_option_cmd::execute(st_net *net, ulong connection_id)
+{
+ int err_code;
+
+ instance_map->lock();
+
+ err_code= execute_impl(net, connection_id);
+
+ instance_map->unlock();
+
+ return err_code;
+}
+
+
+Instance_options_list *
+Abstract_option_cmd::get_instance_options_list(const LEX_STRING *instance_name)
{
- static const int mysys_to_im_error[]= { 0, ER_OUT_OF_RESOURCES,
- ER_ACCESS_OPTION_FILE };
- int error;
+ Instance_options_list *lst=
+ (Instance_options_list *) hash_search(&instance_options_map,
+ (byte *) instance_name->str,
+ instance_name->length);
+
+ if (!lst)
+ {
+ lst= new Instance_options_list(instance_name);
- error= modify_defaults_file(Options::config_file, option,
- option_value, instance_name, skip);
- DBUG_ASSERT(error >= 0 && error <= 2);
+ if (!lst)
+ return NULL;
- return mysys_to_im_error[error];
+ if (lst->init() || my_hash_insert(&instance_options_map, (byte *) lst))
+ {
+ delete lst;
+ return NULL;
+ }
+ }
+
+ return lst;
+}
+
+
+int Abstract_option_cmd::execute_impl(st_net *net, ulong connection_id)
+{
+ int err_code;
+
+ /* Check that all the specified instances exist and are offline. */
+
+ for (uint i= 0; i < instance_options_map.records; ++i)
+ {
+ Instance_options_list *lst=
+ (Instance_options_list *) hash_element(&instance_options_map, i);
+
+ lst->instance= instance_map->find(lst->get_instance_name());
+
+ if (!lst->instance)
+ return ER_BAD_INSTANCE_NAME;
+
+ if (instance_map->guardian->is_active(lst->instance))
+ return ER_INSTANCE_IS_ACTIVE;
+ }
+
+ /* Perform command-specific (SET/UNSET) actions. */
+
+ for (uint i= 0; i < instance_options_map.records; ++i)
+ {
+ Instance_options_list *lst=
+ (Instance_options_list *) hash_element(&instance_options_map, i);
+
+ for (int j= 0; j < lst->options.get_size(); ++j)
+ {
+ Named_value option= lst->options.get_element(j);
+ err_code= process_option(lst->instance, &option);
+
+ if (err_code)
+ break;
+ }
+
+ if (err_code)
+ break;
+ }
+
+ if (err_code == 0)
+ net_send_ok(net, connection_id, NULL);
+
+ return err_code;
+}
+
+
+/**************************************************************************
+ Implementation of Set_option.
+**************************************************************************/
+
+Set_option::Set_option(Instance_map *instance_map_arg)
+ :Abstract_option_cmd(instance_map_arg)
+{
}
/*
- The method sets an option in the the default config file (/etc/my.cnf).
+ This operation parses SET options.
SYNOPSYS
- Set_option::do_command()
- net The network connection to the client.
+ text [IN/OUT] a pointer to the text containing options.
RETURN
- 0 - ok
- 1 - error occured
+ FALSE On success.
+ TRUE On syntax error.
*/
-int Set_option::do_command(struct st_net *net)
+bool Set_option::parse_args(const char **text)
{
- int error;
+ uint len;
- /* we must hold the instance_map mutex while changing config file */
- instance_map->lock();
- error= correct_file(FALSE);
- instance_map->unlock();
+ /* Check if we have something (and trim leading spaces). */
+
+ get_word(text, &len, NONSPACE);
+
+ if (len == 0)
+ return TRUE; /* Syntax error: no option. */
+
+ /* Main parsing loop. */
+
+ while (TRUE)
+ {
+ LEX_STRING instance_name;
+ LEX_STRING option_name;
+ char *option_name_str;
+ char *option_value_str= NULL;
+
+ /* Looking for instance name. */
+
+ get_word(text, &instance_name.length, ALPHANUM);
+
+ if (instance_name.length == 0)
+ return TRUE; /* Syntax error: instance name expected. */
+
+ instance_name.str= (char *) *text;
+ *text+= instance_name.length;
+
+ skip_spaces(text);
+
+ /* Check the the delimiter is a dot. */
+
+ if (**text != '.')
+ return TRUE; /* Syntax error: dot expected. */
+
+ ++(*text);
+
+ /* Looking for option name. */
+
+ get_word(text, &option_name.length, OPTION_NAME);
+
+ if (option_name.length == 0)
+ return TRUE; /* Syntax error: option name expected. */
+
+ option_name.str= (char *) *text;
+ *text+= option_name.length;
+
+ /* Looking for equal sign. */
+
+ skip_spaces(text);
+
+ if (**text == '=')
+ {
+ ++(*text); /* Skip an equal sign. */
+
+ /* Looking for option value. */
+
+ skip_spaces(text);
+
+ if (!**text)
+ return TRUE; /* Syntax error: EOS when option value expected. */
+
+ if (**text != '\'' && **text != '"')
+ {
+ /* Option value is a simple token. */
+
+ LEX_STRING option_value;
+
+ get_word(text, &option_value.length, ALPHANUM);
+
+ if (option_value.length == 0)
+ return TRUE; /* internal parser error. */
+
+ option_value.str= (char *) *text;
+ *text+= option_value.length;
- return error;
+ if (!(option_value_str= Named_value::alloc_str(&option_value)))
+ return TRUE; /* out of memory during parsing. */
+ }
+ else
+ {
+ /* Option value is a string. */
+
+ if (parse_option_value(*text, &len, &option_value_str))
+ return TRUE; /* Syntax error: invalid string specification. */
+
+ *text+= len;
+ }
+ }
+
+ if (!option_value_str)
+ {
+ LEX_STRING empty_str= { C_STRING_WITH_SIZE("") };
+
+ if (!(option_value_str= Named_value::alloc_str(&empty_str)))
+ return TRUE; /* out of memory during parsing. */
+ }
+
+ if (!(option_name_str= Named_value::alloc_str(&option_name)))
+ {
+ Named_value::free_str(&option_name_str);
+ return TRUE; /* out of memory during parsing. */
+ }
+
+ {
+ Named_value option(option_name_str, option_value_str);
+
+ if (add_option(&instance_name, &option))
+ {
+ option.free();
+ return TRUE; /* out of memory during parsing. */
+ }
+ }
+
+ skip_spaces(text);
+
+ if (!**text)
+ return FALSE; /* OK: end of options. */
+
+ if (**text != ',')
+ return TRUE; /* Syntax error: comma expected. */
+
+ ++(*text); /* Skip a comma. */
+ }
}
-int Set_option::execute(struct st_net *net, ulong connection_id)
+int Set_option::process_option(Instance *instance, Named_value *option)
{
- if (instance_name != NULL)
+ /* Check that the option is valid. */
+
+ if (instance->is_mysqld_compatible() &&
+ Instance_options::is_option_im_specific(option->get_name()))
{
- int val;
+ log_error("Error: IM-option (%s) can not be used "
+ "in the configuration of mysqld-compatible instance (%s).",
+ (const char *) option->get_name(),
+ (const char *) instance->get_name()->str);
+ return ER_INCOMPATIBLE_OPTION;
+ }
- val= do_command(net);
+ /* Update the configuration file. */
- if (val == 0)
- net_send_ok(net, connection_id, NULL);
+ int err_code= correct_file(instance, option, FALSE);
- return val;
- }
+ if (err_code)
+ return err_code;
+
+ /* Update the internal cache. */
- return ER_BAD_INSTANCE_NAME;
+ if (instance->options.set_option(option))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
}
-/* the only function from Unset_option we need to Implement */
+/**************************************************************************
+ Implementation of Unset_option.
+**************************************************************************/
-int Unset_option::do_command(struct st_net *net)
+Unset_option::Unset_option(Instance_map *instance_map_arg)
+ :Abstract_option_cmd(instance_map_arg)
{
- return correct_file(TRUE);
}
-/* Implementation for Stop_instance: */
+/*
+ This operation parses UNSET options.
-Stop_instance::Stop_instance(Instance_map *instance_map_arg,
- const char *name, uint len)
- :Command(instance_map_arg)
+ SYNOPSYS
+ text [IN/OUT] a pointer to the text containing options.
+
+ RETURN
+ FALSE On success.
+ TRUE On syntax error.
+*/
+
+bool Unset_option::parse_args(const char **text)
{
- /* we make a search here, since we don't want to store the name */
- if ((instance= instance_map->find(name, len)))
- instance_name= instance->options.instance_name;
+ uint len;
+
+ /* Check if we have something (and trim leading spaces). */
+
+ get_word(text, &len, NONSPACE);
+
+ if (len == 0)
+ return TRUE; /* Syntax error: no option. */
+
+ /* Main parsing loop. */
+
+ while (TRUE)
+ {
+ LEX_STRING instance_name;
+ LEX_STRING option_name;
+ char *option_name_str;
+ char *option_value_str;
+
+ /* Looking for instance name. */
+
+ get_word(text, &instance_name.length, ALPHANUM);
+
+ if (instance_name.length == 0)
+ return TRUE; /* Syntax error: instance name expected. */
+
+ instance_name.str= (char *) *text;
+ *text+= instance_name.length;
+
+ skip_spaces(text);
+
+ /* Check the the delimiter is a dot. */
+
+ if (**text != '.')
+ return TRUE; /* Syntax error: dot expected. */
+
+ ++(*text); /* Skip a dot. */
+
+ /* Looking for option name. */
+
+ get_word(text, &option_name.length, OPTION_NAME);
+
+ if (option_name.length == 0)
+ return TRUE; /* Syntax error: option name expected. */
+
+ option_name.str= (char *) *text;
+ *text+= option_name.length;
+
+ if (!(option_name_str= Named_value::alloc_str(&option_name)))
+ return TRUE; /* out of memory during parsing. */
+
+ {
+ LEX_STRING empty_str= { C_STRING_WITH_SIZE("") };
+
+ if (!(option_value_str= Named_value::alloc_str(&empty_str)))
+ {
+ Named_value::free_str(&option_name_str);
+ return TRUE;
+ }
+ }
+
+ {
+ Named_value option(option_name_str, option_value_str);
+
+ if (add_option(&instance_name, &option))
+ {
+ option.free();
+ return TRUE; /* out of memory during parsing. */
+ }
+ }
+
+ skip_spaces(text);
+
+ if (!**text)
+ return FALSE; /* OK: end of options. */
+
+ if (**text != ',')
+ return TRUE; /* Syntax error: comma expected. */
+
+ ++(*text); /* Skip a comma. */
+ }
}
-int Stop_instance::execute(struct st_net *net, ulong connection_id)
-{
- uint err_code;
+/*
+ Implementation of UNSET statement.
- if (instance == 0)
- return ER_BAD_INSTANCE_NAME; /* haven't found an instance */
+ Possible error codes:
+ ER_BAD_INSTANCE_NAME The instance name specified is not valid
+ ER_INSTANCE_IS_ACTIVE The specified instance is active
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+*/
- if (!(instance->options.nonguarded))
- instance_map->guardian->stop_guard(instance);
+int Unset_option::process_option(Instance *instance, Named_value *option)
+{
+ /* Update the configuration file. */
- if ((err_code= instance->stop()))
+ int err_code= correct_file(instance, option, TRUE);
+
+ if (err_code)
return err_code;
- net_send_ok(net, connection_id, NULL);
+ /* Update the internal cache. */
+
+ instance->options.unset_option(option->get_name());
+
return 0;
}
-int Syntax_error::execute(struct st_net *net, ulong connection_id)
+/**************************************************************************
+ Implementation of Syntax_error.
+**************************************************************************/
+
+int Syntax_error::execute(st_net *net, ulong connection_id)
{
return ER_SYNTAX_ERROR;
}
diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h
index bfd38d34889..92008e00bfa 100644
--- a/server-tools/instance-manager/commands.h
+++ b/server-tools/instance-manager/commands.h
@@ -16,10 +16,20 @@
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 <hash.h>
+
#include "command.h"
#include "instance.h"
#include "parse.h"
+#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
+#pragma interface
+#endif
+
+struct LEX_STRING;
+
/*
Print all instances of this instance manager.
Grammar: SHOW ISTANCES
@@ -31,12 +41,16 @@ public:
Show_instances(Instance_map *instance_map_arg): Command(instance_map_arg)
{}
- int execute(struct st_net *net, ulong connection_id);
+ int execute(st_net *net, ulong connection_id);
+
+private:
+ int write_header(st_net *net);
+ int write_data(st_net *net);
};
/*
- Reread configuration file and refresh instance map.
+ Reread configuration file and refresh internal cache.
Grammar: FLUSH INSTANCES
*/
@@ -46,7 +60,43 @@ public:
Flush_instances(Instance_map *instance_map_arg): Command(instance_map_arg)
{}
- int execute(struct st_net *net, ulong connection_id);
+ int execute(st_net *net, ulong connection_id);
+};
+
+
+/*
+ Abstract class for Instance-specific commands.
+*/
+
+class Abstract_instance_cmd : public Command
+{
+public:
+ Abstract_instance_cmd(Instance_map *instance_map_arg,
+ const LEX_STRING *instance_name_arg);
+
+public:
+ virtual int execute(st_net *net, ulong connection_id);
+
+protected:
+ /* MT-NOTE: this operation is called under acquired Instance_map'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_map's lock.
+ */
+ virtual int send_ok_response(st_net *net, ulong connection_id) = 0;
+
+protected:
+ inline const LEX_STRING *get_instance_name() const
+ {
+ return instance_name.get_str();
+ }
+
+private:
+ Instance_name instance_name;
};
@@ -55,31 +105,40 @@ public:
Grammar: SHOW ISTANCE STATUS <instance_name>
*/
-class Show_instance_status : public Command
+class Show_instance_status : public Abstract_instance_cmd
{
public:
-
Show_instance_status(Instance_map *instance_map_arg,
- const char *name, uint len);
- int execute(struct st_net *net, ulong connection_id);
- const char *instance_name;
+ 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 if chosen instance.
+ Print options of chosen instance.
Grammar: SHOW INSTANCE OPTIONS <instance_name>
*/
-class Show_instance_options : public Command
+class Show_instance_options : public Abstract_instance_cmd
{
public:
-
Show_instance_options(Instance_map *instance_map_arg,
- const char *name, uint len);
+ const LEX_STRING *instance_name_arg);
- int execute(struct st_net *net, ulong connection_id);
- const char *instance_name;
+protected:
+ virtual int execute_impl(st_net *net, Instance *instance);
+ virtual int send_ok_response(st_net *net, ulong connection_id);
+
+private:
+ int write_header(st_net *net);
+ int write_data(st_net *net, Instance *instance);
};
@@ -88,14 +147,15 @@ public:
Grammar: START INSTANCE <instance_name>
*/
-class Start_instance : public Command
+class Start_instance : public Abstract_instance_cmd
{
public:
- Start_instance(Instance_map *instance_map_arg, const char *name, uint len);
+ Start_instance(Instance_map *instance_map_arg,
+ const LEX_STRING *instance_name_arg);
- int execute(struct st_net *net, ulong connection_id);
- const char *instance_name;
- Instance *instance;
+protected:
+ virtual int execute_impl(st_net *net, Instance *instance);
+ virtual int send_ok_response(st_net *net, ulong connection_id);
};
@@ -104,33 +164,95 @@ public:
Grammar: STOP INSTANCE <instance_name>
*/
-class Stop_instance : public Command
+class Stop_instance : public Abstract_instance_cmd
{
public:
- Stop_instance(Instance_map *instance_map_arg, const char *name, uint len);
+ Stop_instance(Instance_map *instance_map_arg,
+ const LEX_STRING *instance_name_arg);
- Instance *instance;
- int execute(struct st_net *net, ulong connection_id);
- const char *instance_name;
+protected:
+ virtual int execute_impl(st_net *net, Instance *instance);
+ virtual int send_ok_response(st_net *net, ulong connection_id);
};
/*
- Print requested part of the log
+ Create an instance.
+ Grammar: CREATE INSTANCE <instance_name> [<options>]
+*/
+
+class Create_instance : public Command
+{
+public:
+ Create_instance(Instance_map *instance_map_arg,
+ const LEX_STRING *instance_name_arg);
+
+public:
+ bool init(const char **text);
+
+protected:
+ virtual int execute(st_net *net, ulong connection_id);
+
+ inline const LEX_STRING *get_instance_name() const
+ {
+ return instance_name.get_str();
+ }
+
+private:
+ bool parse_args(const char **text);
+
+private:
+ Instance_name instance_name;
+
+ 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 Abstract_instance_cmd
+{
+public:
+ Drop_instance(Instance_map *instance_map_arg,
+ 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);
+};
+
+
+/*
+ Print requested part of the log.
Grammar:
- SHOW <instance_name> log {ERROR | SLOW | GENERAL} size[, offset_from_end]
+ SHOW <instance_name> LOG {ERROR | SLOW | GENERAL} size[, offset_from_end]
*/
-class Show_instance_log : public Command
+class Show_instance_log : public Abstract_instance_cmd
{
public:
+ Show_instance_log(Instance_map *instance_map_arg,
+ 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);
- Show_instance_log(Instance_map *instance_map_arg, const char *name,
- uint len, Log_type log_type_arg, const char *size_arg,
- const char *offset_arg);
- int execute(struct st_net *net, ulong connection_id);
+private:
Log_type log_type;
- const char *instance_name;
uint size;
uint offset;
};
@@ -141,75 +263,112 @@ public:
Grammar: SHOW <instance_name> LOG FILES
*/
-class Show_instance_log_files : public Command
+class Show_instance_log_files : public Abstract_instance_cmd
{
public:
-
Show_instance_log_files(Instance_map *instance_map_arg,
- const char *name, uint len);
- int execute(struct st_net *net, ulong connection_id);
- const char *instance_name;
- const char *option;
+ 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);
};
/*
- Syntax error command. This command is issued if parser reported a syntax
- error. We need it to distinguish the parse error and the situation when
- parser internal error occured. E.g. parsing failed because we hadn't had
- enought memory. In the latter case parse_command() should return an error.
+ Abstract class for option-management commands.
*/
-class Syntax_error : public Command
+class Instance_options_list;
+
+class Abstract_option_cmd : public Command
{
public:
- int execute(struct st_net *net, ulong connection_id);
+ ~Abstract_option_cmd();
+
+public:
+ bool add_option(const LEX_STRING *instance_name, Named_value *option);
+
+public:
+ bool init(const char **text);
+
+ virtual int execute(st_net *net, ulong connection_id);
+
+protected:
+ Abstract_option_cmd(Instance_map *instance_map_arg);
+
+ int correct_file(Instance *instance, Named_value *option, bool skip);
+
+protected:
+ virtual bool parse_args(const char **text) = 0;
+ virtual int process_option(Instance *instance, Named_value *option) = 0;
+
+private:
+ Instance_options_list *
+ get_instance_options_list(const LEX_STRING *instance_name);
+
+ int execute_impl(st_net *net, ulong connection_id);
+
+private:
+ HASH instance_options_map;
+ bool initialized;
};
+
/*
Set an option for the instance.
- Grammar: SET instance_name.option=option_value
+ Grammar: SET instance_name.option[=option_value][, ...]
*/
-class Set_option : public Command
+class Set_option : public Abstract_option_cmd
{
public:
- Set_option(Instance_map *instance_map_arg, const char *name, uint len,
- const char *option_arg, uint option_len,
- const char *option_value_arg, uint option_value_len);
- /*
- the following function is virtual to let Unset_option to use
- */
- virtual int do_command(struct st_net *net);
- int execute(struct st_net *net, ulong connection_id);
+ Set_option(Instance_map *instance_map_arg);
+
protected:
- int correct_file(int skip);
-public:
- const char *instance_name;
- uint instance_name_len;
- /* buffer for the option */
- enum { MAX_OPTION_LEN= 1024 };
- char option[MAX_OPTION_LEN];
- char option_value[MAX_OPTION_LEN];
+ virtual bool parse_args(const char **text);
+ virtual int process_option(Instance *instance, Named_value *option);
};
/*
- Remove option of the instance from config file
- Grammar: UNSET instance_name.option
+ Remove option of the instance.
+ Grammar: UNSET instance_name.option[, ...]
*/
-class Unset_option: public Set_option
+class Unset_option: public Abstract_option_cmd
{
public:
- Unset_option(Instance_map *instance_map_arg, const char *name, uint len,
- const char *option_arg, uint option_len,
- const char *option_value_arg, uint option_value_len):
- Set_option(instance_map_arg, name, len, option_arg, option_len,
- option_value_arg, option_value_len)
- {}
- int do_command(struct st_net *net);
+ Unset_option(Instance_map *instance_map_arg);
+
+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:
+ /* This is just to avoid compiler warning. */
+ Syntax_error() :Command(NULL)
+ {}
+
+public:
+ int execute(st_net *net, ulong connection_id);
+};
+
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H */
diff --git a/server-tools/instance-manager/exit_codes.h b/server-tools/instance-manager/exit_codes.h
new file mode 100644
index 00000000000..560ce30b7aa
--- /dev/null
+++ b/server-tools/instance-manager/exit_codes.h
@@ -0,0 +1,41 @@
+#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; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ This file contains a list of exit codes, which are used when Instance
+ Manager is working in user-management mode.
+*/
+
+const int ERR_OK = 0;
+
+const int ERR_OUT_OF_MEMORY = 1;
+const int ERR_INVALID_USAGE = 2;
+const int ERR_INTERNAL_ERROR = 3;
+const int ERR_IO_ERROR = 4;
+const int ERR_PASSWORD_FILE_CORRUPTED = 5;
+const int ERR_PASSWORD_FILE_DOES_NOT_EXIST = 6;
+
+const int ERR_CAN_NOT_READ_USER_NAME = 10;
+const int ERR_CAN_NOT_READ_PASSWORD = 11;
+const int ERR_USER_ALREADY_EXISTS = 12;
+const int ERR_USER_NOT_FOUND = 13;
+
+#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_EXIT_CODES_H
diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc
index 3be672cd71c..7a9ff3e8367 100644
--- a/server-tools/instance-manager/guardian.cc
+++ b/server-tools/instance-manager/guardian.cc
@@ -21,16 +21,14 @@
#include "guardian.h"
-#include "instance_map.h"
-#include "instance.h"
-#include "mysql_manager_error.h"
-#include "log.h"
-#include "portability.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"
pthread_handler_t guardian(void *arg)
@@ -40,6 +38,37 @@ pthread_handler_t guardian(void *arg)
return 0;
}
+
+const char *
+Guardian_thread::get_instance_state_name(enum_instance_state state)
+{
+ switch (state) {
+ case NOT_STARTED:
+ return "offline";
+
+ 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. */
+}
+
+
Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg,
Instance_map *instance_map_arg,
uint monitoring_interval_arg) :
@@ -89,10 +118,17 @@ void Guardian_thread::process_instance(Instance *instance,
if (current_node->state == STOPPING)
{
/* this brach is executed during shutdown */
- if (instance->options.shutdown_delay_val)
+ if (instance->options.shutdown_delay)
+ {
+ /*
+ 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.
+ */
waitchild= instance->options.shutdown_delay_val;
+ }
- /* this returns true if and only if an instance was stopped for sure */
+ /* this returns TRUE if and only if an instance was stopped for sure */
if (instance->is_crashed())
*guarded_instances= list_delete(*guarded_instances, node);
else if ( (uint) (current_time - current_node->last_checked) > waitchild)
@@ -159,7 +195,11 @@ void Guardian_thread::process_instance(Instance *instance,
instance->options.instance_name);
}
else
+ {
+ log_info("guardian: cannot start instance %s. Abandoning attempts "
+ "to (re)start it", instance->options.instance_name);
current_node->state= CRASHED_AND_ABANDONED;
+ }
}
break;
case CRASHED_AND_ABANDONED:
@@ -242,7 +282,9 @@ int Guardian_thread::is_stopped()
SYNOPSYS
Guardian_thread::init()
- NOTE: One should always lock guardian before calling this routine.
+ NOTE: The operation should be invoked with the following locks acquired:
+ - Guardian_thread;
+ - Instance_map;
RETURN
0 - ok
@@ -261,12 +303,11 @@ int Guardian_thread::init()
while ((instance= iterator.next()))
{
- if (!(instance->options.nonguarded))
- if (guard(instance, TRUE)) /* do not lock guardian */
- {
- instance_map->unlock();
- return 1;
- }
+ if (instance->options.nonguarded)
+ continue;
+
+ if (guard(instance, TRUE)) /* do not lock guardian */
+ return 1;
}
return 0;
@@ -334,24 +375,14 @@ int Guardian_thread::stop_guard(Instance *instance)
LIST *node;
pthread_mutex_lock(&LOCK_guardian);
- node= guarded_instances;
- while (node != NULL)
- {
- /*
- We compare only pointers, as we always use pointers from the
- instance_map's MEM_ROOT.
- */
- if (((GUARD_NODE *) node->data)->instance == instance)
- {
- guarded_instances= list_delete(guarded_instances, node);
- pthread_mutex_unlock(&LOCK_guardian);
- return 0;
- }
- else
- node= node->next;
- }
+ node= find_instance_node(instance);
+
+ if (node != NULL)
+ guarded_instances= list_delete(guarded_instances, node);
+
pthread_mutex_unlock(&LOCK_guardian);
+
/* if there is nothing to delete it is also fine */
return 0;
}
@@ -420,7 +451,7 @@ int Guardian_thread::stop_instances(bool stop_instances_arg)
void Guardian_thread::lock()
{
- pthread_mutex_lock(&LOCK_guardian);
+ pthread_mutex_lock(&LOCK_guardian);
}
@@ -428,3 +459,41 @@ void Guardian_thread::unlock()
{
pthread_mutex_unlock(&LOCK_guardian);
}
+
+
+LIST *Guardian_thread::find_instance_node(Instance *instance)
+{
+ LIST *node= guarded_instances;
+
+ while (node != NULL)
+ {
+ /*
+ We compare only pointers, as we always use pointers from the
+ instance_map's MEM_ROOT.
+ */
+ if (((GUARD_NODE *) node->data)->instance == instance)
+ return node;
+
+ node= node->next;
+ }
+
+ return NULL;
+}
+
+
+bool Guardian_thread::is_active(Instance *instance)
+{
+ bool guarded;
+
+ lock();
+
+ guarded= find_instance_node(instance) != NULL;
+
+ /* is_running() can take a long time, so let's unlock mutex first. */
+ unlock();
+
+ if (guarded)
+ return true;
+
+ return instance->is_running();
+}
diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h
index 16b4c373c91..6d3a2b222d7 100644
--- a/server-tools/instance-manager/guardian.h
+++ b/server-tools/instance-manager/guardian.h
@@ -17,11 +17,11 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
-#include "thread_registry.h"
-
#include <my_sys.h>
#include <my_list.h>
+#include "thread_registry.h"
+
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
@@ -79,6 +79,8 @@ public:
time_t last_checked;
};
+ /* Return client state name. */
+ static const char *get_instance_state_name(enum_instance_state state);
Guardian_thread(Thread_registry &thread_registry_arg,
Instance_map *instance_map_arg,
@@ -94,11 +96,28 @@ public:
int guard(Instance *instance, bool nolock= FALSE);
/* Stop instance protection */
int stop_guard(Instance *instance);
- /* Returns true if guardian thread is stopped */
+ /* Returns TRUE if guardian thread is stopped */
int is_stopped();
void lock();
void unlock();
+ /*
+ Return an internal list node for the given instance if the instance is
+ managed by Guardian. Otherwise, return NULL.
+
+ MT-NOTE: must be called under acquired lock.
+ */
+ LIST *find_instance_node(Instance *instance);
+
+ /* The operation is used to check if the instance is active or not. */
+ bool is_active(Instance *instance);
+
+ /*
+ Return state of the given instance list node. The pointer must specify
+ a valid list node.
+ */
+ inline enum_instance_state get_instance_state(LIST *instance_node);
+
public:
pthread_cond_t COND_guardian;
@@ -108,6 +127,7 @@ private:
/* check instance state and act accordingly */
void process_instance(Instance *instance, GUARD_NODE *current_node,
LIST **guarded_instances, LIST *elem);
+
int stopped;
private:
@@ -115,9 +135,15 @@ private:
Thread_info thread_info;
LIST *guarded_instances;
MEM_ROOT alloc;
- enum { MEM_ROOT_BLOCK_SIZE= 512 };
/* this variable is set to TRUE when we want to stop Guardian thread */
bool shutdown_requested;
};
+
+inline Guardian_thread::enum_instance_state
+Guardian_thread::get_instance_state(LIST *instance_node)
+{
+ return ((GUARD_NODE *) instance_node->data)->state;
+}
+
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */
diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc
index 39381b457ab..456052bf7e5 100644
--- a/server-tools/instance-manager/instance.cc
+++ b/server-tools/instance-manager/instance.cc
@@ -20,18 +20,27 @@
#include "instance.h"
-#include "mysql_manager_error.h"
-#include "log.h"
-#include "instance_map.h"
-#include "priv.h"
-#include "portability.h"
+#include <my_global.h>
+#include <mysql.h>
+
+#include <signal.h>
#ifndef __WIN__
#include <sys/wait.h>
#endif
-#include <my_sys.h>
-#include <signal.h>
-#include <m_string.h>
-#include <mysql.h>
+
+#include "guardian.h"
+#include "instance_map.h"
+#include "log.h"
+#include "mysql_manager_error.h"
+#include "portability.h"
+#include "priv.h"
+
+
+const LEX_STRING
+Instance::DFLT_INSTANCE_NAME= { C_STRING_WITH_SIZE("mysqld") };
+
+static const char * const INSTANCE_NAME_PREFIX= Instance::DFLT_INSTANCE_NAME.str;
+static const int INSTANCE_NAME_PREFIX_LEN= Instance::DFLT_INSTANCE_NAME.length;
static void start_and_monitor_instance(Instance_options *old_instance_options,
@@ -152,7 +161,7 @@ static int start_process(Instance_options *instance_options,
switch (*pi) {
case 0: /* never happens on QNX */
- execv(instance_options->mysqld_path, instance_options->argv);
+ execv(instance_options->mysqld_path.str, instance_options->argv);
/* exec never returns */
exit(1);
case -1:
@@ -180,7 +189,7 @@ static int start_process(Instance_options *instance_options,
char *cmdline= new char[cmdlen];
if (cmdline == NULL)
return 1;
-
+
cmdline[0]= 0;
for (int i= 0; instance_options->argv[i] != 0; i++)
{
@@ -232,9 +241,7 @@ static int start_process(Instance_options *instance_options,
static void start_and_monitor_instance(Instance_options *old_instance_options,
Instance_map *instance_map)
{
- enum { MAX_INSTANCE_NAME_LEN= 512 };
- char instance_name_buff[MAX_INSTANCE_NAME_LEN];
- uint instance_name_len;
+ Instance_name instance_name(&old_instance_options->instance_name);
Instance *current_instance;
My_process_info process_info;
@@ -248,11 +255,8 @@ static void start_and_monitor_instance(Instance_options *old_instance_options,
Save the instance name in the case if Instance object we
are using is destroyed. (E.g. by "FLUSH INSTANCES")
*/
- strmake(instance_name_buff, old_instance_options->instance_name,
- MAX_INSTANCE_NAME_LEN - 1);
- instance_name_len= old_instance_options->instance_name_len;
- log_info("starting instance %s", instance_name_buff);
+ log_info("starting instance %s", (const char *) instance_name.get_c_str());
if (start_process(old_instance_options, &process_info))
{
@@ -266,15 +270,36 @@ static void start_and_monitor_instance(Instance_options *old_instance_options,
/* don't check for return value */
wait_process(&process_info);
- current_instance= instance_map->find(instance_name_buff, instance_name_len);
+ instance_map->lock();
+
+ current_instance= instance_map->find(instance_name.get_str());
if (current_instance)
current_instance->set_crash_flag_n_wake_all();
+ instance_map->unlock();
+
return;
}
+bool Instance::is_name_valid(const LEX_STRING *name)
+{
+ const char *name_suffix= name->str + INSTANCE_NAME_PREFIX_LEN;
+
+ if (strncmp(name->str, INSTANCE_NAME_PREFIX, INSTANCE_NAME_PREFIX_LEN) != 0)
+ return FALSE;
+
+ return *name_suffix == 0 || my_isdigit(default_charset_info, *name_suffix);
+}
+
+
+bool Instance::is_mysqld_compatible_name(const LEX_STRING *name)
+{
+ return strcmp(name->str, INSTANCE_NAME_PREFIX) == 0;
+}
+
+
Instance_map *Instance::get_map()
{
return instance_map;
@@ -309,11 +334,11 @@ int Instance::start()
{
/* clear crash flag */
pthread_mutex_lock(&LOCK_instance);
- crashed= 0;
+ crashed= FALSE;
pthread_mutex_unlock(&LOCK_instance);
- if (!is_running())
+ if (configured && !is_running())
{
remove_pid();
@@ -339,8 +364,8 @@ int Instance::start()
return 0;
}
- /* the instance is started already */
- return ER_INSTANCE_ALREADY_STARTED;
+ /* The instance is started already or misconfigured. */
+ return configured ? ER_INSTANCE_ALREADY_STARTED : ER_INSTANCE_MISCONFIGURED;
}
/*
@@ -363,7 +388,7 @@ void Instance::set_crash_flag_n_wake_all()
{
/* set instance state to crashed */
pthread_mutex_lock(&LOCK_instance);
- crashed= 1;
+ crashed= TRUE;
pthread_mutex_unlock(&LOCK_instance);
/*
@@ -378,7 +403,7 @@ void Instance::set_crash_flag_n_wake_all()
-Instance::Instance(): crashed(0)
+Instance::Instance(): crashed(FALSE), configured(FALSE)
{
pthread_mutex_init(&LOCK_instance, 0);
pthread_cond_init(&COND_instance_stopped, 0);
@@ -392,9 +417,9 @@ Instance::~Instance()
}
-int Instance::is_crashed()
+bool Instance::is_crashed()
{
- int val;
+ bool val;
pthread_mutex_lock(&LOCK_instance);
val= crashed;
pthread_mutex_unlock(&LOCK_instance);
@@ -413,10 +438,17 @@ bool Instance::is_running()
bool return_val;
if (options.mysqld_port)
+ {
+ /*
+ 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.
+ */
port= options.mysqld_port_val;
+ }
if (options.mysqld_socket)
- socket= strchr(options.mysqld_socket, '=') + 1;
+ socket= options.mysqld_socket;
/* no port was specified => instance falled back to default value */
if (!options.mysqld_port && !options.mysqld_socket)
@@ -469,8 +501,15 @@ int Instance::stop()
struct timespec timeout;
uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY;
- if (options.shutdown_delay_val)
+ if (options.shutdown_delay)
+ {
+ /*
+ 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.
+ */
waitchild= options.shutdown_delay_val;
+ }
kill_instance(SIGTERM);
/* sleep on condition to wait for SIGCHLD */
@@ -588,20 +627,33 @@ void Instance::kill_instance(int signum)
}
/*
- We execute this function to initialize instance parameters.
- Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY.
+ Initialize instance parameters.
+
+ SYNOPSYS
+ Instance::init()
+ name_arg name of the instance
+
+ RETURN:
+ 0 ok
+ !0 error
*/
-int Instance::init(const char *name_arg)
+int Instance::init(const LEX_STRING *name_arg)
{
+ mysqld_compatible= is_mysqld_compatible_name(name_arg);
+
return options.init(name_arg);
}
int Instance::complete_initialization(Instance_map *instance_map_arg,
- const char *mysqld_path,
- uint instance_type)
+ const char *mysqld_path)
{
instance_map= instance_map_arg;
- return options.complete_initialization(mysqld_path, instance_type);
+ configured= !options.complete_initialization(mysqld_path);
+ return 0;
+ /*
+ TODO: return actual status (from
+ Instance_options::complete_initialization()) here.
+ */
}
diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h
index adb66991685..1f06cabebf7 100644
--- a/server-tools/instance-manager/instance.h
+++ b/server-tools/instance-manager/instance.h
@@ -17,7 +17,10 @@
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
@@ -25,31 +28,120 @@
class Instance_map;
+
+/*
+ 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:
+ /*
+ The following two constants defines name of the default mysqld-instance
+ ("mysqld").
+ */
+ static const LEX_STRING DFLT_INSTANCE_NAME;
+
+public:
+ /*
+ The operation is intended to check whether string is a well-formed
+ instance name or not.
+ */
+ static bool is_name_valid(const LEX_STRING *name);
+
+ /*
+ The operation is intended to check if the given instance name is
+ mysqld-compatible or not.
+ */
+ static bool is_mysqld_compatible_name(const LEX_STRING *name);
+
+public:
Instance();
~Instance();
- int init(const char *name);
+ int init(const LEX_STRING *name_arg);
int complete_initialization(Instance_map *instance_map_arg,
- const char *mysqld_path, uint instance_type);
+ const char *mysqld_path);
bool is_running();
int start();
int stop();
/* send a signal to the instance */
void kill_instance(int signo);
- int is_crashed();
+ bool is_crashed();
void set_crash_flag_n_wake_all();
Instance_map *get_map();
+ /*
+ 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;
+
+ inline const LEX_STRING *get_name() const;
+
public:
enum { DEFAULT_SHUTDOWN_DELAY= 35 };
Instance_options options;
private:
- int crashed;
+ /* This attributes is a flag, specifies if the instance has been crashed. */
+ bool crashed;
+
+ /*
+ This attribute specifies if the instance is configured properly or not.
+ Misconfigured instances are not managed.
+ */
+ bool configured;
+
+ /*
+ This attribute 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. Currently we use it to avoid the
double start of the instance. This happens when the instance is starting
@@ -66,4 +158,22 @@ private:
void remove_pid();
};
+
+inline bool Instance::is_mysqld_compatible() const
+{
+ return mysqld_compatible;
+}
+
+
+inline bool Instance::is_configured() const
+{
+ return configured;
+}
+
+
+inline const LEX_STRING *Instance::get_name() const
+{
+ return &options.instance_name;
+}
+
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */
diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc
index 543c9c31bfa..abe8f6e7dab 100644
--- a/server-tools/instance-manager/instance_map.cc
+++ b/server-tools/instance-manager/instance_map.cc
@@ -20,14 +20,20 @@
#include "instance_map.h"
+#include <my_global.h>
+#include <m_ctype.h>
+#include <mysql_com.h>
+#include <m_string.h>
+
#include "buffer.h"
+#include "guardian.h"
#include "instance.h"
#include "log.h"
+#include "manager.h"
+#include "mysqld_error.h"
+#include "mysql_manager_error.h"
#include "options.h"
-
-#include <m_ctype.h>
-#include <mysql_com.h>
-#include <m_string.h>
+#include "priv.h"
/*
Note: As we are going to suppost different types of connections,
@@ -45,8 +51,8 @@ static byte* get_instance_key(const byte* u, uint* len,
my_bool __attribute__((unused)) t)
{
const Instance *instance= (const Instance *) u;
- *len= instance->options.instance_name_len;
- return (byte *) instance->options.instance_name;
+ *len= instance->options.instance_name.length;
+ return (byte *) instance->options.instance_name.str;
}
static void delete_instance(void *u)
@@ -79,16 +85,60 @@ static void delete_instance(void *u)
static int process_option(void *ctx, const char *group, const char *option)
{
- Instance_map *map= NULL;
+ Instance_map *map= (Instance_map*) ctx;
+ LEX_STRING group_str;
+
+ group_str.str= (char *) group;
+ group_str.length= strlen(group);
- map = (Instance_map*) ctx;
- return map->process_one_option(group, option);
+ return map->process_one_option(&group_str, option);
}
C_MODE_END
/*
+ Parse option string.
+
+ SYNOPSIS
+ parse_option()
+ option_str [IN] option string (e.g. "--name=value")
+ option_name_buf [OUT] parsed name of the option.
+ Must be of (MAX_OPTION_LEN + 1) size.
+ option_value_buf [OUT] parsed value of the option.
+ Must be of (MAX_OPTION_LEN + 1) size.
+
+ DESCRIPTION
+ This is an auxiliary function and should not be used externally. It is
+ intended to parse whole option string into option name and option value.
+*/
+
+static void parse_option(const char *option_str,
+ char *option_name_buf,
+ char *option_value_buf)
+{
+ 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
@@ -103,34 +153,64 @@ C_MODE_END
of the instance map object.
*/
-int Instance_map::process_one_option(const char *group, const char *option)
+int Instance_map::process_one_option(const LEX_STRING *group,
+ const char *option)
{
Instance *instance= NULL;
- static const char prefix[]= { 'm', 'y', 's', 'q', 'l', 'd' };
- if (strncmp(group, prefix, sizeof prefix) == 0 &&
- ((my_isdigit(default_charset_info, group[sizeof prefix]))
- || group[sizeof(prefix)] == '\0'))
+ if (!Instance::is_name_valid(group))
+ {
+ /*
+ Current section name is not a valid instance name.
+ We should skip it w/o error.
+ */
+ return 0;
+ }
+
+ if (!(instance= (Instance *) hash_search(&hash, (byte *) group->str,
+ group->length)))
+ {
+ if (!(instance= new Instance()))
+ return 1;
+
+ if (instance->init(group) || add_instance(instance))
{
- if (!(instance= (Instance *) hash_search(&hash, (byte *) group,
- strlen(group))))
- {
- if (!(instance= new Instance))
- goto err;
- if (instance->init(group) || my_hash_insert(&hash, (byte *) instance))
- goto err_instance;
- }
-
- if (instance->options.add_option(option))
- goto err; /* the instance'll be deleted when we destroy the map */
+ delete instance;
+ return 1;
}
- return 0;
+ if (instance->is_mysqld_compatible())
+ log_info("Warning: instance name '%s' is mysqld-compatible.",
+ (const char *) group->str);
-err_instance:
- delete instance;
-err:
- return 1;
+ log_info("mysqld instance '%s' has been added successfully.",
+ (const char *) group->str);
+ }
+
+ if (option)
+ {
+ char option_name[MAX_OPTION_LEN + 1];
+ char option_value[MAX_OPTION_LEN + 1];
+
+ parse_option(option, option_name, option_value);
+
+ if (instance->is_mysqld_compatible() &&
+ Instance_options::is_option_im_specific(option_name))
+ {
+ log_info("Warning: configuration of mysqld-compatible instance '%s' "
+ "contains IM-specific option '%s'. "
+ "This breaks backward compatibility for the configuration file.",
+ (const char *) group->str,
+ (const char *) option_name);
+ }
+
+ Named_value option(option_name, option_value);
+
+ if (instance->options.set_option(&option))
+ return 1; /* the instance'll be deleted when we destroy the map */
+ }
+
+ return 0;
}
@@ -181,7 +261,7 @@ void Instance_map::unlock()
- 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
+ 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
@@ -194,9 +274,9 @@ void Instance_map::unlock()
In order to avoid such side effects one should never call
FLUSH INSTANCES without prior stop of all running instances.
- TODO
- FLUSH INSTANCES should return an error if it's called
- while there is a running instance.
+ NOTE: The operation should be invoked with the following locks acquired:
+ - Guardian_thread;
+ - Instance_map;
*/
int Instance_map::flush_instances()
@@ -209,67 +289,169 @@ int Instance_map::flush_instances()
guardian (2) reload the instance map (3) reinitialize the guardian
with new instances.
*/
- guardian->lock();
- pthread_mutex_lock(&LOCK_instance_map);
hash_free(&hash);
hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
get_instance_key, delete_instance, 0);
+
rc= load();
guardian->init();
- pthread_mutex_unlock(&LOCK_instance_map);
- guardian->unlock();
return rc;
}
-Instance *
-Instance_map::find(const char *name, uint name_len)
+bool Instance_map::is_there_active_instance()
{
Instance *instance;
- pthread_mutex_lock(&LOCK_instance_map);
- instance= (Instance *) hash_search(&hash, (byte *) name, name_len);
- pthread_mutex_unlock(&LOCK_instance_map);
- return instance;
+ Iterator iterator(this);
+
+ while ((instance= iterator.next()))
+ {
+ if (guardian->find_instance_node(instance) != NULL ||
+ instance->is_running())
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
}
-int Instance_map::complete_initialization()
+int Instance_map::add_instance(Instance *instance)
{
- Instance *instance;
- uint i= 0;
+ return my_hash_insert(&hash, (byte *) instance);
+}
- if (hash.records == 0) /* no instances found */
- {
- if ((instance= new Instance) == 0)
- goto err;
+int Instance_map::remove_instance(Instance *instance)
+{
+ return hash_delete(&hash, (byte *) instance);
+}
- if (instance->init("mysqld") || my_hash_insert(&hash, (byte *) instance))
- goto err_instance;
- /*
- After an instance have been added to the instance_map,
- hash_free should handle it's deletion => goto err, not
- err_instance.
- */
- if (instance->complete_initialization(this, mysqld_path,
- DEFAULT_SINGLE_INSTANCE))
- goto err;
+int Instance_map::create_instance(const LEX_STRING *instance_name,
+ const Named_value_arr *options)
+{
+ Instance *instance= new Instance();
+
+ if (!instance)
+ {
+ log_error("Error: can not initialize (name: '%s').",
+ (const char *) instance_name->str);
+ return ER_OUT_OF_RESOURCES;
}
- else
- while (i < hash.records)
+
+ if (instance->init(instance_name))
+ {
+ log_error("Error: can not initialize (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()))
{
- instance= (Instance *) hash_element(&hash, i);
- if (instance->complete_initialization(this, mysqld_path, USUAL_INSTANCE))
- goto err;
- i++;
+ log_error("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(this, mysqld_path))
+ {
+ log_error("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("Error: can not register instance (name: '%s').",
+ (const char *) instance_name->str);
+ delete instance;
+ return ER_OUT_OF_RESOURCES;
+ }
+
return 0;
-err_instance:
- delete instance;
-err:
- return 1;
+}
+
+
+Instance * Instance_map::find(const LEX_STRING *name)
+{
+ return (Instance *) hash_search(&hash, (byte *) name->str, name->length);
+}
+
+
+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(this, mysqld_path))
+ 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("Error: could 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("Error: could 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;
}
@@ -297,10 +479,10 @@ int Instance_map::load()
name and start looking for files named "my.cnf.cnf" in all
default dirs. Which is not what we want.
*/
- if (Options::is_forced_default_file)
+ if (Options::Main::is_forced_default_file)
{
snprintf(defaults_file_arg, FN_REFLEN, "--defaults-file=%s",
- Options::config_file);
+ Options::Main::config_file);
argv_options[1]= defaults_file_arg;
argv_options[2]= '\0';
@@ -314,15 +496,12 @@ int Instance_map::load()
If the routine failed, we'll simply fallback to defaults in
complete_initialization().
*/
- if (my_search_option_files(Options::config_file, &argc,
+ if (my_search_option_files(Options::Main::config_file, &argc,
(char ***) &argv, &args_used,
process_option, (void*) this))
log_info("Falling back to compiled-in defaults");
- if (complete_initialization())
- return 1;
-
- return 0;
+ return complete_initialization();
}
@@ -343,3 +522,105 @@ Instance *Instance_map::Iterator::next()
return NULL;
}
+
+const char *Instance_map::get_instance_state_name(Instance *instance)
+{
+ LIST *instance_node;
+
+ if (!instance->is_configured())
+ return "misconfigured";
+
+ if ((instance_node= guardian->find_instance_node(instance)) != NULL)
+ {
+ /* The instance is managed by Guardian: we can report precise state. */
+
+ return Guardian_thread::get_instance_state_name(
+ guardian->get_instance_state(instance_node));
+ }
+
+ /* The instance is not managed by Guardian: we can report status only. */
+
+ return instance->is_running() ? "online" : "offline";
+}
+
+
+/*
+ Create a new configuration section for mysqld-instance in the config file.
+
+ SYNOPSYS
+ 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("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("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, (byte*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP)) ||
+ my_write(cnf_file, (byte*)"[", 1, MYF(MY_NABP)) ||
+ my_write(cnf_file, (byte*)instance_name->str, instance_name->length,
+ MYF(MY_NABP)) ||
+ my_write(cnf_file, (byte*)"]", 1, MYF(MY_NABP)) ||
+ my_write(cnf_file, (byte*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP)))
+ {
+ log_error("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, (byte*)option_str, option_str_len, MYF(MY_NABP)) ||
+ my_write(cnf_file, (byte*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP)))
+ {
+ log_error("Error: can not write to configuration file (%s): %s.",
+ (const char *) Options::Main::config_file,
+ (const char *) strerror(errno));
+ my_close(cnf_file, MYF(0));
+ return ER_ACCESS_OPTION_FILE;
+ }
+ }
+
+ my_close(cnf_file, MYF(0));
+
+ return 0;
+}
diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h
index d3de42f4d80..04e617aebe4 100644
--- a/server-tools/instance-manager/instance_map.h
+++ b/server-tools/instance-manager/instance_map.h
@@ -17,10 +17,6 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
-
-#include "protocol.h"
-#include "guardian.h"
-
#include <my_sys.h>
#include <hash.h>
@@ -28,10 +24,18 @@
#pragma interface
#endif
+struct LEX_STRING;
+
+class Guardian_thread;
class Instance;
+class Named_value_arr;
+
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
@@ -56,22 +60,64 @@ public:
};
friend class Iterator;
public:
- /* returns a pointer to the instance or NULL, if there is no such instance */
- Instance *find(const char *name, uint name_len);
+ /*
+ Return a pointer to the instance or NULL, if there is no such instance.
+ MT-NOTE: must be called under acquired lock.
+ */
+ Instance *find(const LEX_STRING *name);
+ /* Clear the configuration cache and reload the configuration file. */
int flush_instances();
+
+ /* The operation is used to check if there is an active instance or not. */
+ bool is_there_active_instance();
+
void lock();
void unlock();
+
int init();
+
/*
Process a given option and assign it to appropricate instance. This is
required for the option handler, passed to my_search_option_files().
*/
- int process_one_option(const char *group, const char *option);
+ int process_one_option(const LEX_STRING *group, const char *option);
+
+ /*
+ Add an instance into the internal hash.
+
+ MT-NOTE: the operation must be called under acquired lock.
+ */
+ int add_instance(Instance *instance);
+
+ /*
+ Remove instance from the internal hash.
+
+ MT-NOTE: the operation must be called under acquired lock.
+ */
+ int remove_instance(Instance *instance);
+
+ /*
+ Create a new instance and register it in the internal hash.
+
+ MT-NOTE: the operation must be called under acquired lock.
+ */
+ int create_instance(const LEX_STRING *instance_name,
+ const Named_value_arr *options);
Instance_map(const char *default_mysqld_path_arg);
~Instance_map();
+ /*
+ Retrieve client state name of the given instance.
+
+ MT-NOTE: the options must be called under acquired locks of the following
+ objects:
+ - Instance_map;
+ - Guardian_thread;
+ */
+ const char *get_instance_state_name(Instance *instance);
+
public:
const char *mysqld_path;
Guardian_thread *guardian;
@@ -80,7 +126,7 @@ private:
/* loads options from config files */
int load();
/* inits instances argv's after all options have been loaded */
- int complete_initialization();
+ bool complete_initialization();
private:
enum { START_HASH_SIZE = 16 };
pthread_mutex_t LOCK_instance_map;
diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc
index 06a6e1ebc63..394efaf0568 100644
--- a/server-tools/instance-manager/instance_options.cc
+++ b/server-tools/instance-manager/instance_options.cc
@@ -20,27 +20,24 @@
#include "instance_options.h"
-#include "parse_output.h"
-#include "buffer.h"
-
+#include <my_global.h>
#include <my_sys.h>
-#include <signal.h>
#include <m_string.h>
-#ifdef __WIN__
-#define NEWLINE_LEN 2
-#else
-#define NEWLINE_LEN 1
-#endif
+#include <signal.h>
+
+#include "buffer.h"
+#include "instance.h"
+#include "log.h"
+#include "parse_output.h"
+#include "priv.h"
/* Create "mysqld ..." command in the buffer */
static inline int create_mysqld_command(Buffer *buf,
- const char *mysqld_path_str,
- uint mysqld_path_len,
- const char *option,
- uint option_len)
+ const LEX_STRING *mysqld_path,
+ const LEX_STRING *option)
{
int position= 0;
@@ -49,13 +46,13 @@ static inline int create_mysqld_command(Buffer *buf,
#ifdef __WIN__
buf->append(position++, "\"", 1);
#endif
- buf->append(position, mysqld_path_str, mysqld_path_len);
- position+= mysqld_path_len;
+ buf->append(position, mysqld_path->str, mysqld_path->length);
+ position+= mysqld_path->length;
#ifdef __WIN__
buf->append(position++, "\"", 1);
#endif
/* here the '\0' character is copied from the option string */
- buf->append(position, option, option_len);
+ buf->append(position, option->str, option->length + 1);
return buf->is_error();
}
@@ -63,6 +60,39 @@ static inline int create_mysqld_command(Buffer *buf,
}
+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), mysqld_port(NULL), mysqld_port_val(0),
+ nonguarded(NULL), shutdown_delay(NULL), shutdown_delay_val(0),
+ filled_default_options(0)
+{
+ mysqld_path.str= NULL;
+ mysqld_path.length= 0;
+
+ memset(logs, 0, sizeof(logs));
+}
+
+
/*
Get compiled-in value of default_option
@@ -86,13 +116,13 @@ int Instance_options::get_default_option(char *result, size_t result_len,
const char *option_name)
{
int rc= 1;
- char verbose_option[]= " --no-defaults --verbose --help";
+ LEX_STRING verbose_option=
+ { C_STRING_WITH_SIZE(" --no-defaults --verbose --help") };
- /* reserve space fot the path + option + final '\0' */
- Buffer cmd(mysqld_path_len + sizeof(verbose_option));
+ /* reserve space for the path + option + final '\0' */
+ Buffer cmd(mysqld_path.length + verbose_option.length + 1);
- if (create_mysqld_command(&cmd, mysqld_path, mysqld_path_len,
- verbose_option, sizeof(verbose_option)))
+ if (create_mysqld_command(&cmd, &mysqld_path, &verbose_option))
goto err;
/* +2 eats first "--" from the option string (E.g. "--datadir") */
@@ -120,21 +150,19 @@ err:
int Instance_options::fill_instance_version()
{
- enum { MAX_VERSION_STRING_LENGTH= 160 };
- char result[MAX_VERSION_STRING_LENGTH];
- char version_option[]= " --no-defaults --version";
+ char result[MAX_VERSION_LENGTH];
+ LEX_STRING version_option=
+ { C_STRING_WITH_SIZE(" --no-defaults --version") };
int rc= 1;
- Buffer cmd(mysqld_path_len + sizeof(version_option));
+ Buffer cmd(mysqld_path.length + version_option.length + 1);
- if (create_mysqld_command(&cmd, mysqld_path, mysqld_path_len,
- version_option, sizeof(version_option)))
+ if (create_mysqld_command(&cmd, &mysqld_path, &version_option))
goto err;
- bzero(result, MAX_VERSION_STRING_LENGTH);
+ bzero(result, MAX_VERSION_LENGTH);
- rc= parse_output_and_get_value(cmd.buffer, "Ver",
- result, MAX_VERSION_STRING_LENGTH,
- GET_LINE);
+ rc= parse_output_and_get_value(cmd.buffer, "Ver", result,
+ MAX_VERSION_LENGTH, GET_LINE);
if (*result != '\0')
{
@@ -145,6 +173,7 @@ int Instance_options::fill_instance_version()
start= result;
while (my_isspace(default_charset_info, *start))
++start;
+
mysqld_version= strdup_root(&alloc, start);
}
err:
@@ -200,8 +229,7 @@ int Instance_options::fill_log_options()
else
{
/* below is safe, as --datadir always has a value */
- strmake(datadir,
- strchr(mysqld_datadir, '=') + 1, MAX_LOG_OPTION_LENGTH - 1);
+ strmake(datadir, mysqld_datadir, MAX_LOG_OPTION_LENGTH - 1);
}
if (gethostname(hostname,sizeof(hostname)-1) < 0)
@@ -287,7 +315,6 @@ err:
int Instance_options::get_pid_filename(char *result)
{
- const char *pid_file= mysqld_pid_file;
char datadir[MAX_PATH_LEN];
if (mysqld_datadir == NULL)
@@ -297,14 +324,10 @@ int Instance_options::get_pid_filename(char *result)
return 1;
}
else
- strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1,
- "/", NullS);
-
- DBUG_ASSERT(mysqld_pid_file);
- pid_file= strchr(pid_file, '=') + 1;
+ strxnmov(datadir, MAX_PATH_LEN - 1, mysqld_datadir, "/", NullS);
/* get the full path to the pidfile */
- my_load_path(result, pid_file, datadir);
+ my_load_path(result, mysqld_pid_file, datadir);
return 0;
}
@@ -333,23 +356,22 @@ pid_t Instance_options::get_pid()
}
-int Instance_options::complete_initialization(const char *default_path,
- uint instance_type)
+int Instance_options::complete_initialization(const char *default_path)
{
const char *tmp;
char *end;
- if (!mysqld_path && !(mysqld_path= strdup_root(&alloc, default_path)))
+ if (!mysqld_path.str && !(mysqld_path.str= strdup_root(&alloc, default_path)))
goto err;
// it's safe to cast this to char* since this is a buffer we are allocating
- end= convert_dirname((char*)mysqld_path, mysqld_path, NullS);
+ end= convert_dirname((char*)mysqld_path.str, mysqld_path.str, NullS);
end[-1]= 0;
- mysqld_path_len= strlen(mysqld_path);
+ mysqld_path.length= strlen(mysqld_path.str);
if (mysqld_port)
- mysqld_port_val= atoi(strchr(mysqld_port, '=') + 1);
+ mysqld_port_val= atoi(mysqld_port);
if (shutdown_delay)
shutdown_delay_val= atoi(shutdown_delay);
@@ -357,7 +379,7 @@ int Instance_options::complete_initialization(const char *default_path,
if (!(tmp= strdup_root(&alloc, "--no-defaults")))
goto err;
- if (!(mysqld_pid_file))
+ if (!mysqld_pid_file)
{
char pidfilename[MAX_PATH_LEN];
char hostname[MAX_PATH_LEN];
@@ -366,26 +388,27 @@ int Instance_options::complete_initialization(const char *default_path,
If we created only one istance [mysqld], because no config. files were
found, we would like to model mysqld pid file values.
*/
+
if (!gethostname(hostname, sizeof(hostname) - 1))
{
- if (instance_type & DEFAULT_SINGLE_INSTANCE)
- strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname,
- ".pid", NullS);
+ if (Instance::is_mysqld_compatible_name(&instance_name))
+ strxnmov(pidfilename, MAX_PATH_LEN - 1, hostname, ".pid", NullS);
else
- strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name,
- "-", hostname, ".pid", NullS);
+ strxnmov(pidfilename, MAX_PATH_LEN - 1, instance_name.str, "-",
+ hostname, ".pid", NullS);
}
else
{
- if (instance_type & DEFAULT_SINGLE_INSTANCE)
- strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", "mysql",
- ".pid", NullS);
+ if (Instance::is_mysqld_compatible_name(&instance_name))
+ strxnmov(pidfilename, MAX_PATH_LEN - 1, "mysql", ".pid", NullS);
else
- strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name,
- ".pid", NullS);
+ strxnmov(pidfilename, MAX_PATH_LEN - 1, instance_name.str, ".pid",
+ NullS);
}
- add_option(pidfilename);
+ Named_value option((char *) "pid-file", pidfilename);
+
+ set_option(&option);
}
if (get_pid_filename(pid_file_with_path))
@@ -393,20 +416,37 @@ int Instance_options::complete_initialization(const char *default_path,
/* we need to reserve space for the final zero + possible default options */
if (!(argv= (char**)
- alloc_root(&alloc, (options_array.elements + 1
+ alloc_root(&alloc, (get_num_options() + 1
+ MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*))))
goto err;
+ filled_default_options= 0;
/* the path must be first in the argv */
- if (add_to_argv(mysqld_path))
+ if (add_to_argv(mysqld_path.str))
goto err;
if (add_to_argv(tmp))
goto err;
- memcpy((gptr) (argv + filled_default_options), options_array.buffer,
- options_array.elements*sizeof(char*));
- argv[filled_default_options + options_array.elements]= 0;
+ int 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_instance_version())
goto err;
@@ -418,75 +458,91 @@ err:
}
-/*
- Assigns given value to the appropriate option from the class.
+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;
- SYNOPSYS
- add_option()
- option string with the option prefixed by --
+ if (!(option_name_str= Named_value::alloc_str(option->get_name())))
+ return TRUE;
- DESCRIPTION
+ if (!(option_value_str= Named_value::alloc_str(option->get_value())))
+ {
+ Named_value::free_str(&option_name_str);
+ return TRUE;
+ }
- The method is called from the option handling routine.
+ Named_value option_copy(option_name_str, option_value_str);
- RETURN
- 0 - ok
- 1 - error occured
-*/
+ if (idx < 0)
+ err_status= options.add_element(&option_copy);
+ else
+ err_status= options.replace_element(idx, &option_copy);
-int Instance_options::add_option(const char* option)
+ 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)
{
- char *tmp;
- enum { SAVE_VALUE= 1, SAVE_WHOLE, SAVE_WHOLE_AND_ADD };
- struct selected_options_st
+ 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 length;
- const char **value;
- uint type;
- } options[]=
+ uint name_len;
+ const char **var;
+ } options_def[]=
{
- {"--socket=", 9, &mysqld_socket, SAVE_WHOLE_AND_ADD},
- {"--port=", 7, &mysqld_port, SAVE_WHOLE_AND_ADD},
- {"--datadir=", 10, &mysqld_datadir, SAVE_WHOLE_AND_ADD},
- {"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD},
- {"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD},
- {"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE},
- {"--nonguarded", 9, &nonguarded, SAVE_WHOLE},
- {"--shutdown_delay", 9, &shutdown_delay, SAVE_VALUE},
- {NULL, 0, NULL, 0}
+ {"socket", 6, &mysqld_socket},
+ {"port", 4, &mysqld_port},
+ {"datadir", 7, &mysqld_datadir},
+ {"pid-file", 8, &mysqld_pid_file},
+ {"nonguarded", 10, &nonguarded},
+ {"mysqld-path", 11, (const char **) &mysqld_path.str},
+ {"shutdown-delay", 14, &shutdown_delay},
+ {NULL, 0, NULL}
};
- struct selected_options_st *selected_options;
- if (!(tmp= strdup_root(&alloc, option)))
- goto err;
+ for (options_st *opt= options_def; opt->name; ++opt)
+ {
+ if (!strncmp(opt->name, option_name, opt->name_len))
+ {
+ *(opt->var)= option_value;
+ break;
+ }
+ }
+}
- for (selected_options= options; selected_options->name; selected_options++)
- {
- if (strncmp(tmp, selected_options->name, selected_options->length) == 0)
- switch (selected_options->type) {
- case SAVE_WHOLE_AND_ADD:
- *(selected_options->value)= tmp;
- insert_dynamic(&options_array,(gptr) &tmp);
- return 0;
- case SAVE_VALUE:
- *(selected_options->value)= strchr(tmp, '=') + 1;
- return 0;
- case SAVE_WHOLE:
- *(selected_options->value)= tmp;
- return 0;
- default:
- break;
- }
- }
-
- /* if we haven't returned earlier we should just save the option */
- insert_dynamic(&options_array,(gptr) &tmp);
- return 0;
+int Instance_options::find_option(const char *option_name)
+{
+ for (int i= 0; i < get_num_options(); i++)
+ {
+ if (!strcmp(get_option(i).get_name(), option_name))
+ return i;
+ }
-err:
- return 1;
+ return -1;
}
@@ -504,7 +560,10 @@ int Instance_options::add_to_argv(const char* option)
void Instance_options::print_argv()
{
int i;
- printf("printing out an instance %s argv:\n", instance_name);
+
+ printf("printing out an instance %s argv:\n",
+ (const char *) instance_name.str);
+
for (i=0; argv[i] != NULL; i++)
printf("argv: %s\n", argv[i]);
}
@@ -515,17 +574,17 @@ void Instance_options::print_argv()
Return value: 0 - ok. 1 - unable to allocate memory.
*/
-int Instance_options::init(const char *instance_name_arg)
+int Instance_options::init(const LEX_STRING *instance_name_arg)
{
- instance_name_len= strlen(instance_name_arg);
+ instance_name.length= instance_name_arg->length;
init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
- if (my_init_dynamic_array(&options_array, sizeof(char*), 0, 32))
+ if (options.init())
goto err;
- if (!(instance_name= strmake_root(&alloc, (char*) instance_name_arg,
- instance_name_len)))
+ if (!(instance_name.str= strmake_root(&alloc, instance_name_arg->str,
+ instance_name_arg->length)))
goto err;
return 0;
@@ -538,6 +597,4 @@ err:
Instance_options::~Instance_options()
{
free_root(&alloc, MYF(0));
- delete_dynamic(&options_array);
}
-
diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h
index dae1c2695d1..16e548f7324 100644
--- a/server-tools/instance-manager/instance_options.h
+++ b/server-tools/instance-manager/instance_options.h
@@ -18,8 +18,8 @@
#include <my_global.h>
#include <my_sys.h>
+
#include "parse.h"
-#include "portability.h"
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
@@ -35,24 +35,26 @@
don't have to synchronize between threads.
*/
-#define USUAL_INSTANCE 0
-#define DEFAULT_SINGLE_INSTANCE 1
-
class Instance_options
{
public:
- Instance_options() :
- mysqld_version(0), mysqld_socket(0), mysqld_datadir(0),
- mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0),
- mysqld_port_val(0), mysqld_path(0), nonguarded(0), shutdown_delay(0),
- shutdown_delay_val(0), filled_default_options(0)
- {}
+ /* The operation is used to check if the option is IM-specific or not. */
+ static bool is_option_im_specific(const char *option_name);
+
+public:
+ Instance_options();
~Instance_options();
/* fills in argv */
- int complete_initialization(const char *default_path, uint instance_type);
+ int complete_initialization(const char *default_path);
- int add_option(const char* option);
- int init(const char *instance_name_arg);
+ 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:
+ int init(const LEX_STRING *instance_name_arg);
pid_t get_pid();
int get_pid_filename(char *result);
int unlink_pidfile();
@@ -65,7 +67,6 @@ public:
*/
enum { MAX_PATH_LEN= 512 };
enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 2 };
- enum { MEM_ROOT_BLOCK_SIZE= 512 };
char pid_file_with_path[MAX_PATH_LEN];
char **argv;
/*
@@ -76,31 +77,44 @@ public:
/* We need the some options, so we store them as a separate pointers */
const char *mysqld_socket;
const char *mysqld_datadir;
- const char *mysqld_bind_address;
const char *mysqld_pid_file;
const char *mysqld_port;
uint mysqld_port_val;
- const char *instance_name;
- uint instance_name_len;
- const char *mysqld_path;
- uint mysqld_path_len;
+ LEX_STRING instance_name;
+ LEX_STRING mysqld_path;
const char *nonguarded;
const char *shutdown_delay;
uint shutdown_delay_val;
/* log enums are defined in parse.h */
char *logs[3];
- /* this value is computed and cashed here */
- DYNAMIC_ARRAY options_array;
private:
int fill_log_options();
int fill_instance_version();
int 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:
uint filled_default_options;
MEM_ROOT alloc;
+
+ Named_value_arr options;
};
+
+inline int Instance_options::get_num_options() const
+{
+ return options.get_size();
+}
+
+
+inline Named_value Instance_options::get_option(int idx) const
+{
+ return options.get_element(idx);
+}
+
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H */
diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc
index 500b25bec03..da9338e28c5 100644
--- a/server-tools/instance-manager/listener.cc
+++ b/server-tools/instance-manager/listener.cc
@@ -19,21 +19,23 @@
#endif
#include "listener.h"
-#include "priv.h"
-#include <m_string.h>
+
+#include <my_global.h>
#include <mysql.h>
#include <violite.h>
+
+#include <sys/stat.h>
#ifndef __WIN__
#include <sys/un.h>
#endif
-#include <sys/stat.h>
-#include "thread_registry.h"
-#include "options.h"
#include "instance_map.h"
#include "log.h"
#include "mysql_connection.h"
+#include "options.h"
#include "portability.h"
+#include "priv.h"
+#include "thread_registry.h"
/*
@@ -62,8 +64,7 @@ private:
Listener_thread::Listener_thread(const Listener_thread_args &args) :
- Listener_thread_args(args.thread_registry, args.options, args.user_map,
- args.instance_map)
+ Listener_thread_args(args.thread_registry, args.user_map, args.instance_map)
,total_connection_count(0)
,thread_info(pthread_self())
,num_sockets(0)
@@ -234,14 +235,16 @@ int Listener_thread::create_tcp_socket()
bzero(&ip_socket_address, sizeof(ip_socket_address));
ulong im_bind_addr;
- if (options.bind_address != 0)
+ if (Options::Main::bind_address != 0)
{
- if ((im_bind_addr= (ulong) inet_addr(options.bind_address)) == INADDR_NONE)
+ im_bind_addr= (ulong) inet_addr(Options::Main::bind_address);
+
+ if (im_bind_addr == INADDR_NONE)
im_bind_addr= htonl(INADDR_ANY);
}
else
im_bind_addr= htonl(INADDR_ANY);
- uint im_port= options.port_number;
+ uint im_port= Options::Main::port_number;
ip_socket_address.sin_family= AF_INET;
ip_socket_address.sin_addr.s_addr= im_bind_addr;
@@ -295,7 +298,7 @@ create_unix_socket(struct sockaddr_un &unix_socket_address)
bzero(&unix_socket_address, sizeof(unix_socket_address));
unix_socket_address.sun_family= AF_UNIX;
- strmake(unix_socket_address.sun_path, options.socket_file_name,
+ strmake(unix_socket_address.sun_path, Options::Main::socket_file_name,
sizeof(unix_socket_address.sun_path));
unlink(unix_socket_address.sun_path); // in case we have stale socket file
diff --git a/server-tools/instance-manager/listener.h b/server-tools/instance-manager/listener.h
index 28ccbf91731..c28ab0649d7 100644
--- a/server-tools/instance-manager/listener.h
+++ b/server-tools/instance-manager/listener.h
@@ -27,23 +27,19 @@
pthread_handler_t listener(void *arg);
class Thread_registry;
-struct Options;
class User_map;
class Instance_map;
struct Listener_thread_args
{
Thread_registry &thread_registry;
- const Options &options;
const User_map &user_map;
Instance_map &instance_map;
Listener_thread_args(Thread_registry &thread_registry_arg,
- const Options &options_arg,
const User_map &user_map_arg,
Instance_map &instance_map_arg) :
thread_registry(thread_registry_arg)
- ,options(options_arg)
,user_map(user_map_arg)
,instance_map(instance_map_arg)
{}
diff --git a/server-tools/instance-manager/log.cc b/server-tools/instance-manager/log.cc
index 3f54bc0649a..94eaf0f7747 100644
--- a/server-tools/instance-manager/log.cc
+++ b/server-tools/instance-manager/log.cc
@@ -14,14 +14,14 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <my_global.h>
-
#include "log.h"
-#include "portability.h"
-#include <stdarg.h>
+
+#include <my_global.h>
#include <m_string.h>
#include <my_sys.h>
+#include <stdarg.h>
+
/*
TODO:
- add flexible header support
@@ -71,7 +71,7 @@ static inline void log(FILE *file, const char *format, va_list args)
{
int size= sizeof(buff_stack) * 2;
buff_msg= (char*) my_malloc(size, MYF(0));
- while (true)
+ while (TRUE)
{
if (buff_msg == 0)
{
diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc
index 95f9029f648..21e02c0b023 100644
--- a/server-tools/instance-manager/manager.cc
+++ b/server-tools/instance-manager/manager.cc
@@ -14,39 +14,55 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <my_global.h>
#include "manager.h"
-#include "priv.h"
-#include "thread_registry.h"
-#include "listener.h"
-#include "instance_map.h"
-#include "options.h"
-#include "user_map.h"
-#include "log.h"
-#include "guardian.h"
-
-#include <my_sys.h>
+#include <my_global.h>
#include <m_string.h>
-#include <signal.h>
+#include <my_sys.h>
#include <thr_alarm.h>
+
+#include <signal.h>
#ifndef __WIN__
#include <sys/wait.h>
#endif
+#include "exit_codes.h"
+#include "guardian.h"
+#include "instance_map.h"
+#include "listener.h"
+#include "log.h"
+#include "options.h"
+#include "priv.h"
+#include "thread_registry.h"
+#include "user_map.h"
+
static int create_pid_file(const char *pid_file_name)
{
- if (FILE *pid_file= my_fopen(pid_file_name,
- O_WRONLY | O_CREAT | O_BINARY, MYF(0)))
+ FILE *pid_file;
+
+ if (!(pid_file= my_fopen(pid_file_name, O_WRONLY | O_CREAT | O_BINARY,
+ MYF(0))))
+ {
+ log_error("Error: can not create pid file '%s': %s (errno: %d)",
+ (const char *) pid_file_name,
+ (const char *) strerror(errno),
+ (int) errno);
+ return 1;
+ }
+
+ if (fprintf(pid_file, "%d\n", (int) getpid()) <= 0)
{
- fprintf(pid_file, "%d\n", (int) getpid());
- my_fclose(pid_file, MYF(0));
- return 0;
+ log_error("Error: can not write to pid file '%s': %s (errno: %d)",
+ (const char *) pid_file_name,
+ (const char *) strerror(errno),
+ (int) errno);
+ return 1;
}
- log_error("can't create pid file %s: errno=%d, %s",
- pid_file_name, errno, strerror(errno));
- return 1;
+
+ my_fclose(pid_file, MYF(0));
+
+ return 0;
}
#ifndef __WIN__
@@ -82,14 +98,14 @@ bool have_signal;
void onsignal(int signo)
{
- have_signal= true;
+ have_signal= TRUE;
}
void set_signals(sigset_t *set)
{
signal(SIGINT, onsignal);
signal(SIGTERM, onsignal);
- have_signal= false;
+ have_signal= FALSE;
}
int my_sigwait(const sigset_t *set, int *sig)
@@ -109,10 +125,15 @@ int my_sigwait(const sigset_t *set, int *sig)
listener thread, write pid file and enter into signal handling.
See also comments in mysqlmanager.cc to picture general Instance Manager
architecture.
+
+ TODO: how about returning error status.
*/
-void manager(const Options &options)
+void manager()
{
+ int err_code;
+ const char *err_msg;
+
Thread_registry thread_registry;
/*
All objects created in the manager() function live as long as
@@ -121,26 +142,55 @@ void manager(const Options &options)
*/
User_map user_map;
- Instance_map instance_map(options.default_mysqld_path);
+ Instance_map instance_map(Options::Main::default_mysqld_path);
Guardian_thread guardian_thread(thread_registry,
&instance_map,
- options.monitoring_interval);
+ Options::Main::monitoring_interval);
- Listener_thread_args listener_args(thread_registry, options, user_map,
- instance_map);
+ Listener_thread_args listener_args(thread_registry, user_map, instance_map);
manager_pid= getpid();
instance_map.guardian= &guardian_thread;
- if (instance_map.init() || user_map.init())
+ /* Initialize instance map. */
+
+ if (instance_map.init())
+ {
+ log_error("Error: can not initialize instance list: out of memory.");
return;
+ }
+
+ /* Initialize user map and load password file. */
- if (user_map.load(options.password_file_name))
+ if (user_map.init())
+ {
+ log_error("Error: can not initialize user list: out of memory.");
return;
+ }
+
+ if ((err_code= user_map.load(Options::Main::password_file_name, &err_msg)))
+ {
+ 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_error("Warning: password file does not exist, "
+ "nobody will be able to connect to Instance Manager.");
+ }
+ else
+ {
+ log_error("Error: %s.", (const char *) err_msg);
+ return;
+ }
+ }
/* write pid file */
- if (create_pid_file(options.pid_file_name))
- return;
+ if (create_pid_file(Options::Main::pid_file_name))
+ return; /* necessary logging has been already done. */
sigset_t mask;
set_signals(&mask);
@@ -198,7 +248,15 @@ void manager(const Options &options)
shutdown_complete= FALSE;
- if (instance_map.flush_instances())
+ instance_map.guardian->lock();
+ instance_map.lock();
+
+ int flush_instances_status= instance_map.flush_instances();
+
+ instance_map.unlock();
+ instance_map.guardian->unlock();
+
+ if (flush_instances_status)
{
log_error("Cannot init instances repository. This might be caused by "
"the wrong config file options. For instance, missing mysqld "
@@ -240,7 +298,7 @@ void manager(const Options &options)
{
if (!guardian_thread.is_stopped())
{
- bool stop_instances= true;
+ bool stop_instances= TRUE;
guardian_thread.request_shutdown(stop_instances);
pthread_cond_signal(&guardian_thread.COND_guardian);
}
@@ -254,7 +312,7 @@ void manager(const Options &options)
err:
/* delete the pid file */
- my_delete(options.pid_file_name, MYF(0));
+ my_delete(Options::Main::pid_file_name, MYF(0));
#ifndef __WIN__
/* free alarm structures */
@@ -262,4 +320,3 @@ err:
/* don't pthread_exit to kill all threads who did not shut down in time */
#endif
}
-
diff --git a/server-tools/instance-manager/manager.h b/server-tools/instance-manager/manager.h
index 12ed6b3b1ff..d350b5f51f2 100644
--- a/server-tools/instance-manager/manager.h
+++ b/server-tools/instance-manager/manager.h
@@ -16,8 +16,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-struct Options;
-
-void manager(const Options &options);
+void manager();
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H
diff --git a/server-tools/instance-manager/messages.cc b/server-tools/instance-manager/messages.cc
index a9b00b9e01f..af35af1e7b0 100644
--- a/server-tools/instance-manager/messages.cc
+++ b/server-tools/instance-manager/messages.cc
@@ -14,15 +14,14 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <my_global.h>
#include "messages.h"
+#include <my_global.h>
+#include <mysql_com.h>
+
#include "mysqld_error.h"
#include "mysql_manager_error.h"
-#include <mysql_com.h>
-#include <assert.h>
-
static const char *mysqld_error_message(unsigned sql_errno)
{
@@ -70,6 +69,23 @@ static const char *mysqld_error_message(unsigned sql_errno)
"in the instance options";
case ER_ACCESS_OPTION_FILE:
return "Cannot open the option file to edit. Check permissions";
+ case ER_DROP_ACTIVE_INSTANCE:
+ return "Cannot drop an active instance. You should stop it first";
+ case ER_CREATE_EXISTING_INSTANCE:
+ return "Instance already exists";
+ case ER_INSTANCE_MISCONFIGURED:
+ return "Instance is misconfigured. Cannot start it";
+ case ER_MALFORMED_INSTANCE_NAME:
+ return "Malformed instance name.";
+ case ER_INSTANCE_IS_ACTIVE:
+ return "The instance is active. Stop the instance first";
+ case ER_THERE_IS_ACTIVE_INSTACE:
+ return "At least one instance is active. Stop all instances first";
+ case ER_INCOMPATIBLE_OPTION:
+ return "Instance Manager-specific options are prohibited from being used "
+ "in the configuration of mysqld-compatible instances";
+ case ER_CONF_FILE_DOES_NOT_EXIST:
+ return "Configuration file does not exist";
default:
DBUG_ASSERT(0);
return 0;
diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc
index dcd1807701f..17cda3af704 100644
--- a/server-tools/instance-manager/mysql_connection.cc
+++ b/server-tools/instance-manager/mysql_connection.cc
@@ -20,22 +20,24 @@
#include "mysql_connection.h"
-#include "priv.h"
-#include "mysql_manager_error.h"
-#include "mysqld_error.h"
-#include "thread_registry.h"
+#include <m_string.h>
+#include <m_string.h>
+#include <my_global.h>
+#include <mysql_com.h>
+#include <mysql.h>
+#include <my_sys.h>
+#include <violite.h>
+
+#include "command.h"
#include "log.h"
-#include "user_map.h"
-#include "protocol.h"
#include "messages.h"
-#include "command.h"
+#include "mysqld_error.h"
+#include "mysql_manager_error.h"
#include "parse.h"
-
-#include <mysql.h>
-#include <violite.h>
-#include <mysql_com.h>
-#include <m_string.h>
-#include <my_sys.h>
+#include "priv.h"
+#include "protocol.h"
+#include "thread_registry.h"
+#include "user_map.h"
Mysql_connection_thread_args::Mysql_connection_thread_args(
@@ -56,7 +58,7 @@ Mysql_connection_thread_args::Mysql_connection_thread_args(
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,
+ 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().
*/
@@ -190,8 +192,6 @@ void Mysql_connection_thread::run()
int Mysql_connection_thread::check_connection()
{
ulong pkt_len=0; // to hold client reply length
- /* maximum size of the version string */
- enum { MAX_VERSION_LENGTH= 80 };
/* buffer for the first packet */ /* packet contains: */
char buff[MAX_VERSION_LENGTH + 1 + // server version, 0-ended
@@ -202,8 +202,8 @@ int Mysql_connection_thread::check_connection()
char *pos= buff;
ulong server_flags;
- memcpy(pos, mysqlmanager_version, mysqlmanager_version_length + 1);
- pos+= mysqlmanager_version_length + 1;
+ memcpy(pos, mysqlmanager_version.str, mysqlmanager_version.length + 1);
+ pos+= mysqlmanager_version.length + 1;
int4store((uchar*) pos, connection_id);
pos+= 4;
@@ -271,12 +271,14 @@ int Mysql_connection_thread::check_connection()
const char *user= 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, password-user-2, password, scramble))
+ if (user_map.authenticate(&user_name, password, scramble))
{
net_send_error(&net, ER_ACCESS_DENIED_ERROR);
return 1;
@@ -312,7 +314,7 @@ int Mysql_connection_thread::do_command()
packet= (char*) net.read_pos;
enum enum_server_command command= (enum enum_server_command)
(uchar) *packet;
- log_info("connection %d: packet_length=%d, command=%d",
+ log_info("connection %d: packet_length=%d, command=%d",
connection_id, packet_length, command);
return dispatch_command(command, packet + 1, packet_length - 1);
}
@@ -336,7 +338,7 @@ int Mysql_connection_thread::dispatch_command(enum enum_server_command command,
if (Command *command= parse_command(&instance_map, packet))
{
int res= 0;
- log_info("query for connection %d successefully parsed",connection_id);
+ log_info("query for connection %d successfully parsed",connection_id);
res= command->execute(&net, connection_id);
delete command;
if (!res)
@@ -380,5 +382,5 @@ pthread_handler_t mysql_connection(void *arg)
}
/*
- vim: fdm=marker
+ vim: fdm=marker
*/
diff --git a/server-tools/instance-manager/mysql_manager_error.h b/server-tools/instance-manager/mysql_manager_error.h
index ff782923a8e..373c7d798a0 100644
--- a/server-tools/instance-manager/mysql_manager_error.h
+++ b/server-tools/instance-manager/mysql_manager_error.h
@@ -29,5 +29,13 @@
#define ER_ACCESS_OPTION_FILE 3008
#define ER_OFFSET_ERROR 3009
#define ER_READ_FILE 3010
+#define ER_DROP_ACTIVE_INSTANCE 3011
+#define ER_CREATE_EXISTING_INSTANCE 3012
+#define ER_INSTANCE_MISCONFIGURED 3013
+#define ER_MALFORMED_INSTANCE_NAME 3014
+#define ER_INSTANCE_IS_ACTIVE 3015
+#define ER_THERE_IS_ACTIVE_INSTACE 3016
+#define ER_INCOMPATIBLE_OPTION 3017
+#define ER_CONF_FILE_DOES_NOT_EXIST 3018
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H */
diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc
index d0b2cf2666c..c8be1842258 100644
--- a/server-tools/instance-manager/mysqlmanager.cc
+++ b/server-tools/instance-manager/mysqlmanager.cc
@@ -15,25 +15,29 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
-#include "manager.h"
-
-#include "options.h"
-#include "log.h"
-
#include <my_sys.h>
+
#include <string.h>
#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
#ifndef __WIN__
#include <pwd.h>
#include <grp.h>
#include <sys/wait.h>
#endif
-#include <sys/types.h>
-#include <sys/stat.h>
+
+#include "log.h"
+#include "manager.h"
+#include "options.h"
+#include "user_management_commands.h"
+
#ifdef __WIN__
#include "windowsservice.h"
#endif
+
/*
Few notes about Instance Manager architecture:
Instance Manager consisits of two processes: the angel process, and the
@@ -59,13 +63,12 @@
*/
static void init_environment(char *progname);
+
#ifndef __WIN__
static void daemonize(const char *log_file_name);
-static void angel(const Options &options);
+static void angel();
static struct passwd *check_user(const char *user);
static int set_user(const char *user, struct passwd *user_info);
-#else
-int HandleServiceOptions(Options options);
#endif
@@ -81,41 +84,61 @@ int main(int argc, char *argv[])
{
int return_value= 1;
init_environment(argv[0]);
- Options options;
- if (options.load(argc, argv))
- goto err;
+ if ((return_value= Options::load(argc, argv)))
+ goto main_end;
+
+ if (Options::User_management::cmd)
+ {
+ return_value= Options::User_management::cmd->execute();
+
+ goto main_end;
+ }
#ifndef __WIN__
+
struct passwd *user_info;
- if ((user_info= check_user(options.user)))
+ if ((user_info= check_user(Options::Daemon::user)))
{
- if (set_user(options.user, user_info))
- goto err;
+ if (set_user(Options::Daemon::user, user_info))
+ {
+ return_value= 1;
+ goto main_end;
+ }
}
- if (options.run_as_service)
+ if (Options::Daemon::run_as_service)
{
/* forks, and returns only in child */
- daemonize(options.log_file_name);
+ daemonize(Options::Daemon::log_file_name);
/* forks again, and returns only in child: parent becomes angel */
- angel(options);
+ angel();
}
+
+ manager();
+
#else
- if (!options.stand_alone)
+
+ if (!Options::Service::stand_alone)
{
- if (HandleServiceOptions(options))
- goto err;
+ if (HandleServiceOptions())
+ {
+ return_value= 1;
+ goto main_end;
+ }
}
else
+ {
+ manager();
+ }
+
#endif
- manager(options);
return_value= 0;
-err:
- options.cleanup();
+main_end:
+ Options::cleanup();
my_end(0);
return return_value;
}
@@ -200,7 +223,7 @@ static void init_environment(char *progname)
MY_INIT(progname);
log_init();
umask(0117);
- srand(time(0));
+ srand((unsigned int) time(0));
}
@@ -298,7 +321,7 @@ void terminate(int signo)
Angel process will exit silently if mysqlmanager exits normally.
*/
-static void angel(const Options &options)
+static void angel()
{
/* install signal handlers */
sigset_t zeromask; // to sigsuspend in parent
diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc
index 85fea76c079..ab40ba96dba 100644
--- a/server-tools/instance-manager/options.cc
+++ b/server-tools/instance-manager/options.cc
@@ -20,45 +20,88 @@
#include "options.h"
-#include "priv.h"
-#include "portability.h"
+#include <my_global.h>
#include <my_sys.h>
#include <my_getopt.h>
-#include <m_string.h>
#include <mysql_com.h>
+#include "exit_codes.h"
+#include "log.h"
+#include "portability.h"
+#include "priv.h"
+#include "user_management_commands.h"
+
#define QUOTE2(x) #x
#define QUOTE(x) QUOTE2(x)
#ifdef __WIN__
-char Options::install_as_service;
-char Options::remove_service;
-char Options::stand_alone;
-char windows_config_file[FN_REFLEN];
-char default_password_file_name[FN_REFLEN];
-char default_log_file_name[FN_REFLEN];
-const char *Options::config_file= windows_config_file;
-#else
-char Options::run_as_service;
-const char *Options::user= 0; /* No default value */
-const char *default_password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
-const char *default_log_file_name= QUOTE(DEFAULT_LOG_FILE_NAME);
-const char *Options::config_file= QUOTE(DEFAULT_CONFIG_FILE);
+
+/* 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_socket_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::socket_file_name= win_dflt_socket_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::Main::config_file= QUOTE(DEFAULT_CONFIG_FILE);
+const char *
+Options::Main::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
+const char *Options::Main::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME);
+const char *Options::Main::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME);
+
+const char *Options::Main::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH);
+
#endif
-const char *Options::log_file_name= default_log_file_name;
-const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME);
-const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME);
-const char *Options::password_file_name= default_password_file_name;
-const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH);
-const char *Options::bind_address= 0; /* No default value */
-uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL;
-uint Options::port_number= DEFAULT_PORT;
-/* just to declare */
+
+/* Remember if the config file was forced. */
+
+bool Options::Main::is_forced_default_file= FALSE;
+
+/* Define and initialize common options. */
+
+const char *Options::Main::bind_address= NULL; /* No default value */
+uint Options::Main::monitoring_interval= DEFAULT_MONITORING_INTERVAL;
+uint Options::Main::port_number= DEFAULT_PORT;
+my_bool Options::Main::mysqld_safe_compatible= FALSE;
+
+/* Options::User_management */
+
+char *Options::User_management::user_name= NULL;
+char *Options::User_management::password= NULL;
+
+User_management_cmd *Options::User_management::cmd= NULL;
+
+/* Private members. */
+
char **Options::saved_argv= NULL;
-/* Remember if the config file was forced */
-bool Options::is_forced_default_file= 0;
+
#ifndef DBUG_OFF
-const char *Options::default_dbug_option= "d:t:i:O,im.trace";
+const char *Options::Debug::config_str= "d:t:i:O,im.trace";
#endif
/*
@@ -67,23 +110,33 @@ const char *Options::default_dbug_option= "d:t:i:O,im.trace";
*/
enum options {
+ OPT_PASSWD= 'P',
+ OPT_USERNAME= 'u',
+ OPT_PASSWORD= 'p',
OPT_LOG= 256,
OPT_PID_FILE,
OPT_SOCKET,
OPT_PASSWORD_FILE,
OPT_MYSQLD_PATH,
-#ifndef __WIN__
- OPT_RUN_AS_SERVICE,
- OPT_USER,
-#else
+#ifdef __WIN__
OPT_INSTALL_SERVICE,
OPT_REMOVE_SERVICE,
OPT_STAND_ALONE,
+#else
+ OPT_RUN_AS_SERVICE,
+ OPT_USER,
#endif
OPT_MONITORING_INTERVAL,
OPT_PORT,
OPT_WAIT_TIMEOUT,
- OPT_BIND_ADDRESS
+ OPT_BIND_ADDRESS,
+ 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[] =
@@ -91,94 +144,151 @@ 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 },
+
{ "bind-address", OPT_BIND_ADDRESS, "Bind address to use for connection.",
- (gptr *) &Options::bind_address, (gptr *) &Options::bind_address,
+ (gptr *) &Options::Main::bind_address,
+ (gptr *) &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.",
- (gptr*) &Options::default_dbug_option, (gptr*) &Options::default_dbug_option,
+ (gptr *) &Options::Debug::config_str,
+ (gptr *) &Options::Debug::config_str,
0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
{ "default-mysqld-path", OPT_MYSQLD_PATH, "Where to look for MySQL"
" Server binary.",
- (gptr *) &Options::default_mysqld_path,
- (gptr *) &Options::default_mysqld_path,
+ (gptr *) &Options::Main::default_mysqld_path,
+ (gptr *) &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.",
- (gptr *) &Options::install_as_service, (gptr*) &Options::install_as_service,
+ (gptr *) &Options::Service::install_as_service,
+ (gptr *) &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.",
- (gptr *) &Options::log_file_name, (gptr *) &Options::log_file_name,
+ (gptr *) &Options::Daemon::log_file_name,
+ (gptr *) &Options::Daemon::log_file_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+#endif
{ "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor"
" instances in seconds.",
- (gptr *) &Options::monitoring_interval,
- (gptr *) &Options::monitoring_interval,
+ (gptr *) &Options::Main::monitoring_interval,
+ (gptr *) &Options::Main::monitoring_interval,
0, GET_UINT, REQUIRED_ARG, DEFAULT_MONITORING_INTERVAL,
0, 0, 0, 0, 0 },
- { "passwd", 'P', "Prepare entry for passwd file and exit.", 0, 0, 0,
- GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "mysqld-safe-compatible", OPT_MYSQLD_SAFE_COMPATIBLE,
+ "Start Instance Manager in mysqld_safe compatible manner",
+ (gptr *) &Options::Main::mysqld_safe_compatible,
+ (gptr *) &Options::Main::mysqld_safe_compatible,
+ 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
+
+ { "passwd", OPT_PASSWD,
+ "Prepare an entry 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",
+ (gptr *) &Options::User_management::password,
+ (gptr *) &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.",
- (gptr *) &Options::password_file_name,
- (gptr *) &Options::password_file_name,
+ { "password-file", OPT_PASSWORD_FILE,
+ "Look for Instance Manager users and passwords here.",
+ (gptr *) &Options::Main::password_file_name,
+ (gptr *) &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.",
- (gptr *) &Options::pid_file_name, (gptr *) &Options::pid_file_name,
+ (gptr *) &Options::Main::pid_file_name,
+ (gptr *) &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",
- (gptr *) &Options::port_number, (gptr *) &Options::port_number,
+ (gptr *) &Options::Main::port_number,
+ (gptr *) &Options::Main::port_number,
0, GET_UINT, REQUIRED_ARG, DEFAULT_PORT, 0, 0, 0, 0, 0 },
#ifdef __WIN__
{ "remove", OPT_REMOVE_SERVICE, "Remove system service.",
- (gptr *)&Options::remove_service, (gptr*) &Options::remove_service,
+ (gptr *) &Options::Service::remove_service,
+ (gptr *) &Options::Service::remove_service,
0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0},
#else
{ "run-as-service", OPT_RUN_AS_SERVICE,
- "Daemonize and start angel process.", (gptr *) &Options::run_as_service,
+ "Daemonize and start angel process.",
+ (gptr *) &Options::Daemon::run_as_service,
0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
#endif
{ "socket", OPT_SOCKET, "Socket file to use for connection.",
- (gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name,
+ (gptr *) &Options::Main::socket_file_name,
+ (gptr *) &Options::Main::socket_file_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
#ifdef __WIN__
{ "standalone", OPT_STAND_ALONE, "Run the application in stand alone mode.",
- (gptr *)&Options::stand_alone, (gptr*) &Options::stand_alone,
+ (gptr *) &Options::Service::stand_alone,
+ (gptr *) &Options::Service::stand_alone,
0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0},
#else
{ "user", OPT_USER, "Username to start mysqlmanager",
- (gptr *) &Options::user,
- (gptr *) &Options::user,
+ (gptr *) &Options::Daemon::user,
+ (gptr *) &Options::Daemon::user,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
#endif
+ { "username", OPT_USERNAME,
+ "Username to update the password file",
+ (gptr *) &Options::User_management::user_name,
+ (gptr *) &Options::User_management::user_name,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+
{ "version", 'V', "Output version information and exit.", 0, 0, 0,
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "wait-timeout", OPT_WAIT_TIMEOUT, "The number of seconds IM waits "
"for activity on a connection before closing it.",
- (gptr *) &net_read_timeout, (gptr *) &net_read_timeout, 0, GET_ULONG,
- REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0 },
+ (gptr *) &net_read_timeout,
+ (gptr *) &net_read_timeout,
+ 0, GET_ULONG, REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0 },
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }
};
static void version()
{
- printf("%s Ver %s for %s on %s\n", my_progname, mysqlmanager_version,
+ printf("%s Ver %s for %s on %s\n", my_progname,
+ (const char *) mysqlmanager_version.str,
SYSTEM_TYPE, MACHINE_TYPE);
}
@@ -206,37 +316,6 @@ static void usage()
}
-static void passwd()
-{
- char user[1024], *p;
- const char *pw1, *pw2;
- char pw1msg[]= "Enter password: ";
- char pw2msg[]= "Re-type password: ";
- char crypted_pw[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1];
-
- fprintf(stderr, "Creating record for new user.\n");
- fprintf(stderr, "Enter user name: ");
- if (!fgets(user, sizeof(user), stdin))
- {
- fprintf(stderr, "Unable to read user.\n");
- return;
- }
- if ((p= strchr(user, '\n'))) *p= 0;
-
- pw1= get_tty_password(pw1msg);
- pw2= get_tty_password(pw2msg);
-
- if (strcmp(pw1, pw2))
- {
- fprintf(stderr, "Sorry, passwords do not match.\n");
- return;
- }
-
- make_scrambled_password(crypted_pw, pw1);
- printf("%s:%s\n", user, crypted_pw);
-}
-
-
C_MODE_START
static my_bool
@@ -248,16 +327,52 @@ get_one_option(int optid,
case 'V':
version();
exit(0);
- case 'P':
- passwd();
- exit(0);
+ case OPT_PASSWD:
+ 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_PASSWD:
+ Options::User_management::cmd= new Passwd_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::default_dbug_option);
- DBUG_SET_INITIAL(argument ? argument : Options::default_dbug_option);
+ DBUG_SET(argument ? argument : Options::Debug::config_str);
+ DBUG_SET_INITIAL(argument ? argument : Options::Debug::config_str);
#endif
break;
}
@@ -283,8 +398,8 @@ int Options::load(int argc, char **argv)
{
if (is_prefix(argv[1], "--defaults-file="))
{
- Options::config_file= strchr(argv[1], '=') + 1;
- Options::is_forced_default_file= 1;
+ Main::config_file= strchr(argv[1], '=') + 1;
+ Main::is_forced_default_file= TRUE;
}
if (is_prefix(argv[1], "--defaults-extra-file=") ||
is_prefix(argv[1], "--no-defaults"))
@@ -293,59 +408,92 @@ int Options::load(int argc, char **argv)
fprintf(stderr, "The --defaults-extra-file and --no-defaults options"
" are not supported by\n"
"Instance Manager. Program aborted.\n");
- goto err;
+ return ERR_INVALID_USAGE;
}
}
#ifdef __WIN__
if (setup_windows_defaults())
- goto err;
+ {
+ fprintf(stderr, "Internal error: could not setup default values.\n");
+ return ERR_OUT_OF_MEMORY;
+ }
#endif
+
/* load_defaults will reset saved_argv with a new allocated list */
saved_argv= argv;
/* config-file options are prepended to command-line ones */
- load_defaults(config_file, default_groups, &argc,
- &saved_argv);
- if ((handle_options(&argc, &saved_argv, my_long_options,
- get_one_option)) != 0)
- goto err;
+ log_info("Loading config file '%s'...",
+ (const char *) Main::config_file);
- return 0;
+ load_defaults(Main::config_file, default_groups, &argc, &saved_argv);
+
+ if ((handle_options(&argc, &saved_argv, my_long_options, get_one_option)))
+ return ERR_INVALID_USAGE;
+
+ if (!User_management::cmd &&
+ (User_management::user_name || User_management::password))
+ {
+ fprintf(stderr,
+ "--username and/or --password options have been specified, "
+ "but no password-management command has been given.\n");
+ return ERR_INVALID_USAGE;
+ }
-err:
- return 1;
+ return 0;
}
void Options::cleanup()
{
- /* free_defaults returns nothing */
- if (Options::saved_argv != NULL)
- free_defaults(Options::saved_argv);
+ if (saved_argv)
+ free_defaults(saved_argv);
+
+ delete User_management::cmd;
}
#ifdef __WIN__
-int Options::setup_windows_defaults()
+static int setup_windows_defaults()
{
- if (!GetModuleFileName(NULL, default_password_file_name,
- sizeof(default_password_file_name)))
- return 1;
- char *filename= strstr(default_password_file_name, ".exe");
- strcpy(filename, ".passwd");
-
- if (!GetModuleFileName(NULL, default_log_file_name,
- sizeof(default_log_file_name)))
+ char module_full_name[FN_REFLEN];
+ char dir_name[FN_REFLEN];
+ char base_name[FN_REFLEN];
+ char im_name[FN_REFLEN];
+ char *base_name_ptr;
+ char *ptr;
+
+ /* Determine dirname and basename. */
+
+ if (!GetModuleFileName(NULL, module_full_name, sizeof (module_full_name)) ||
+ !GetFullPathName(module_full_name, sizeof (dir_name), dir_name,
+ &base_name_ptr))
+ {
return 1;
- filename= strstr(default_log_file_name, ".exe");
- strcpy(filename, ".log");
+ }
+
+ strmake(base_name, base_name_ptr, FN_REFLEN);
+ *base_name_ptr= 0;
+
+ strmake(im_name, base_name, FN_REFLEN);
+ ptr= strrchr(im_name, '.');
+
+ if (!ptr)
+ return 1;
+
+ *ptr= 0;
+
+ /* Initialize the defaults. */
+
+ strxmov(win_dflt_config_file_name, dir_name, DFLT_CONFIG_FILE_NAME, NullS);
+ strxmov(win_dflt_mysqld_path, dir_name, DFLT_MYSQLD_PATH, NullS);
+ strxmov(win_dflt_password_file_name, dir_name, im_name, DFLT_PASSWD_FILE_EXT,
+ NullS);
+ strxmov(win_dflt_pid_file_name, dir_name, im_name, DFLT_PID_FILE_EXT, NullS);
+ strxmov(win_dflt_socket_file_name, dir_name, im_name, DFLT_SOCKET_FILE_EXT,
+ NullS);
- if (!GetModuleFileName(NULL, windows_config_file,
- sizeof(windows_config_file)))
- return 1;
- char *slash= strrchr(windows_config_file, '\\');
- strcpy(slash, "\\my.ini");
return 0;
}
diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h
index 06818288606..898114c45da 100644
--- a/server-tools/instance-manager/options.h
+++ b/server-tools/instance-manager/options.h
@@ -17,49 +17,86 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*
- Options - all possible options for the instance manager grouped in one
- struct.
+ Options - all possible command-line options for the Instance Manager grouped
+ in one struct.
*/
+
#include <my_global.h>
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
+class User_management_cmd;
+
struct Options
{
-#ifdef __WIN__
- static char install_as_service;
- static char remove_service;
- static char stand_alone;
-#else
- static char run_as_service; /* handle_options doesn't support bool */
- static const char *user;
-#endif
- static bool is_forced_default_file;
- static const char *log_file_name;
- static const char *pid_file_name;
- static const char *socket_file_name;
- static const char *password_file_name;
- static const char *default_mysqld_path;
- /* the option which should be passed to process_default_option_files */
- static uint monitoring_interval;
- static uint port_number;
- static const char *bind_address;
- static const char *config_file;
+ /*
+ NOTE: handle_options() expects value of my_bool type for GET_BOOL
+ accessor (i.e. bool must not be used).
+ */
- /* argv pointer returned by load_defaults() to be used by free_defaults() */
- static char **saved_argv;
+ 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;
+ static const char *socket_file_name;
+ 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
- static const char *default_dbug_option;
+ struct Debug
+ {
+ static const char *config_str;
+ };
#endif
- int load(int argc, char **argv);
- void cleanup();
-#ifdef __WIN__
- int setup_windows_defaults();
+#ifndef __WIN__
+
+ struct Daemon
+ {
+ static my_bool run_as_service;
+ static const char *log_file_name;
+ static const char *user;
+ };
+
+#else
+
+ struct Service
+ {
+ static my_bool install_as_service;
+ static my_bool remove_service;
+ static my_bool stand_alone;
+ };
+
#endif
+
+public:
+ static int load(int argc, char **argv);
+ static void cleanup();
+
+private:
+ Options(); /* Deny instantiation of this class. */
+
+private:
+ /* argv pointer returned by load_defaults() to be used by free_defaults() */
+ static char **saved_argv;
};
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H
diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc
index 14b3db16b45..4e931488fce 100644
--- a/server-tools/instance-manager/parse.cc
+++ b/server-tools/instance-manager/parse.cc
@@ -17,12 +17,12 @@
#include "parse.h"
#include "commands.h"
-#include <string.h>
-
enum Token
{
- TOK_ERROR= 0, /* Encodes the "ERROR" word, it doesn't indicate error. */
+ TOK_CREATE= 0,
+ TOK_DROP,
+ TOK_ERROR, /* Encodes the "ERROR" word, it doesn't indicate error. */
TOK_FILES,
TOK_FLUSH,
TOK_GENERAL,
@@ -50,6 +50,8 @@ struct tokens_st
static struct tokens_st tokens[]= {
+ {6, "CREATE"},
+ {4, "DROP"},
{5, "ERROR"},
{5, "FILES"},
{5, "FLUSH"},
@@ -67,6 +69,37 @@ static struct tokens_st tokens[]= {
{5, "UNSET"}
};
+/************************************************************************/
+
+Named_value_arr::Named_value_arr() :
+ initialized(FALSE)
+{
+}
+
+
+bool Named_value_arr::init()
+{
+ if (my_init_dynamic_array(&arr, sizeof(Named_value), 0, 32))
+ return TRUE;
+
+ initialized= TRUE;
+
+ return FALSE;
+}
+
+
+Named_value_arr::~Named_value_arr()
+{
+ if (!initialized)
+ return;
+
+ for (int i= 0; i < get_size(); ++i)
+ get_element(i).free();
+
+ delete_dynamic(&arr);
+}
+
+/************************************************************************/
/*
Returns token no if word corresponds to some token, otherwise returns
@@ -104,53 +137,200 @@ Token shift_token(const char **text, uint *word_len)
}
-int get_text_id(const char **text, uint *word_len, const char **id)
+int get_text_id(const char **text, LEX_STRING *token)
{
- get_word(text, word_len);
- if (*word_len == 0)
+ get_word(text, &token->length);
+ if (token->length == 0)
return 1;
- *id= *text;
+ token->str= (char *) *text;
return 0;
}
+static bool parse_long(const LEX_STRING *token, long *value)
+{
+ int err_code;
+ char *end_ptr= token->str + token->length;
+
+ *value= my_strtoll10(token->str, &end_ptr, &err_code);
+
+ return err_code != 0;
+}
+
+
+bool parse_option_value(const char *text, uint *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(Instance_map *map, const char *text)
{
uint word_len;
- const char *instance_name;
- uint instance_name_len;
- const char *option;
- uint option_len;
- const char *option_value;
- uint option_value_len;
- const char *log_size;
+ LEX_STRING instance_name;
Command *command;
const char *saved_text= text;
- bool skip= false;
- const char *tmp;
Token tok1= shift_token(&text, &word_len);
switch (tok1) {
case TOK_START: // fallthrough
case TOK_STOP:
+ case TOK_CREATE:
+ case TOK_DROP:
if (shift_token(&text, &word_len) != TOK_INSTANCE)
goto syntax_error;
get_word(&text, &word_len);
if (word_len == 0)
goto syntax_error;
- instance_name= text;
- instance_name_len= word_len;
+ instance_name.str= (char *) text;
+ instance_name.length= word_len;
text+= word_len;
- /* it should be the end of command */
- get_word(&text, &word_len, NONSPACE);
- if (word_len)
- goto syntax_error;
- if (tok1 == TOK_START)
- command= new Start_instance(map, instance_name, instance_name_len);
+ if (tok1 == TOK_CREATE)
+ {
+ Create_instance *cmd= new Create_instance(map, &instance_name);
+
+ if (!cmd)
+ return NULL; /* Report ER_OUT_OF_RESOURCES. */
+
+ if (cmd->init(&text))
+ {
+ delete cmd;
+ goto syntax_error;
+ }
+
+ command= cmd;
+ }
else
- command= new Stop_instance(map, instance_name, instance_name_len);
+ {
+ /* it should be the end of command */
+ get_word(&text, &word_len, NONSPACE);
+ if (word_len)
+ goto syntax_error;
+ }
+
+ switch (tok1) {
+ case TOK_START:
+ command= new Start_instance(map, &instance_name);
+ break;
+ case TOK_STOP:
+ command= new Stop_instance(map, &instance_name);
+ break;
+ case TOK_CREATE:
+ ; /* command already initialized. */
+ break;
+ case TOK_DROP:
+ command= new Drop_instance(map, &instance_name);
+ break;
+ default: /* this is impossible, but nevertheless... */
+ DBUG_ASSERT(0);
+ }
break;
case TOK_FLUSH:
if (shift_token(&text, &word_len) != TOK_INSTANCES)
@@ -163,53 +343,28 @@ Command *parse_command(Instance_map *map, const char *text)
command= new Flush_instances(map);
break;
case TOK_UNSET:
- skip= true;
case TOK_SET:
+ {
+ Abstract_option_cmd *cmd;
- if (get_text_id(&text, &instance_name_len, &instance_name))
- goto syntax_error;
- text+= instance_name_len;
-
- /* the next token should be a dot */
- get_word(&text, &word_len);
- if (*text != '.')
- goto syntax_error;
- text++;
+ if (tok1 == TOK_SET)
+ cmd= new Set_option(map);
+ else
+ cmd= new Unset_option(map);
- get_word(&text, &option_len, NONSPACE);
- option= text;
- if ((tmp= strchr(text, '=')) != NULL)
- option_len= tmp - text;
- text+= option_len;
+ if (!cmd)
+ return NULL; /* Report ER_OUT_OF_RESOURCES. */
- get_word(&text, &word_len);
- if (*text == '=')
- {
- text++; /* skip '=' */
- get_word(&text, &option_value_len, NONSPACE);
- option_value= text;
- text+= option_value_len;
- }
- else
- {
- option_value= "";
- option_value_len= 0;
- }
+ if (cmd->init(&text))
+ {
+ delete cmd;
+ goto syntax_error;
+ }
- /* should be empty */
- get_word(&text, &word_len, NONSPACE);
- if (word_len)
- goto syntax_error;
+ command= cmd;
- if (skip)
- command= new Unset_option(map, instance_name, instance_name_len,
- option, option_len, option_value,
- option_value_len);
- else
- command= new Set_option(map, instance_name, instance_name_len,
- option, option_len, option_value,
- option_value_len);
- break;
+ break;
+ }
case TOK_SHOW:
switch (shift_token(&text, &word_len)) {
case TOK_INSTANCES:
@@ -222,30 +377,35 @@ Command *parse_command(Instance_map *map, const char *text)
switch (Token tok2= shift_token(&text, &word_len)) {
case TOK_OPTIONS:
case TOK_STATUS:
- if (get_text_id(&text, &instance_name_len, &instance_name))
+ if (get_text_id(&text, &instance_name))
goto syntax_error;
- text+= instance_name_len;
+ text+= instance_name.length;
/* check that this is the end of the command */
get_word(&text, &word_len, NONSPACE);
if (word_len)
goto syntax_error;
if (tok2 == TOK_STATUS)
- command= new Show_instance_status(map, instance_name,
- instance_name_len);
+ command= new Show_instance_status(map, &instance_name);
else
- command= new Show_instance_options(map, instance_name,
- instance_name_len);
+ command= new Show_instance_options(map, &instance_name);
break;
default:
goto syntax_error;
}
break;
default:
- instance_name= text - word_len;
- instance_name_len= word_len;
- if (instance_name_len)
+ instance_name.str= (char *) text - word_len;
+ instance_name.length= word_len;
+ if (instance_name.length)
{
Log_type log_type;
+
+ long log_size;
+ LEX_STRING log_size_str;
+
+ long log_offset= 0;
+ LEX_STRING log_offset_str= { NULL, 0 };
+
switch (shift_token(&text, &word_len)) {
case TOK_LOG:
switch (Token tok3= shift_token(&text, &word_len)) {
@@ -254,8 +414,7 @@ Command *parse_command(Instance_map *map, const char *text)
/* check that this is the end of the command */
if (word_len)
goto syntax_error;
- command= new Show_instance_log_files(map, instance_name,
- instance_name_len);
+ command= new Show_instance_log_files(map, &instance_name);
break;
case TOK_ERROR:
case TOK_GENERAL:
@@ -275,12 +434,14 @@ Command *parse_command(Instance_map *map, const char *text)
goto syntax_error;
}
/* get the size of the log we want to retrieve */
- if (get_text_id(&text, &word_len, &log_size))
+ if (get_text_id(&text, &log_size_str))
goto syntax_error;
- text+= word_len;
+ text+= log_size_str.length;
+
/* this parameter is required */
- if (!word_len)
+ if (!log_size_str.length)
goto syntax_error;
+
/* the next token should be comma, or nothing */
get_word(&text, &word_len);
switch (*text) {
@@ -290,23 +451,41 @@ Command *parse_command(Instance_map *map, const char *text)
get_word(&text, &word_len);
if (!word_len)
goto syntax_error;
+ log_offset_str.str= (char *) text;
+ log_offset_str.length= word_len;
text+= word_len;
- command= new Show_instance_log(map, instance_name,
- instance_name_len, log_type,
- log_size, text);
get_word(&text, &word_len, NONSPACE);
/* check that this is the end of the command */
if (word_len)
goto syntax_error;
break;
case '\0':
- command= new Show_instance_log(map, instance_name,
- instance_name_len, log_type,
- log_size, NULL);
break; /* this is ok */
default:
+ goto syntax_error;
+ }
+
+ /* Parse size parameter. */
+
+ if (parse_long(&log_size_str, &log_size))
+ goto syntax_error;
+
+ if (log_size <= 0)
goto syntax_error;
+
+ /* Parse offset parameter (if specified). */
+
+ if (log_offset_str.length)
+ {
+ if (parse_long(&log_offset_str, &log_offset))
+ goto syntax_error;
+
+ if (log_offset <= 0)
+ goto syntax_error;
}
+
+ command= new Show_instance_log(map, &instance_name,
+ log_type, log_size, log_offset);
break;
default:
goto syntax_error;
diff --git a/server-tools/instance-manager/parse.h b/server-tools/instance-manager/parse.h
index 3da53e3a61e..ae29c7eb64a 100644
--- a/server-tools/instance-manager/parse.h
+++ b/server-tools/instance-manager/parse.h
@@ -18,6 +18,7 @@
#include <my_global.h>
#include <my_sys.h>
+#include <m_string.h>
class Command;
class Instance_map;
@@ -29,10 +30,148 @@ enum Log_type
IM_LOG_SLOW
};
-Command *parse_command(Instance_map *instance_map, const char *text);
+Command *parse_command(Instance_map *map, const char *text);
+
+bool parse_option_value(const char *text, uint *text_len, char **value);
+
+void skip_spaces(const char **text);
/* define kinds of the word seek method */
-enum { ALPHANUM= 1, NONSPACE };
+enum enum_seek_method { ALPHANUM= 1, NONSPACE, OPTION_NAME };
+
+/************************************************************************/
+
+class Named_value
+{
+public:
+ /*
+ The purpose of these methods is just to have one method for
+ allocating/deallocating memory for strings for Named_value.
+ */
+
+ static inline char *alloc_str(const LEX_STRING *str);
+ static inline char *alloc_str(const char *str);
+ static inline void free_str(char **str);
+
+public:
+ inline Named_value();
+ inline Named_value(char *name_arg, char *value_arg);
+
+ inline char *get_name();
+ inline char *get_value();
+
+ inline void free();
+
+private:
+ char *name;
+ char *value;
+};
+
+inline char *Named_value::alloc_str(const LEX_STRING *str)
+{
+ return my_strndup((const byte *) 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, (gptr) &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, (gptr) 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, (gptr) option, idx);
+}
+
+/************************************************************************/
/*
tries to find next word in the text
@@ -41,7 +180,7 @@ enum { ALPHANUM= 1, NONSPACE };
*/
inline void get_word(const char **text, uint *word_len,
- int seek_method= ALPHANUM)
+ enum_seek_method seek_method= ALPHANUM)
{
const char *word_end;
@@ -51,13 +190,23 @@ inline void get_word(const char **text, uint *word_len,
word_end= *text;
- if (seek_method == ALPHANUM)
+ switch (seek_method) {
+ case ALPHANUM:
while (my_isalnum(default_charset_info, *word_end))
++word_end;
- else
+ break;
+ case NONSPACE:
while (!my_isspace(default_charset_info, *word_end) &&
(*word_end != '\0'))
++word_end;
+ break;
+ case OPTION_NAME:
+ while (my_isalnum(default_charset_info, *word_end) ||
+ *word_end == '-' ||
+ *word_end == '_')
+ ++word_end;
+ break;
+ }
*word_len= word_end - *text;
}
diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc
index 64bb6a6485f..643a50625a1 100644
--- a/server-tools/instance-manager/parse_output.cc
+++ b/server-tools/instance-manager/parse_output.cc
@@ -14,13 +14,15 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <my_global.h>
-#include "parse.h"
#include "parse_output.h"
-#include <stdio.h>
+#include <my_global.h>
#include <my_sys.h>
#include <m_string.h>
+
+#include <stdio.h>
+
+#include "parse.h"
#include "portability.h"
diff --git a/server-tools/instance-manager/parse_output.h b/server-tools/instance-manager/parse_output.h
index 6a84fabbf17..b86363a4452 100644
--- a/server-tools/instance-manager/parse_output.h
+++ b/server-tools/instance-manager/parse_output.h
@@ -16,6 +16,8 @@
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>
+
#define GET_VALUE 1
#define GET_LINE 2
diff --git a/server-tools/instance-manager/portability.h b/server-tools/instance-manager/portability.h
index 1a3be5705e3..a76cff58893 100644
--- a/server-tools/instance-manager/portability.h
+++ b/server-tools/instance-manager/portability.h
@@ -16,11 +16,25 @@
/*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
+
+#else /* ! __WIN__ */
+
+#define NEWLINE "\n"
+#define NEWLINE_LEN 1
+
#endif /* __WIN__ */
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PORTABILITY_H */
diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc
index d2d6a3f636c..d3cc52ec638 100644
--- a/server-tools/instance-manager/priv.cc
+++ b/server-tools/instance-manager/priv.cc
@@ -14,10 +14,10 @@
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 "priv.h"
-#include "portability.h"
#if defined(__ia64__) || defined(__ia64)
/*
@@ -43,9 +43,7 @@ bool linuxthreads;
The following string must be less then 80 characters, as
mysql_connection.cc relies on it
*/
-const char mysqlmanager_version[] = "0.2-alpha";
-
-const int mysqlmanager_version_length= sizeof(mysqlmanager_version) - 1;
+const LEX_STRING mysqlmanager_version= { C_STRING_WITH_SIZE("1.0-beta") };
const unsigned char protocol_version= PROTOCOL_VERSION;
diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h
index 52d7aa1d23d..0b393c17ac2 100644
--- a/server-tools/instance-manager/priv.h
+++ b/server-tools/instance-manager/priv.h
@@ -16,13 +16,17 @@
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 <my_pthread.h>
+
#include <sys/types.h>
-#ifdef __WIN__
-#include "portability.h"
-#else
+
+#ifndef __WIN__
#include <unistd.h>
#endif
-#include "my_pthread.h"
+
+#include "portability.h"
/* IM-wide platform-independent defines */
#define SERVER_DEFAULT_PORT 3306
@@ -31,6 +35,21 @@
/* 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;
+
/* the pid of the manager process (of the signal thread on the LinuxThreads) */
extern pid_t manager_pid;
@@ -42,8 +61,7 @@ extern pid_t manager_pid;
extern bool linuxthreads;
#endif
-extern const char mysqlmanager_version[];
-extern const int mysqlmanager_version_length;
+extern const LEX_STRING mysqlmanager_version;
/* MySQL client-server protocol version: substituted from configure */
extern const unsigned char protocol_version;
diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc
index 73e07f993ae..4a8c4d0b88d 100644
--- a/server-tools/instance-manager/protocol.cc
+++ b/server-tools/instance-manager/protocol.cc
@@ -163,7 +163,7 @@ int send_fields(struct st_net *net, LIST *fields)
Buffer send_buff;
char small_buff[4];
uint position= 0;
- NAME_WITH_LENGTH *field;
+ LEX_STRING *field;
/* send the number of fileds */
net_store_length(small_buff, (uint) list_length(fields));
@@ -173,7 +173,7 @@ int send_fields(struct st_net *net, LIST *fields)
while (tmp)
{
position= 0;
- field= (NAME_WITH_LENGTH *) tmp->data;
+ field= (LEX_STRING *) tmp->data;
store_to_protocol_packet(&send_buff,
(char*) "", &position); /* catalog name */
@@ -184,9 +184,9 @@ int send_fields(struct st_net *net, LIST *fields)
store_to_protocol_packet(&send_buff,
(char*) "", &position); /* table name alias */
store_to_protocol_packet(&send_buff,
- field->name, &position); /* column name */
+ field->str, &position); /* column name */
store_to_protocol_packet(&send_buff,
- field->name, &position); /* column name alias */
+ field->str, &position); /* column name alias */
send_buff.reserve(position, 12);
if (send_buff.is_error())
goto err;
diff --git a/server-tools/instance-manager/protocol.h b/server-tools/instance-manager/protocol.h
index f38eac6b079..2c84ad394b4 100644
--- a/server-tools/instance-manager/protocol.h
+++ b/server-tools/instance-manager/protocol.h
@@ -20,11 +20,6 @@
#include <my_list.h>
-typedef struct field {
- char *name;
- uint length;
-} NAME_WITH_LENGTH;
-
/* default field length to be used in various field-realted functions */
enum { DEFAULT_FIELD_LENGTH= 20 };
diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc
index 0091d713a91..a424860548d 100644
--- a/server-tools/instance-manager/thread_registry.cc
+++ b/server-tools/instance-manager/thread_registry.cc
@@ -20,11 +20,12 @@
#include "thread_registry.h"
-#include "log.h"
+#include <my_global.h>
+#include <thr_alarm.h>
-#include <assert.h>
#include <signal.h>
-#include <thr_alarm.h>
+
+#include "log.h"
#ifndef __WIN__
@@ -52,7 +53,7 @@ Thread_info::Thread_info(pthread_t thread_id_arg) :
*/
Thread_registry::Thread_registry() :
- shutdown_in_progress(false)
+ shutdown_in_progress(FALSE)
,sigwait_thread_pid(pthread_self())
{
pthread_mutex_init(&LOCK_thread_registry, 0);
@@ -186,7 +187,7 @@ void Thread_registry::deliver_shutdown()
set_timespec(shutdown_time, 1);
pthread_mutex_lock(&LOCK_thread_registry);
- shutdown_in_progress= true;
+ shutdown_in_progress= TRUE;
#ifndef __WIN__
/* to stop reading from the network we need to flush alarm queue */
diff --git a/server-tools/instance-manager/user_management_commands.cc b/server-tools/instance-manager/user_management_commands.cc
new file mode 100644
index 00000000000..8332d2d38de
--- /dev/null
+++ b/server-tools/instance-manager/user_management_commands.cc
@@ -0,0 +1,408 @@
+#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.
+
+ SYNOPSYS
+ 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.
+
+ SYNOPSYS
+ 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.
+
+ SYNOPSYS
+ 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.
+
+ SYNOPSYS
+ 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.
+
+ SYNOPSYS
+ 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;
+}
+
+/*************************************************************************
+ Passwd_cmd
+*************************************************************************/
+
+int Passwd_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;
+ char scrambled_password_buf[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1];
+
+ 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;
+ char scrambled_password_buf[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1];
+
+ User_map user_map;
+ User *user;
+
+ int err_code;
+
+ if (get_user_name(&user_name))
+ return ERR_CAN_NOT_READ_USER_NAME;
+
+ /* Load the password file. */
+
+ if ((err_code= load_password_file(&user_map)) != ERR_OK)
+ return err_code;
+
+ /* Find the user. */
+
+ user= user_map.find_user(&user_name);
+
+ if (!user)
+ {
+ fprintf(stderr, "Error: user '%s' does not exist.\n",
+ (const char *) user_name.str);
+ return ERR_USER_NOT_FOUND;
+ }
+
+ /* Modify user's password. */
+
+ if (!(password= get_password()))
+ return ERR_CAN_NOT_READ_PASSWORD;
+
+ user->set_password(password);
+
+ /* Save the password file. */
+
+ return save_password_file(&user_map);
+}
+
+
+/*************************************************************************
+ Clean_db_cmd
+*************************************************************************/
+
+int Clean_db_cmd::execute()
+{
+ User_map user_map;
+
+ if (user_map.init())
+ {
+ fprintf(stderr, "Error: can not initialize user map.\n");
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ return save_password_file(&user_map);
+}
+
+
+/*************************************************************************
+ Check_db_cmd
+*************************************************************************/
+
+int Check_db_cmd::execute()
+{
+ User_map user_map;
+
+ return load_password_file(&user_map);
+}
+
+
+/*************************************************************************
+ List_users_cmd
+*************************************************************************/
+
+int List_users_cmd::execute()
+{
+ User_map user_map;
+
+ int err_code;
+
+ /* Load the password file. */
+
+ if ((err_code= load_password_file(&user_map)))
+ return err_code;
+
+ /* Print out registered users. */
+
+ {
+ User_map::Iterator it(&user_map);
+ User *user;
+
+ while ((user= it.next()))
+ fprintf(stderr, "%s\n", (const char *) user->user);
+ }
+
+ return ERR_OK;
+}
diff --git a/server-tools/instance-manager/user_management_commands.h b/server-tools/instance-manager/user_management_commands.h
new file mode 100644
index 00000000000..4bf3546f0a6
--- /dev/null
+++ b/server-tools/instance-manager/user_management_commands.h
@@ -0,0 +1,167 @@
+#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MANAGEMENT_CMD_H
+#define INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MANAGEMENT_CMD_H
+
+/*
+ Copyright (C) 2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ SYNOPSYS
+ execute()
+
+ RETURN
+ See exit_codes.h for possible values.
+ */
+
+ virtual int execute() = 0;
+};
+
+
+/*************************************************************************
+ Passwd_cmd: support for --passwd command-line option.
+*************************************************************************/
+
+class Passwd_cmd : public User_management_cmd
+{
+public:
+ Passwd_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ Add_user_cmd: support for --add-user command-line option.
+*************************************************************************/
+
+class Add_user_cmd : public User_management_cmd
+{
+public:
+ Add_user_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ Drop_user_cmd: support for --drop-user command-line option.
+*************************************************************************/
+
+class Drop_user_cmd : public User_management_cmd
+{
+public:
+ Drop_user_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ Edit_user_cmd: support for --edit-user command-line option.
+*************************************************************************/
+
+class Edit_user_cmd : public User_management_cmd
+{
+public:
+ Edit_user_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ Clean_db_cmd: support for --clean-db command-line option.
+*************************************************************************/
+
+class Clean_db_cmd : public User_management_cmd
+{
+public:
+ Clean_db_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ Check_db_cmd: support for --check-db command-line option.
+*************************************************************************/
+
+class Check_db_cmd : public User_management_cmd
+{
+public:
+ Check_db_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+
+/*************************************************************************
+ List_users_cmd: support for --list-users command-line option.
+*************************************************************************/
+
+class List_users_cmd : public User_management_cmd
+{
+public:
+ List_users_cmd()
+ { }
+
+public:
+ virtual int execute();
+};
+
+#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MANAGEMENT_CMD_H
diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc
index 9cb15307131..2f957be426c 100644
--- a/server-tools/instance-manager/user_map.cc
+++ b/server-tools/instance-manager/user_map.cc
@@ -19,32 +19,31 @@
#endif
#include "user_map.h"
-
-#include <mysql_com.h>
-#include <m_string.h>
-
+#include "exit_codes.h"
#include "log.h"
-struct User
+User::User(const LEX_STRING *user_name_arg, const char *password)
{
- char user[USERNAME_LENGTH + 1];
- uint8 user_length;
- uint8 salt[SCRAMBLE_LENGTH];
- int init(const char *line);
-};
+ user_length= strmake(user, user_name_arg->str, USERNAME_LENGTH + 1) - user;
+ set_password(password);
+}
int User::init(const char *line)
{
const char *name_begin, *name_end, *password;
- int line_ending_len= 1;
+ int password_length;
if (line[0] == '\'' || line[0] == '"')
{
name_begin= line + 1;
name_end= strchr(name_begin, line[0]);
if (name_end == 0 || name_end[1] != ':')
- goto err;
+ {
+ log_info("Error: invalid format (unmatched quote) of user line (%s).",
+ (const char *) line);
+ return 1;
+ }
password= name_end + 2;
}
else
@@ -52,33 +51,47 @@ int User::init(const char *line)
name_begin= line;
name_end= strchr(name_begin, ':');
if (name_end == 0)
- goto err;
+ {
+ log_info("Error: invalid format (no delimiter) of user line (%s).",
+ (const char *) line);
+ return 1;
+ }
password= name_end + 1;
}
+
user_length= name_end - name_begin;
if (user_length > USERNAME_LENGTH)
- goto err;
-
- /*
- assume that newline characater is present
- we support reading password files that end in \n or \r\n on
- either platform.
- */
- if (password[strlen(password)-2] == '\r')
- line_ending_len= 2;
- if (strlen(password) != (uint) (SCRAMBLED_PASSWORD_CHAR_LENGTH +
- line_ending_len))
- goto err;
+ {
+ log_info("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= strlen(password);
+ if (password_length > SCRAMBLED_PASSWORD_CHAR_LENGTH)
+ {
+ log_info("Error: password is too long (%d). Max length: %d. ",
+ "User line: '%s'.",
+ (int) password_length,
+ (int) SCRAMBLED_PASSWORD_CHAR_LENGTH,
+ (const char *) line);
+ return 1;
+ }
memcpy(user, name_begin, user_length);
user[user_length]= 0;
+
+ memcpy(scrambled_password, password, password_length);
+ scrambled_password[password_length]= 0;
+
get_salt_from_password(salt, password);
- log_info("loaded user %s", user);
+
+ log_info("loaded user '%s'.", user);
return 0;
-err:
- log_error("error parsing user and password at line %s", line);
- return 1;
}
@@ -101,30 +114,70 @@ static void delete_user(void *u)
C_MODE_END
+void User_map::Iterator::reset()
+{
+ cur_idx= 0;
+}
+
+
+User *User_map::Iterator::next()
+{
+ if (cur_idx < user_map->hash.records)
+ return (User *) hash_element(&user_map->hash, cur_idx++);
+
+ return NULL;
+}
+
+
int User_map::init()
{
enum { START_HASH_SIZE= 16 };
if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
get_user_key, delete_user, 0))
return 1;
+
+ initialized= TRUE;
+
return 0;
}
+User_map::User_map()
+ :initialized(FALSE)
+{
+}
+
+
User_map::~User_map()
{
- hash_free(&hash);
+ if (initialized)
+ hash_free(&hash);
}
/*
- Load all users from the password file. Must be called once right after
- construction.
- In case of failure, puts error message to the log file and returns 1
+ Load password database.
+
+ SYNOPSYS
+ load()
+ password_file_name [IN] password file path
+ err_msg [OUT] error message
+
+ DESCRIPTION
+ Load all users from the password file. Must be called once right after
+ construction. In case of failure, puts error message to the log file and
+ returns specific error code.
+
+ RETURN
+ 0 on success
+ !0 on error
*/
-int User_map::load(const char *password_file_name)
+int User_map::load(const char *password_file_name, const char **err_msg)
{
+ static const int ERR_MSG_BUF_SIZE = 255;
+ static char err_msg_buf[ERR_MSG_BUF_SIZE];
+
FILE *file;
char line[USERNAME_LENGTH + SCRAMBLED_PASSWORD_CHAR_LENGTH +
2 + /* for possible quotes */
@@ -134,33 +187,172 @@ int User_map::load(const char *password_file_name)
User *user;
int rc= 1;
+ if (my_access(password_file_name, F_OK) != 0)
+ {
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "password file (%s) does not exist",
+ (const char *) password_file_name);
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_PASSWORD_FILE_DOES_NOT_EXIST;
+ }
+
if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0)
{
- /* Probably the password file wasn't specified. Try to leave without it */
- log_info("[WARNING] can't open password file %s: errno=%d, %s", password_file_name,
- errno, strerror(errno));
- return 0;
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "can not open password file (%s): %s",
+ (const char *) password_file_name,
+ (const char *) strerror(errno));
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_IO_ERROR;
}
+ log_info("loading the password database...");
+
while (fgets(line, sizeof(line), file))
{
+ char *user_line= line;
+
+ /*
+ We need to skip EOL-symbols also from the beginning of the line, because
+ if the previous line was ended by \n\r sequence, we get \r in our line.
+ */
+
+ while (user_line[0] == '\r' || user_line[0] == '\n')
+ ++user_line;
+
+ /* Skip EOL-symbols in the end of the line. */
+
+ {
+ char *ptr;
+
+ if ((ptr= strchr(user_line, '\n')))
+ *ptr= 0;
+
+ if ((ptr= strchr(user_line, '\r')))
+ *ptr= 0;
+ }
+
/* skip comments and empty lines */
- if (line[0] == '#' || line[0] == '\n' &&
- (line[1] == '\0' || line[1] == '\r'))
+ if (!user_line[0] || user_line[0] == '#')
continue;
+
if ((user= new User) == 0)
- goto done;
- if (user->init(line) || my_hash_insert(&hash, (byte *) user))
- goto err_init_user;
+ {
+ my_fclose(file, MYF(0));
+
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "out of memory while parsing password file (%s)",
+ (const char *) password_file_name);
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ if (user->init(user_line))
+ {
+ delete user;
+ my_fclose(file, MYF(0));
+
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "password file (%s) corrupted",
+ (const char *) password_file_name);
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_PASSWORD_FILE_CORRUPTED;
+ }
+
+ if (my_hash_insert(&hash, (byte *) user))
+ {
+ delete user;
+ my_fclose(file, MYF(0));
+
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "out of memory while parsing password file (%s)",
+ (const char *) password_file_name);
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_OUT_OF_MEMORY;
+ }
}
- if (feof(file))
- rc= 0;
- goto done;
-err_init_user:
- delete user;
-done:
+
+ log_info("the password database loaded successfully.");
+
my_fclose(file, MYF(0));
- return rc;
+
+ if (err_msg)
+ *err_msg= NULL;
+
+ return ERR_OK;
+}
+
+
+int User_map::save(const char *password_file_name, const char **err_msg)
+{
+ static const int ERR_MSG_BUF_SIZE = 255;
+ static char err_msg_buf[ERR_MSG_BUF_SIZE];
+
+ FILE *file;
+
+ if ((file= my_fopen(password_file_name, O_WRONLY | O_TRUNC | O_BINARY,
+ MYF(0))) == 0)
+ {
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "can not open password file (%s) for writing: %s",
+ (const char *) password_file_name,
+ (const char *) strerror(errno));
+ *err_msg= err_msg_buf;
+ }
+
+ return ERR_IO_ERROR;
+ }
+
+ {
+ User_map::Iterator it(this);
+ User *user;
+
+ while ((user= it.next()))
+ {
+ if (fprintf(file, "%s:%s\n", (const char *) user->user,
+ (const char *) user->scrambled_password) < 0)
+ {
+ if (err_msg)
+ {
+ snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
+ "can not write to password file (%s): %s",
+ (const char *) password_file_name,
+ (const char *) strerror(errno));
+ *err_msg= err_msg_buf;
+ }
+
+ my_fclose(file, MYF(0));
+
+ return ERR_IO_ERROR;
+ }
+ }
+ }
+
+ my_fclose(file, MYF(0));
+
+ return ERR_OK;
}
@@ -172,13 +364,33 @@ done:
2 - user not found
*/
-int User_map::authenticate(const char *user_name, uint length,
+int User_map::authenticate(const LEX_STRING *user_name,
const char *scrambled_password,
const char *scramble) const
{
- const User *user= (const User *) hash_search((HASH *) &hash,
- (byte *) user_name, length);
- if (user)
- return check_scramble(scrambled_password, scramble, user->salt);
- return 2;
+ const User *user= find_user(user_name);
+ return user ? check_scramble(scrambled_password, scramble, user->salt) : 2;
+}
+
+
+User *User_map::find_user(const LEX_STRING *user_name)
+{
+ return (User*) hash_search(&hash, (byte*) 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, (byte*) user) == 0 ? FALSE : TRUE;
+}
+
+
+bool User_map::remove_user(User *user)
+{
+ return hash_delete(&hash, (byte*) user) == 0 ? FALSE : TRUE;
}
diff --git a/server-tools/instance-manager/user_map.h b/server-tools/instance-manager/user_map.h
index 4134017dd9b..de207c11e65 100644
--- a/server-tools/instance-manager/user_map.h
+++ b/server-tools/instance-manager/user_map.h
@@ -18,14 +18,35 @@
#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
*/
@@ -33,15 +54,51 @@
class User_map
{
public:
+ /* User_map iterator */
+
+ class Iterator
+ {
+ public:
+ Iterator(User_map *user_map_arg) :
+ cur_idx(0), user_map(user_map_arg)
+ { }
+
+ public:
+ void reset();
+
+ User *next();
+
+ private:
+ User_map *user_map;
+ uint cur_idx;
+ };
+
+public:
+ User_map();
~User_map();
int init();
- int load(const char *password_file_name);
- int authenticate(const char *user_name, uint length,
+ int load(const char *password_file_name, const char **err_msg);
+ int save(const char *password_file_name, const char **err_msg);
+ int authenticate(const LEX_STRING *user_name,
const char *scrambled_password,
const char *scramble) const;
+
+ const User *find_user(const LEX_STRING *user_name) const;
+ User *find_user(const LEX_STRING *user_name);
+
+ bool add_user(User *user);
+ bool remove_user(User *user);
+
+private:
+ User_map(const User_map &);
+ User_map &operator =(const User_map &);
+
private:
HASH hash;
+ bool initialized;
+
+ friend class Iterator;
};
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H
diff --git a/sql/sp.cc b/sql/sp.cc
index 7fc388860d1..77d91be28b9 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -409,15 +409,13 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
ulong old_sql_mode= thd->variables.sql_mode;
ha_rows old_select_limit= thd->variables.select_limit;
sp_rcontext *old_spcont= thd->spcont;
-
+
char definer_user_name_holder[USERNAME_LENGTH + 1];
- LEX_STRING_WITH_INIT definer_user_name(definer_user_name_holder,
- USERNAME_LENGTH);
+ LEX_STRING definer_user_name= { definer_user_name_holder, USERNAME_LENGTH };
char definer_host_name_holder[HOSTNAME_LENGTH + 1];
- LEX_STRING_WITH_INIT definer_host_name(definer_host_name_holder,
- HOSTNAME_LENGTH);
-
+ LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH };
+
int ret;
thd->variables.sql_mode= sql_mode;
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 47d4326d780..2707cb76281 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1850,10 +1850,10 @@ void
sp_head::set_definer(const char *definer, uint definerlen)
{
char user_name_holder[USERNAME_LENGTH + 1];
- LEX_STRING_WITH_INIT user_name(user_name_holder, USERNAME_LENGTH);
+ LEX_STRING user_name= { user_name_holder, USERNAME_LENGTH };
char host_name_holder[HOSTNAME_LENGTH + 1];
- LEX_STRING_WITH_INIT host_name(host_name_holder, HOSTNAME_LENGTH);
+ LEX_STRING host_name= { host_name_holder, HOSTNAME_LENGTH };
parse_user(definer, definerlen, user_name.str, &user_name.length,
host_name.str, &host_name.length);
diff --git a/sql/spatial.cc b/sql/spatial.cc
index e91653f79d5..9f1f05aa18f 100644
--- a/sql/spatial.cc
+++ b/sql/spatial.cc
@@ -34,8 +34,11 @@ static Geometry::Class_info **ci_collection_end=
Geometry::Class_info::Class_info(const char *name, int type_id,
void(*create_func)(void *)):
- m_name(name, strlen(name)), m_type_id(type_id), m_create_func(create_func)
+ m_type_id(type_id), m_create_func(create_func)
{
+ m_name.str= (char *) name;
+ m_name.length= strlen(name);
+
ci_collection[type_id]= this;
}
diff --git a/sql/spatial.h b/sql/spatial.h
index a6f74a1ada0..36949ff5014 100644
--- a/sql/spatial.h
+++ b/sql/spatial.h
@@ -200,7 +200,7 @@ public:
class Class_info
{
public:
- LEX_STRING_WITH_INIT m_name;
+ LEX_STRING m_name;
int m_type_id;
void (*m_create_func)(void *);
Class_info(const char *name, int type_id, void(*create_func)(void *));
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 0659f684afe..ddae6368228 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -24,8 +24,6 @@
#define NOT_FIXED_DEC 31
#endif
-#define STRING_WITH_LEN(X) ((const char*) X), ((uint) (sizeof(X) - 1))
-
class String;
int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index b2b6b115f7d..60a64f9fc1f 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -1412,8 +1412,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
}
if (table.triggers)
{
- LEX_STRING_WITH_INIT old_table_name(old_table, strlen(old_table));
- LEX_STRING_WITH_INIT new_table_name(new_table, strlen(new_table));
+ LEX_STRING old_table_name= { (char *) STRING_WITH_LEN(old_table) };
+ LEX_STRING new_table_name= { (char *) STRING_WITH_LEN(new_table) };
/*
Since triggers should be in the same schema as their subject tables
moving table with them between two schemas raises too many questions.
diff --git a/sql/structs.h b/sql/structs.h
index e369d8ed7e8..dc835db1e99 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -20,22 +20,6 @@
struct st_table;
class Field;
-typedef struct st_lex_string
-{
- char *str;
- uint length;
-} LEX_STRING;
-
-typedef struct st_lex_string_with_init :public st_lex_string
-{
- st_lex_string_with_init(const char *str_arg, uint length_arg)
- {
- str= (char*) str_arg;
- length= length_arg;
- }
-} LEX_STRING_WITH_INIT;
-
-
typedef struct st_date_time_format {
uchar positions[8];
char time_separator; /* Separator between hour and minute */
diff --git a/support-files/mysql.server.sh b/support-files/mysql.server.sh
index bf17375c0eb..bd16297f8e7 100644
--- a/support-files/mysql.server.sh
+++ b/support-files/mysql.server.sh
@@ -265,7 +265,10 @@ case "$mode" in
then
# Give extra arguments to mysqld with the my.cnf file. This script may
# be overwritten at next upgrade.
- $manager --user=$user --pid-file=$pid_file >/dev/null 2>&1 &
+ "$manager" \
+ --mysqld-safe-compatible \
+ --user="$user" \
+ --pid-file="$pid_file" >/dev/null 2>&1 &
wait_for_pid created
# Make lock for RedHat / SuSE