Commit 8b4fcf6e authored by unknown's avatar unknown

This is an implementation of two WL items:

  - WL#3158: IM: Instance configuration extensions;
  - WL#3159: IM: --bootstrap and --start-default-instance modes

The following new statements have been added:
  - CREATE INSTANCE;
  - DROP INSTANCE;

The behaviour of the following statements have been changed:
  - SET;
  - UNSET;
  - FLUSH INSTANCES;
  - SHOW INSTANCES;
  - SHOW INSTANCE OPTIONS;


BitKeeper/deleted/.del-im_options_set.imtest~b53d9d60e5684833:
  Delete: mysql-test/t/im_options_set.imtest
BitKeeper/deleted/.del-im_options_set.result~59278f56be61d921:
  Delete: mysql-test/r/im_options_set.result
BitKeeper/deleted/.del-im_options_unset.imtest~768eb186b51d0048:
  Delete: mysql-test/t/im_options_unset.imtest
BitKeeper/deleted/.del-im_options_unset.result~20a4790cd3c70a4f:
  Delete: mysql-test/r/im_options_unset.result
client/get_password.c:
  Change prototype to avoid casting when using C-strings (char *).
include/m_string.h:
  Moved LEX_STRING to global header from sql/ to be accessible
  from all components (IM for one).
include/my_sys.h:
  Added constants for modify_defaults_file().
include/mysql_com.h:
  Removed duplicated declarations. my_sys.h should be used instead.
libmysql/get_password.c:
  Change prototype to avoid casting when using C-strings (char *).
mysql-test/mysql-test-run.pl:
  Added environment variables to be used from tests.
mysql-test/r/im_daemon_life_cycle.result:
  Column name has been changed in SHOW INSTANCES.
mysql-test/r/im_life_cycle.result:
  1. Column name has been changed in SHOW INSTANCES.
  2. Removed redundant SHOW INSTANCE STATUS statements.
mysql-test/r/im_utils.result:
  Updated the result file.
mysql-test/t/im_daemon_life_cycle-im.opt:
  Set minimal monitoring interval for Instance Manager to speed up testing.
mysql-test/t/im_daemon_life_cycle.imtest:
  Get Instance Manager and managed mysqld-instances enough time to start.
mysql-test/t/im_life_cycle.imtest:
  1. Polishing;
  2. Fixed a test error in 1.1.2.
mysql-test/t/im_utils.imtest:
  Get Instance Manager and managed mysqld-instances enough time to start.
mysys/default.c:
  Pass the name of the section to the handler function as well.
mysys/default_modify.c:
  Added REMOVE_SECTION functionality.
server-tools/instance-manager/IMService.cpp:
  Polishing: be more verbose.
server-tools/instance-manager/IMService.h:
  Polishing: added copyright.
server-tools/instance-manager/Makefile.am:
  Added new files.
server-tools/instance-manager/WindowsService.cpp:
  Polishing: according to The Coding Style, TRUE/FALSE must be
  used instead of true/false.
server-tools/instance-manager/WindowsService.h:
  Polishing: added copyright.
server-tools/instance-manager/command.h:
  Polishing: provide a comment for the main operation of "Command" class.
server-tools/instance-manager/commands.cc:
  1. Added support for CREATE INSTANCE, DROP INSTANCE statements;
  2. Added "deprecated" column in output of SHOW INSTANCE OPTIONS;
  3. Modified the behaviour of SET/UNSET, FLUSH INSTANCES statements;
server-tools/instance-manager/commands.h:
  1. Added support for CREATE INSTANCE, DROP INSTANCE statements;
  2. Added "deprecated" column in output of SHOW INSTANCE OPTIONS;
  3. Modified the behaviour of SET/UNSET, FLUSH INSTANCES statements;
server-tools/instance-manager/guardian.cc:
  Added operations to retrieve state of managed instances.
server-tools/instance-manager/guardian.h:
  Added operations to retrieve state of managed instances.
server-tools/instance-manager/instance.cc:
  1. Provided an operation to check validity of instance name.
  2. Added an attribute to distiguish mysqld-instances,
     whose configuration should be kept backward-compatible.
server-tools/instance-manager/instance.h:
  1. Provided an operation to check validity of instance name.
  2. Added an attribute to distiguish mysqld-instances,
     whose configuration should be kept backward-compatible.
server-tools/instance-manager/instance_map.cc:
  1. Used the operation to check validity of instance name;
  2. Added operations to manage instances.
server-tools/instance-manager/instance_map.h:
  Added operations to manage instances.
server-tools/instance-manager/instance_options.cc:
  Changed Instance_options so that it will be possible to manage
  options on the fly.
server-tools/instance-manager/instance_options.h:
  Changed Instance_options so that it will be possible to manage
  options on the fly.
server-tools/instance-manager/listener.cc:
  1. Remove reference to the instance of Options;
  2. Use new Options naming scheme.
server-tools/instance-manager/listener.h:
  Remove reference to the instance of Options;
server-tools/instance-manager/log.cc:
  Polishing: use TRUE/FALSE instead of true/false.
server-tools/instance-manager/manager.cc:
  Added a common for IM operation to work with configuration file.
server-tools/instance-manager/manager.h:
  Added a common for IM operation to work with configuration file.
server-tools/instance-manager/messages.cc:
  Added messages for new errors.
server-tools/instance-manager/mysql_connection.cc:
  1. Move a constant to common place.
  2. Polishing.
server-tools/instance-manager/mysql_manager_error.h:
  Added new errors.
server-tools/instance-manager/mysqlmanager.cc:
  1. Use error code from Options::load();
  2. Eliminate type-casting warning on Windows.
server-tools/instance-manager/options.cc:
  Added support for user-management command-line options.
server-tools/instance-manager/options.h:
  Added support for user-management command-line options.
server-tools/instance-manager/parse.cc:
  1. Added support of new statements:
     - CREATE INSTANCE;
     - DROP INSTANCE.
  2. Modified SET/UNSET.
server-tools/instance-manager/parse.h:
  1. Added support of new statements:
     - CREATE INSTANCE;
     - DROP INSTANCE.
  2. Modified SET/UNSET.
server-tools/instance-manager/parse_output.cc:
  Sorted out header files.
server-tools/instance-manager/parse_output.h:
  Sorted out header files.
server-tools/instance-manager/portability.h:
  1. Added constants for Windows.
  2. Moved system-dependent defines from instance_options.cc.
server-tools/instance-manager/priv.cc:
  Updated version.
server-tools/instance-manager/priv.h:
  Added some global constants.
server-tools/instance-manager/protocol.cc:
  Replaced NAME_WITH_LENGTH by LEX_STRING.
server-tools/instance-manager/protocol.h:
  Replaced NAME_WITH_LENGTH by LEX_STRING.
server-tools/instance-manager/thread_registry.cc:
  Polishing: use TRUE/FALSE instead of true/false.
server-tools/instance-manager/user_map.cc:
  Added support for managing password database.
server-tools/instance-manager/user_map.h:
  Added support for managing password database.
sql/sp.cc:
  Replaced LEX_STRING_WITH_INIT by LEX_STRING + struct initialization.
sql/sp_head.cc:
  Replaced LEX_STRING_WITH_INIT by LEX_STRING + struct initialization.
sql/spatial.cc:
  Removed LEX_STRING_WITH_INIT.
sql/spatial.h:
  Removed LEX_STRING_WITH_INIT.
sql/sql_string.h:
  Moved STRING_WITH_LEN() macro out from sql (to m_string.h).
sql/sql_trigger.cc:
  Moved STRING_WITH_LEN() macro out from sql (to m_string.h).
sql/structs.h:
  Removed LEX_STRING_WITH_INIT.
support-files/mysql.server.sh:
  Instruct Instance Manager to work in mysqld-safe compatible mode
  for backward compatibility.
mysql-test/r/im_cmd_line.result:
  Added result file.
mysql-test/r/im_instance_conf.result:
  Added result file.
mysql-test/r/im_options.result:
  Added result file.
mysql-test/t/im_cmd_line.imtest:
  IM command-line options test.
mysql-test/t/im_instance_conf-im.opt:
  Set minimal monitoring interval for Instance Manager to speed up testing.
mysql-test/t/im_instance_conf.imtest:
  Added a new test case for checking instance-management.
mysql-test/t/im_life_cycle-im.opt:
  Set minimal monitoring interval for Instance Manager to speed up testing.
mysql-test/t/im_options.imtest:
  Join im_options_set and im_options_unset and add new tests.
mysql-test/t/im_utils-im.opt:
  Set minimal monitoring interval for Instance Manager to speed up testing.
server-tools/instance-manager/exit_codes.h:
  New file for defining exit codes for user-management mode.
server-tools/instance-manager/user_management_commands.cc:
  User-management commands implementation.
server-tools/instance-manager/user_management_commands.h:
  User-management command declarations.
parent 24c005cf
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
/* were just going to fake it here and get input from /* were just going to fake it here and get input from
the keyboard */ the keyboard */
char *get_tty_password(char *opt_message) char *get_tty_password(const char *opt_message)
{ {
char to[80]; char to[80];
char *pos=to,*end=to+sizeof(to)-1; char *pos=to,*end=to+sizeof(to)-1;
...@@ -150,7 +150,7 @@ static void get_password(char *to,uint length,int fd,bool echo) ...@@ -150,7 +150,7 @@ static void get_password(char *to,uint length,int fd,bool echo)
#endif /* ! HAVE_GETPASS */ #endif /* ! HAVE_GETPASS */
char *get_tty_password(char *opt_message) char *get_tty_password(const char *opt_message)
{ {
#ifdef HAVE_GETPASS #ifdef HAVE_GETPASS
char *passbuff; char *passbuff;
......
...@@ -246,4 +246,16 @@ extern int my_snprintf(char* to, size_t n, const char* fmt, ...); ...@@ -246,4 +246,16 @@ extern int my_snprintf(char* to, size_t n, const char* fmt, ...);
#if defined(__cplusplus) #if defined(__cplusplus)
} }
#endif #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 #endif
...@@ -76,6 +76,10 @@ extern int NEAR my_errno; /* Last error in mysys */ ...@@ -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_CHECK_ERROR 1 /* Params to my_end; Check open-close */
#define MY_GIVE_INFO 2 /* Give time info about process*/ #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_HIGHBYTE 8 /* Shift for colours */
#define ME_NOCUR 1 /* Don't use curses message */ #define ME_NOCUR 1 /* Don't use curses message */
#define ME_OLDWIN 2 /* Use old window */ #define ME_OLDWIN 2 /* Use old window */
......
...@@ -423,17 +423,11 @@ char *octet2hex(char *to, const char *str, unsigned int len); ...@@ -423,17 +423,11 @@ char *octet2hex(char *to, const char *str, unsigned int len);
/* end of password.c */ /* 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); const char *mysql_errno_to_sqlstate(unsigned int mysql_errno);
/* Some other useful functions */ /* 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); my_bool my_thread_init(void);
void my_thread_end(void); void my_thread_end(void);
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
#define _cputs(A) putstring(A) #define _cputs(A) putstring(A)
#endif #endif
char *get_tty_password(char *opt_message) char *get_tty_password(const char *opt_message)
{ {
char to[80]; char to[80];
char *pos=to,*end=to+sizeof(to)-1; char *pos=to,*end=to+sizeof(to)-1;
...@@ -159,7 +159,7 @@ static void get_password(char *to,uint length,int fd,bool echo) ...@@ -159,7 +159,7 @@ static void get_password(char *to,uint length,int fd,bool echo)
#endif /* ! HAVE_GETPASS */ #endif /* ! HAVE_GETPASS */
char *get_tty_password(char *opt_message) char *get_tty_password(const char *opt_message)
{ {
#ifdef HAVE_GETPASS #ifdef HAVE_GETPASS
char *passbuff; char *passbuff;
......
...@@ -1212,8 +1212,11 @@ sub environment_setup () { ...@@ -1212,8 +1212,11 @@ sub environment_setup () {
$ENV{'NDBCLUSTER_PORT'}= $opt_ndbcluster_port; $ENV{'NDBCLUSTER_PORT'}= $opt_ndbcluster_port;
$ENV{'NDBCLUSTER_PORT_SLAVE'}=$opt_ndbcluster_port_slave; $ENV{'NDBCLUSTER_PORT_SLAVE'}=$opt_ndbcluster_port_slave;
$ENV{'IM_EXE'}= $exe_im;
$ENV{'IM_PATH_PID'}= $instance_manager->{path_pid}; $ENV{'IM_PATH_PID'}= $instance_manager->{path_pid};
$ENV{'IM_PORT'}= $instance_manager->{port}; $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_SOCK'}= $instance_manager->{instances}->[0]->{path_sock};
$ENV{'IM_MYSQLD1_PORT'}= $instance_manager->{instances}->[0]->{port}; $ENV{'IM_MYSQLD1_PORT'}= $instance_manager->{instances}->[0]->{port};
......
--> 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
SHOW INSTANCES; SHOW INSTANCES;
instance_name status instance_name state
mysqld1 online mysqld1 online
mysqld2 offline mysqld2 offline
Killing the process... Killing the process...
......
--------------------------------------------------------------------
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.
--------------------------------------------------------------------
-- 1.1.1.
--------------------------------------------------------------------
SHOW INSTANCES; SHOW INSTANCES;
instance_name status instance_name state
mysqld1 online mysqld1 online
mysqld2 offline mysqld2 offline
SHOW INSTANCE STATUS mysqld1;
instance_name status version_number version --------------------------------------------------------------------
mysqld1 online VERSION_NUMBER VERSION -- 1.1.2.
SHOW INSTANCE STATUS mysqld2; --------------------------------------------------------------------
instance_name status version_number version
mysqld2 offline VERSION_NUMBER VERSION
START INSTANCE mysqld2; START INSTANCE mysqld2;
SHOW INSTANCES; SHOW INSTANCES;
instance_name status instance_name state
mysqld1 online mysqld1 online
mysqld2 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'; SHOW VARIABLES LIKE 'port';
Variable_name Value Variable_name Value
port IM_MYSQLD1_PORT port IM_MYSQLD2_PORT
--------------------------------------------------------------------
-- 1.1.3.
--------------------------------------------------------------------
STOP INSTANCE mysqld2; STOP INSTANCE mysqld2;
SHOW INSTANCES; SHOW INSTANCES;
instance_name status instance_name state
mysqld1 online mysqld1 online
mysqld2 offline mysqld2 offline
SHOW INSTANCE STATUS mysqld1; SHOW INSTANCE STATUS mysqld1;
instance_name status version_number version instance_name state version_number version mysqld_compatible
mysqld1 online VERSION_NUMBER VERSION mysqld1 online VERSION_NUMBER VERSION no
SHOW INSTANCE STATUS mysqld2; SHOW INSTANCE STATUS mysqld2;
instance_name status version_number version instance_name state version_number version mysqld_compatible
mysqld2 offline VERSION_NUMBER VERSION mysqld2 offline VERSION_NUMBER VERSION no
--------------------------------------------------------------------
-- 1.1.4.
--------------------------------------------------------------------
START INSTANCE mysqld3; START INSTANCE mysqld3;
ERROR HY000: Bad instance name. Check that the instance with such a name exists ERROR HY000: Bad instance name. Check that the instance with such a name exists
START INSTANCE mysqld1; START INSTANCE mysqld1;
ERROR HY000: The instance is already started ERROR HY000: The instance is already started
--------------------------------------------------------------------
-- 1.1.5.
--------------------------------------------------------------------
STOP INSTANCE mysqld3; STOP INSTANCE mysqld3;
ERROR HY000: Bad instance name. Check that the instance with such a name exists ERROR HY000: Bad instance name. Check that the instance with such a name exists
--------------------------------------------------------------------
-- 1.1.6.
--------------------------------------------------------------------
SHOW INSTANCES; SHOW INSTANCES;
instance_name status instance_name state
mysqld1 online mysqld1 online
mysqld2 offline mysqld2 offline
Killing the process... Killing the process...
Sleeping... Sleeping...
Success: the process was restarted. Success: the process was restarted.
--------------------------------------------------------------------
-- 1.1.7.
--------------------------------------------------------------------
SHOW INSTANCES; SHOW INSTANCES;
instance_name status instance_name state
mysqld1 online mysqld1 online
mysqld2 offline mysqld2 offline
START INSTANCE mysqld2; START INSTANCE mysqld2;
SHOW INSTANCES; SHOW INSTANCES;
instance_name status instance_name state
mysqld1 online mysqld1 online
mysqld2 online mysqld2 online
Killing the process... Killing the process...
Sleeping... Sleeping...
Success: the process was killed. Success: the process was killed.
SHOW INSTANCES; SHOW INSTANCES;
instance_name status instance_name state
mysqld1 online mysqld1 online
mysqld2 offline mysqld2 offline
--------------------------------------------------------------------
-- 1.1.8.
--------------------------------------------------------------------
SHOW INSTANCE STATUS; 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 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; 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 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; STOP INSTANCE mysqld1,mysqld2,mysqld3;
......
--------------------------------------------------------------------
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;
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
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
SHOW INSTANCES; SHOW INSTANCES;
instance_name status instance_name state
mysqld1 online mysqld1 online
mysqld2 offline mysqld2 offline
SHOW INSTANCE OPTIONS mysqld1; SHOW INSTANCE OPTIONS mysqld1;
option_name value option_name value
instance_name VALUE instance_name VALUE
mysqld-path VALUE
socket VALUE socket VALUE
pid-file VALUE pid-file VALUE
port VALUE port VALUE
...@@ -25,8 +24,6 @@ log-output VALUE ...@@ -25,8 +24,6 @@ log-output VALUE
SHOW INSTANCE OPTIONS mysqld2; SHOW INSTANCE OPTIONS mysqld2;
option_name value option_name value
instance_name VALUE instance_name VALUE
mysqld-path VALUE
nonguarded VALUE
socket VALUE socket VALUE
pid-file VALUE pid-file VALUE
port VALUE port VALUE
...@@ -42,6 +39,7 @@ skip-stack-trace VALUE ...@@ -42,6 +39,7 @@ skip-stack-trace VALUE
skip-innodb VALUE skip-innodb VALUE
skip-bdb VALUE skip-bdb VALUE
skip-ndbcluster VALUE skip-ndbcluster VALUE
nonguarded VALUE
log-output VALUE log-output VALUE
START INSTANCE mysqld2; START INSTANCE mysqld2;
STOP INSTANCE mysqld2; STOP INSTANCE mysqld2;
......
###########################################################################
#
# 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
--run-as-service --run-as-service
--log=$MYSQLTEST_VARDIR/log/im.log --log=$MYSQLTEST_VARDIR/log/im.log
--monitoring-interval=1
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
########################################################################### ###########################################################################
--sleep 3
# should be longer than monitoring interval and enough to start instance.
SHOW INSTANCES; SHOW INSTANCES;
--exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted --exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted
###########################################################################
#
# 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;
...@@ -17,11 +17,15 @@ ...@@ -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; 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; ...@@ -33,20 +37,22 @@ SHOW INSTANCE STATUS mysqld2;
# #
########################################################################### ###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.2.
--echo --------------------------------------------------------------------
START INSTANCE mysqld2; START INSTANCE mysqld2;
# FIXME # FIXME: START INSTANCE should be synchronous.
--sleep 3 --sleep 3
# should be longer than monitoring interval and enough to start instance.
SHOW INSTANCES; 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 --connection mysql_con
--replace_result $IM_MYSQLD1_PORT IM_MYSQLD1_PORT --replace_result $IM_MYSQLD2_PORT IM_MYSQLD2_PORT
SHOW VARIABLES LIKE 'port'; SHOW VARIABLES LIKE 'port';
--connection default --connection default
...@@ -61,9 +67,15 @@ SHOW VARIABLES LIKE 'port'; ...@@ -61,9 +67,15 @@ SHOW VARIABLES LIKE 'port';
# #
########################################################################### ###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.3.
--echo --------------------------------------------------------------------
STOP INSTANCE mysqld2; STOP INSTANCE mysqld2;
# FIXME # FIXME: STOP INSTANCE should be synchronous.
--sleep 3 --sleep 3
# should be longer than monitoring interval and enough to stop instance.
SHOW INSTANCES; SHOW INSTANCES;
--replace_column 3 VERSION_NUMBER 4 VERSION --replace_column 3 VERSION_NUMBER 4 VERSION
...@@ -81,16 +93,17 @@ SHOW INSTANCE STATUS mysqld2; ...@@ -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; START INSTANCE mysqld3;
--error 3002 --error 3002 # ER_INSTANCE_ALREADY_STARTED
START INSTANCE mysqld1; 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' # 1.1.5. Check that Instance Manager reports correct errors for 'STOP INSTANCE'
...@@ -101,39 +114,54 @@ START INSTANCE mysqld1; ...@@ -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; STOP INSTANCE mysqld3;
# TODO: IM should be fixed. # TODO: IM should be fixed.
# BUG#12673: Instance Manager allows to stop the instance many times # BUG#12673: Instance Manager allows to stop the instance many times
# --error 3002 # --error 3002 # ER_INSTANCE_ALREADY_STARTED
# STOP INSTANCE mysqld2; # 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. # 1.1.6. Check that Instance Manager is able to restart guarded instances.
# #
########################################################################### ###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.6.
--echo --------------------------------------------------------------------
SHOW INSTANCES; SHOW INSTANCES;
--exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_MYSQLD1_PATH_PID restarted --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. # 1.1.7. Check that Instance Manager does not restart non-guarded instance.
# #
########################################################################### ###########################################################################
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.7.
--echo --------------------------------------------------------------------
SHOW INSTANCES; SHOW INSTANCES;
START INSTANCE mysqld2; START INSTANCE mysqld2;
# FIXME # FIXME: START INSTANCE should be synchronous.
--sleep 3 --sleep 3
# should be longer than monitoring interval and enough to start instance.
SHOW INSTANCES; SHOW INSTANCES;
...@@ -147,7 +175,13 @@ SHOW INSTANCES; ...@@ -147,7 +175,13 @@ SHOW INSTANCES;
# incomplete SHOW INSTANCE STATUS command. # incomplete SHOW INSTANCE STATUS command.
# #
########################################################################### ###########################################################################
--error 1149
--echo
--echo --------------------------------------------------------------------
--echo -- 1.1.8.
--echo --------------------------------------------------------------------
--error ER_SYNTAX_ERROR
SHOW INSTANCE STATUS; SHOW INSTANCE STATUS;
# #
...@@ -159,8 +193,13 @@ SHOW INSTANCE STATUS; ...@@ -159,8 +193,13 @@ SHOW INSTANCE STATUS;
# a list as argument. # a list as argument.
# #
--error 1149 --echo
--echo --------------------------------------------------------------------
--echo -- BUG#12813
--echo --------------------------------------------------------------------
--error ER_SYNTAX_ERROR
START INSTANCE mysqld1,mysqld2,mysqld3; START INSTANCE mysqld1,mysqld2,mysqld3;
--error 1149 --error ER_SYNTAX_ERROR
STOP INSTANCE mysqld1,mysqld2,mysqld3; STOP INSTANCE mysqld1,mysqld2,mysqld3;
###########################################################################
#
# 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;
###########################################################################
#
# 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;
###########################################################################
#
# 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;
--monitoring-interval=1
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
# - the second instance is offline; # - the second instance is offline;
# #
--sleep 3
# should be longer than monitoring interval and enough to start instance.
SHOW INSTANCES; SHOW INSTANCES;
# #
...@@ -41,8 +44,9 @@ SHOW INSTANCE OPTIONS mysqld2; ...@@ -41,8 +44,9 @@ SHOW INSTANCE OPTIONS mysqld2;
START INSTANCE mysqld2; START INSTANCE mysqld2;
# FIXME # FIXME: START INSTANCE should be synchronous.
-- sleep 3 --sleep 3
# should be longer than monitoring interval and enough to start instance.
STOP INSTANCE mysqld2; STOP INSTANCE mysqld2;
......
...@@ -244,7 +244,8 @@ err: ...@@ -244,7 +244,8 @@ err:
handle_option_ctx structure. handle_option_ctx structure.
group_name The name of the group the option belongs to. group_name The name of the group the option belongs to.
option The very option to be processed. It is already 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 DESCRIPTION
This handler checks whether a group is one of the listed and adds an option 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, ...@@ -263,6 +264,9 @@ static int handle_default_option(void *in_ctx, const char *group_name,
char *tmp; char *tmp;
struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx; 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 (find_type((char *)group_name, ctx->group, 3))
{ {
if (!(tmp= alloc_root(ctx->alloc, (uint) strlen(option) + 1))) 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, ...@@ -719,6 +723,10 @@ static int search_default_file_with_ext(Process_option_func opt_handler,
end[0]=0; end[0]=0;
strnmov(curr_gr, ptr, min((uint) (end-ptr)+1, 4096)); 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; continue;
} }
if (!found_group) if (!found_group)
......
...@@ -40,11 +40,13 @@ static char *add_option(char *dst, const char *option_value, ...@@ -40,11 +40,13 @@ static char *add_option(char *dst, const char *option_value,
SYNOPSYS SYNOPSYS
modify_defaults_file() modify_defaults_file()
file_location The location of configuration file to edit file_location The location of configuration file to edit
option option to look for option The name of the option to look for (can be NULL)
option value The value of the option we would like to set option value The value of the option we would like to set (can be NULL)
section_name the name of the section section_name The name of the section (must be NOT NULL)
remove_option This is true if we want to remove the option. remove_option This defines what we want to remove:
False otherwise. - MY_REMOVE_NONE -- nothing to remove;
- MY_REMOVE_OPTION -- remove the specified option;
- MY_REMOVE_SECTION -- remove the specified section;
IMPLEMENTATION IMPLEMENTATION
We open the option file first, then read the file line-by-line, 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 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, ...@@ -67,7 +69,9 @@ int modify_defaults_file(const char *file_location, const char *option,
FILE *cnf_file; FILE *cnf_file;
MY_STAT file_stat; MY_STAT file_stat;
char linebuff[BUFF_SIZE], *src_ptr, *dst_ptr, *file_buffer; 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; my_bool in_section= FALSE, opt_applied= 0;
uint reserve_extended; uint reserve_extended;
uint new_opt_len; uint new_opt_len;
...@@ -81,8 +85,11 @@ int modify_defaults_file(const char *file_location, const char *option, ...@@ -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))) if (my_fstat(fileno(cnf_file), &file_stat, MYF(0)))
goto malloc_err; goto malloc_err;
opt_len= (uint) strlen(option); if (option && option_value)
optval_len= (uint) strlen(option_value); {
opt_len= (uint) strlen(option);
optval_len= (uint) strlen(option_value);
}
new_opt_len= opt_len + 1 + optval_len + NEWLINE_LEN; 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, ...@@ -119,8 +126,8 @@ int modify_defaults_file(const char *file_location, const char *option,
continue; continue;
} }
/* correct the option */ /* correct the option (if requested) */
if (in_section && !strncmp(src_ptr, option, opt_len) && if (option && in_section && !strncmp(src_ptr, option, opt_len) &&
(*(src_ptr + opt_len) == '=' || (*(src_ptr + opt_len) == '=' ||
my_isspace(&my_charset_latin1, *(src_ptr + opt_len)) || my_isspace(&my_charset_latin1, *(src_ptr + opt_len)) ||
*(src_ptr + opt_len) == '\0')) *(src_ptr + opt_len) == '\0'))
...@@ -143,7 +150,12 @@ int modify_defaults_file(const char *file_location, const char *option, ...@@ -143,7 +150,12 @@ int modify_defaults_file(const char *file_location, const char *option,
} }
else 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 == '[') if (in_section && !opt_applied && *src_ptr == '[')
{ {
dst_ptr= add_option(dst_ptr, option_value, option, remove_option); 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, ...@@ -153,7 +165,10 @@ int modify_defaults_file(const char *file_location, const char *option,
for (; nr_newlines; nr_newlines--) for (; nr_newlines; nr_newlines--)
dst_ptr= strmov(dst_ptr, NEWLINE); 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 */ /* Look for a section */
if (*src_ptr == '[') if (*src_ptr == '[')
...@@ -167,18 +182,31 @@ int modify_defaults_file(const char *file_location, const char *option, ...@@ -167,18 +182,31 @@ int modify_defaults_file(const char *file_location, const char *option,
{} {}
if (*src_ptr != ']') if (*src_ptr != ']')
{
in_section= FALSE;
continue; /* Missing closing parenthesis. Assume this was no group */ continue; /* Missing closing parenthesis. Assume this was no group */
}
if (remove_option == MY_REMOVE_SECTION)
dst_ptr= dst_ptr - strlen(linebuff);
in_section= TRUE; in_section= TRUE;
} }
else else
in_section= FALSE; /* mark that this section is of no interest to us */ 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 */ /* 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= strmov(dst_ptr, NEWLINE);
dst_ptr= add_option(dst_ptr, option_value, option, remove_option); dst_ptr= add_option(dst_ptr, option_value, option, remove_option);
opt_applied= 1; opt_applied= 1;
......
...@@ -20,7 +20,7 @@ IMService::~IMService(void) ...@@ -20,7 +20,7 @@ IMService::~IMService(void)
void IMService::Stop() void IMService::Stop()
{ {
ReportStatus(SERVICE_STOP_PENDING); ReportStatus(SERVICE_STOP_PENDING);
// stop the IM work // stop the IM work
raise(SIGTERM); raise(SIGTERM);
} }
...@@ -32,7 +32,7 @@ void IMService::Run(DWORD argc, LPTSTR *argv) ...@@ -32,7 +32,7 @@ void IMService::Run(DWORD argc, LPTSTR *argv)
Options o; Options o;
o.load(argc, argv); o.load(argc, argv);
// init goes here // init goes here
ReportStatus((DWORD)SERVICE_RUNNING); ReportStatus((DWORD)SERVICE_RUNNING);
...@@ -46,13 +46,13 @@ void IMService::Log(const char *msg) ...@@ -46,13 +46,13 @@ void IMService::Log(const char *msg)
log_info(msg); log_info(msg);
} }
int HandleServiceOptions(Options options) int HandleServiceOptions()
{ {
int ret_val= 0; int ret_val= 0;
IMService winService; IMService winService;
if (options.install_as_service) if (Options::Service::install_as_service)
{ {
if (winService.IsInstalled()) if (winService.IsInstalled())
log_info("Service is already installed"); log_info("Service is already installed");
...@@ -64,7 +64,7 @@ int HandleServiceOptions(Options options) ...@@ -64,7 +64,7 @@ int HandleServiceOptions(Options options)
ret_val= 1; ret_val= 1;
} }
} }
else if (options.remove_service) else if (Options::Service::remove_service)
{ {
if (! winService.IsInstalled()) if (! winService.IsInstalled())
log_info("Service is not installed"); log_info("Service is not installed");
...@@ -77,6 +77,19 @@ int HandleServiceOptions(Options options) ...@@ -77,6 +77,19 @@ int HandleServiceOptions(Options options)
} }
} }
else 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; return ret_val;
} }
/*
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 #pragma once
#include "windowsservice.h" #include "windowsservice.h"
...@@ -12,3 +30,5 @@ protected: ...@@ -12,3 +30,5 @@ protected:
void Stop(); void Stop();
void Run(DWORD argc, LPTSTR *argv); void Run(DWORD argc, LPTSTR *argv);
}; };
extern int HandleServiceOptions();
...@@ -76,7 +76,10 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ ...@@ -76,7 +76,10 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
guardian.cc guardian.h \ guardian.cc guardian.h \
parse_output.cc parse_output.h \ parse_output.cc parse_output.h \
mysql_manager_error.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@ \ mysqlmanager_LDADD= @CLIENT_EXTRA_LDFLAGS@ \
liboptions.la \ liboptions.la \
...@@ -90,6 +93,9 @@ mysqlmanager_LDADD= @CLIENT_EXTRA_LDFLAGS@ \ ...@@ -90,6 +93,9 @@ mysqlmanager_LDADD= @CLIENT_EXTRA_LDFLAGS@ \
EXTRA_DIST = WindowsService.cpp WindowsService.h IMService.cpp \ EXTRA_DIST = WindowsService.cpp WindowsService.h IMService.cpp \
IMService.h cmakelists.txt IMService.h cmakelists.txt
AM_CFLAGS = -Werror
AM_CXXFLAGS = -Werror
tags: tags:
ctags -R *.h *.cc ctags -R *.h *.cc
......
...@@ -7,9 +7,9 @@ static WindowsService *gService; ...@@ -7,9 +7,9 @@ static WindowsService *gService;
WindowsService::WindowsService(void) : WindowsService::WindowsService(void) :
statusCheckpoint(0), statusCheckpoint(0),
serviceName(NULL), serviceName(NULL),
inited(false), inited(FALSE),
dwAcceptedControls(SERVICE_ACCEPT_STOP), dwAcceptedControls(SERVICE_ACCEPT_STOP),
debugging(false) debugging(FALSE)
{ {
gService= this; gService= this;
status.dwServiceType= SERVICE_WIN32_OWN_PROCESS; status.dwServiceType= SERVICE_WIN32_OWN_PROCESS;
...@@ -22,11 +22,12 @@ WindowsService::~WindowsService(void) ...@@ -22,11 +22,12 @@ WindowsService::~WindowsService(void)
BOOL WindowsService::Install() BOOL WindowsService::Install()
{ {
bool ret_val= false; bool ret_val= FALSE;
SC_HANDLE newService; SC_HANDLE newService;
SC_HANDLE scm; SC_HANDLE scm;
if (IsInstalled()) return true; if (IsInstalled())
return TRUE;
// determine the name of the currently executing file // determine the name of the currently executing file
char szFilePath[_MAX_PATH]; char szFilePath[_MAX_PATH];
...@@ -34,7 +35,7 @@ BOOL WindowsService::Install() ...@@ -34,7 +35,7 @@ BOOL WindowsService::Install()
// open a connection to the SCM // open a connection to the SCM
if (!(scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE))) if (!(scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
return false; return FALSE;
newService= CreateService(scm, serviceName, displayName, newService= CreateService(scm, serviceName, displayName,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
...@@ -45,7 +46,7 @@ BOOL WindowsService::Install() ...@@ -45,7 +46,7 @@ BOOL WindowsService::Install()
if (newService) if (newService)
{ {
CloseServiceHandle(newService); CloseServiceHandle(newService);
ret_val= true; ret_val= TRUE;
} }
CloseServiceHandle(scm); CloseServiceHandle(scm);
...@@ -56,34 +57,35 @@ BOOL WindowsService::Init() ...@@ -56,34 +57,35 @@ BOOL WindowsService::Init()
{ {
assert(serviceName != NULL); assert(serviceName != NULL);
if (inited) return true; if (inited)
return TRUE;
SERVICE_TABLE_ENTRY stb[] = SERVICE_TABLE_ENTRY stb[] =
{ {
{ (LPSTR)serviceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain}, { (LPSTR)serviceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{ NULL, NULL } { NULL, NULL }
}; };
inited= true; inited= TRUE;
return StartServiceCtrlDispatcher(stb); //register with the Service Manager return StartServiceCtrlDispatcher(stb); //register with the Service Manager
} }
BOOL WindowsService::Remove() BOOL WindowsService::Remove()
{ {
bool ret_val= false; bool ret_val= FALSE;
if (! IsInstalled()) if (!IsInstalled())
return true; return TRUE;
// open a connection to the SCM // open a connection to the SCM
SC_HANDLE scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE); SC_HANDLE scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE);
if (! scm) if (!scm)
return false; return FALSE;
SC_HANDLE service= OpenService(scm, serviceName, DELETE); SC_HANDLE service= OpenService(scm, serviceName, DELETE);
if (service) if (service)
{ {
if (DeleteService(service)) if (DeleteService(service))
ret_val= true; ret_val= TRUE;
DWORD dw= ::GetLastError(); DWORD dw= ::GetLastError();
CloseServiceHandle(service); CloseServiceHandle(service);
} }
...@@ -116,7 +118,8 @@ void WindowsService::SetAcceptedControls(DWORD acceptedControls) ...@@ -116,7 +118,8 @@ void WindowsService::SetAcceptedControls(DWORD acceptedControls)
BOOL WindowsService::ReportStatus(DWORD currentState, DWORD waitHint, BOOL WindowsService::ReportStatus(DWORD currentState, DWORD waitHint,
DWORD dwError) DWORD dwError)
{ {
if(debugging) return TRUE; if (debugging)
return TRUE;
if(currentState == SERVICE_START_PENDING) if(currentState == SERVICE_START_PENDING)
status.dwControlsAccepted= 0; status.dwControlsAccepted= 0;
......
/*
Copyright (C) 2006 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; 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 #pragma once
class WindowsService class WindowsService
......
...@@ -22,10 +22,12 @@ ...@@ -22,10 +22,12 @@
#pragma interface #pragma interface
#endif #endif
/* Class responsible for allocation of im commands. */ /* Class responsible for allocation of IM commands. */
class Instance_map; class Instance_map;
struct st_net;
/* /*
Command - entry point for any command. Command - entry point for any command.
GangOf4: 'Command' design pattern GangOf4: 'Command' design pattern
...@@ -37,8 +39,18 @@ public: ...@@ -37,8 +39,18 @@ public:
Command(Instance_map *instance_map_arg= 0); Command(Instance_map *instance_map_arg= 0);
virtual ~Command(); 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: protected:
Instance_map *instance_map; Instance_map *instance_map;
......
...@@ -14,36 +14,54 @@ ...@@ -14,36 +14,54 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 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 "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 "instance_map.h"
#include "log.h"
#include "manager.h"
#include "messages.h" #include "messages.h"
#include "mysqld_error.h" #include "mysqld_error.h"
#include "mysql_manager_error.h" #include "mysql_manager_error.h"
#include "protocol.h"
#include "buffer.h"
#include "options.h" #include "options.h"
#include "priv.h"
#include "protocol.h"
#include <m_string.h>
#include <m_ctype.h> /*
#include <mysql.h> modify_defaults_to_im_error -- a map of error codes of
#include <my_dir.h> 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 SYNOPSYS
put_to_buff() put_to_buff()
buff buffer to add the string buff buffer to add the string
str string to add str string to add
uint offset in the buff to add a string position offset in the buff to add a string
DESCRIPTION DESCRIPTION
Function to add a string to the buffer. It is different from 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 store_to_protocol_packet, which is used in the protocol.cc.
one also stores the length of the string in a special way. The last one also stores the length of the string in a special way.
This is required for MySQL client/server protocol support only. This is required for MySQL client/server protocol support only.
RETURN RETURN
...@@ -51,7 +69,6 @@ ...@@ -51,7 +69,6 @@
1 - error occured 1 - error occured
*/ */
static inline int put_to_buff(Buffer *buff, const char *str, uint *position) static inline int put_to_buff(Buffer *buff, const char *str, uint *position)
{ {
uint len= strlen(str); uint len= strlen(str);
...@@ -88,749 +105,1615 @@ static int parse_version_number(const char *version_str, char *version, ...@@ -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;
/* if (str.length > MAX_INSTANCE_NAME_SIZE - 1)
The method sends a list of instances in the instance map to the client. str.length= MAX_INSTANCE_NAME_SIZE - 1;
SYNOPSYS strmake(str.str, name->str, str.length);
Show_instances::execute() }
net The network connection to the client.
connection_id Client connection ID
RETURN /**************************************************************************
0 - ok Implementation of Show_instances.
1 - error occured **************************************************************************/
/*
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 */ int err_code;
LIST name, status;
NAME_WITH_LENGTH name_field, status_field; 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; 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_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field; name.data= &name_field;
status_field.name= (char*) "status";
status_field.length= DEFAULT_FIELD_LENGTH; state_field.str= (char *) "state";
status.data= &status_field; state_field.length= DEFAULT_FIELD_LENGTH;
field_list= list_add(NULL, &status); state.data= &state_field;
field_list= list_add(NULL, &state);
field_list= list_add(field_list, &name); 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; Buffer send_buf; /* buffer for packets */
Instance_map::Iterator iterator(instance_map); 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(); if (store_to_protocol_packet(&send_buf, instance_name, &pos) ||
while ((instance= iterator.next())) store_to_protocol_packet(&send_buf, state_name, &pos) ||
my_net_write(net, send_buf.buffer, pos))
{ {
position= 0; err_status= TRUE;
store_to_protocol_packet(&send_buff, instance->options.instance_name, break;
&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;
} }
instance_map->unlock();
} }
if (send_eof(net))
goto err;
if (net_flush(net))
goto err;
return 0; instance_map->unlock();
err: instance_map->guardian->unlock();
return ER_OUT_OF_RESOURCES;
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() || instance_map->guardian->lock();
net_send_ok(net, connection_id, NULL)) 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 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, Abstract_instance_cmd::Abstract_instance_cmd(
const char *name, uint len) Instance_map *instance_map_arg, const LEX_STRING *instance_name_arg)
:Command(instance_map_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 */ instance_map->unlock();
if ((instance= instance_map->find(name, len)))
instance_name= instance->options.instance_name; if (!err_code)
else err_code= send_ok_response(net, connection_id);
instance_name= NULL;
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::Show_instance_status(Instance_map *instance_map_arg,
Show_instance_status::do_command() const LEX_STRING *instance_name_arg)
net The network connection to the client. :Abstract_instance_cmd(instance_map_arg, instance_name_arg)
instance_name The name of the instance. {
}
RETURN
0 - ok /*
1 - error occured Implementation of SHOW INSTANCE STATUS statement.
Possible error codes:
ER_BAD_INSTANCE_NAME The instance with the given name does not exist
ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/ */
int Show_instance_status::execute_impl(st_net *net, Instance *instance)
{
int err_code;
if ((err_code= write_header(net)) ||
(err_code= write_data(net, instance)))
return err_code;
return 0;
}
int Show_instance_status::execute(struct st_net *net,
ulong connection_id) int Show_instance_status::send_ok_response(st_net *net, ulong connection_id)
{ {
enum { MAX_VERSION_LENGTH= 40 }; if (send_eof(net) || net_flush(net))
Buffer send_buff; /* buffer for packets */ return ER_OUT_OF_RESOURCES;
LIST name, status, version, version_number;
return 0;
}
int Show_instance_status::write_header(st_net *net)
{
LIST name, state, version, version_number, mysqld_compatible;
LIST *field_list; LIST *field_list;
NAME_WITH_LENGTH name_field, status_field, version_field, LEX_STRING name_field, state_field, version_field,
version_number_field; version_number_field, mysqld_compatible_field;
uint position=0;
if (!instance_name) /* Create list of the fileds to be passed to send_fields(). */
return ER_BAD_INSTANCE_NAME;
/* create list of the fileds to be passed to send_fields */ name_field.str= (char *) "instance_name";
name_field.name= (char*) "instance_name";
name_field.length= DEFAULT_FIELD_LENGTH; name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field; name.data= &name_field;
status_field.name= (char*) "status";
status_field.length= DEFAULT_FIELD_LENGTH; state_field.str= (char *) "state";
status.data= &status_field; state_field.length= DEFAULT_FIELD_LENGTH;
version_field.name= (char*) "version"; state.data= &state_field;
version_field.str= (char *) "version";
version_field.length= MAX_VERSION_LENGTH; version_field.length= MAX_VERSION_LENGTH;
version.data= &version_field; 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_field.length= MAX_VERSION_LENGTH;
version_number.data= &version_number_field; 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, &version_number);
field_list= list_add(field_list, &status); field_list= list_add(field_list, &state);
field_list= list_add(field_list, &name); 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); int Show_instance_status::write_data(st_net *net, Instance *instance)
if (!(instance= instance_map->find(instance_name, strlen(instance_name)))) {
goto err; Buffer send_buf; /* buffer for packets */
if (instance->is_running()) char version_num_buf[MAX_VERSION_LENGTH];
store_to_protocol_packet(&send_buff, (char*) "online", &position); uint pos= 0;
else
store_to_protocol_packet(&send_buff, (char*) "offline", &position);
if (instance->options.mysqld_version) const char *state_name;
{ const char *version_tag= "unknown";
char parsed_version[MAX_VERSION_LENGTH]; const char *version_num= "unknown";
const char *mysqld_compatible_status;
parse_version_number(instance->options.mysqld_version, parsed_version, instance_map->guardian->lock();
sizeof(parsed_version)); state_name= instance_map->get_instance_state_name(instance);
store_to_protocol_packet(&send_buff, parsed_version, &position); mysqld_compatible_status= instance->is_mysqld_compatible() ? "yes" : "no";
instance_map->guardian->unlock();
store_to_protocol_packet(&send_buff, instance->options.mysqld_version, if (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 (parse_version_number(instance->options.mysqld_version, version_num_buf,
sizeof(version_num_buf)))
return ER_OUT_OF_RESOURCES;
if (send_buff.is_error() || version_num= version_num_buf;
my_net_write(net, send_buff.buffer, (uint) position)) version_tag= instance->options.mysqld_version;
goto err;
} }
if (send_eof(net) || net_flush(net)) if (store_to_protocol_packet(&send_buf, get_instance_name()->str, &pos) ||
goto err; 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; return 0;
}
err:
return ER_OUT_OF_RESOURCES; /**************************************************************************
Implementation of Show_instance_options.
**************************************************************************/
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.
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
*/
Show_instance_options::Show_instance_options(Instance_map *instance_map_arg, int Show_instance_options::execute_impl(st_net *net, Instance *instance)
const char *name, uint len):
Command(instance_map_arg)
{ {
Instance *instance; int err_code;
if ((err_code= write_header(net)) ||
(err_code= write_data(net, instance)))
return err_code;
/* we make a search here, since we don't want to store the name */ return 0;
if ((instance= instance_map->find(name, len)))
instance_name= instance->options.instance_name;
else
instance_name= NULL;
} }
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 name, option;
LIST *field_list; LIST *field_list;
NAME_WITH_LENGTH name_field, option_field; LEX_STRING name_field, option_field;
uint position=0;
if (!instance_name) /* Create list of the fileds to be passed to send_fields(). */
return ER_BAD_INSTANCE_NAME;
/* create list of the fileds to be passed to send_fields */ name_field.str= (char *) "option_name";
name_field.name= (char*) "option_name";
name_field.length= DEFAULT_FIELD_LENGTH; name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field; name.data= &name_field;
option_field.name= (char*) "value";
option_field.str= (char *) "value";
option_field.length= DEFAULT_FIELD_LENGTH; option_field.length= DEFAULT_FIELD_LENGTH;
option.data= &option_field; option.data= &option_field;
field_list= list_add(NULL, &option); field_list= list_add(NULL, &option);
field_list= list_add(field_list, &name); 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; return ER_OUT_OF_RESOURCES;
}
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;
}
if ((instance->options.nonguarded)) /* Loop through the options. */
{
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;
}
/* loop through the options stored in DYNAMIC_ARRAY */ for (int i= 0; i < instance->options.get_num_options(); i++)
for (uint i= 0; i < instance->options.options_array.elements; i++) {
{ Named_value option= instance->options.get_option(i);
char *tmp_option, *option_value; const char *option_value= option.get_value()[0] ? option.get_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 */
position= 0; pos= 0;
if (option_value != NULL)
{
*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= '=';
}
else
{
store_to_protocol_packet(&send_buff, tmp_option + 2, &position);
store_to_protocol_packet(&send_buff, "", &position);
}
if (send_buff.is_error() || if (store_to_protocol_packet(&send_buff, option.get_name(), &pos) ||
my_net_write(net, send_buff.buffer, (uint) position)) store_to_protocol_packet(&send_buff, option_value, &pos) ||
goto err; my_net_write(net, send_buff.buffer, pos))
{
return ER_OUT_OF_RESOURCES;
} }
} }
if (send_eof(net) || net_flush(net))
goto err;
return 0; return 0;
err:
return ER_OUT_OF_RESOURCES;
} }
/* Implementation for Start_instance */ /**************************************************************************
Implementation of Start_instance.
**************************************************************************/
Start_instance::Start_instance(Instance_map *instance_map_arg, Start_instance::Start_instance(Instance_map *instance_map_arg,
const char *name, uint len) const LEX_STRING *instance_name_arg)
:Command(instance_map_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 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)
{ {
uint err_code; int err_code;
if (instance == 0)
return ER_BAD_INSTANCE_NAME; /* haven't found an instance */
else
{
if ((err_code= instance->start()))
return err_code;
if (!(instance->options.nonguarded)) if ((err_code= instance->start()))
instance_map->guardian->guard(instance); return err_code;
net_send_ok(net, connection_id, "Instance started"); if (!(instance->options.nonguarded))
return 0; instance_map->guardian->guard(instance);
}
}
return 0;
}
/* implementation for Show_instance_log: */
Show_instance_log::Show_instance_log(Instance_map *instance_map_arg, int Start_instance::send_ok_response(st_net *net, ulong connection_id)
const char *name, uint len,
Log_type log_type_arg,
const char *size_arg,
const char *offset_arg)
:Command(instance_map_arg)
{ {
Instance *instance; if (net_send_ok(net, connection_id, "Instance started"))
return ER_OUT_OF_RESOURCES;
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 */ return 0;
if ((instance= instance_map->find(name, len)))
instance_name= instance->options.instance_name;
else
instance_name= NULL;
} }
/**************************************************************************
Implementation of Stop_instance.
**************************************************************************/
/* Stop_instance::Stop_instance(Instance_map *instance_map_arg,
Open the logfile, read requested part of the log and send the info const LEX_STRING *instance_name_arg)
to the client. :Abstract_instance_cmd(instance_map_arg, instance_name_arg)
{
SYNOPSYS }
Show_instance_log::execute()
net The network connection to the client.
connection_id Client connection ID
DESCRIPTION
Send a table with the content of the log requested. The function also /*
deals with errro handling, to be verbose. Implementation of STOP INSTANCE statement.
RETURN Possible error codes:
ER_OFFSET_ERROR We were requested to read negative number of bytes ER_BAD_INSTANCE_NAME The instance with the given name does not exist
from the log ER_OUT_OF_RESOURCES Not enough resources to complete the operation
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 Stop_instance::execute_impl(st_net *net, Instance *instance)
{ {
Buffer send_buff; /* buffer for packets */ int err_code;
LIST name;
LIST *field_list;
NAME_WITH_LENGTH name_field;
uint position= 0;
/* create list of the fileds to be passed to send_fields */ if (!(instance->options.nonguarded))
name_field.name= (char*) "Log"; instance_map->guardian->stop_guard(instance);
name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field;
field_list= list_add(NULL, &name);
if (!instance_name) if ((err_code= instance->stop()))
return ER_BAD_INSTANCE_NAME; return err_code;
/* cannot read negative number of bytes */ return 0;
if (offset > size) }
return ER_OFFSET_ERROR;
send_fields(net, field_list);
{ int Stop_instance::send_ok_response(st_net *net, ulong connection_id)
Instance *instance; {
const char *logpath; if (net_send_ok(net, connection_id, NULL))
File fd; return ER_OUT_OF_RESOURCES;
if ((instance= instance_map->find(instance_name, return 0;
strlen(instance_name))) == NULL) }
goto err;
logpath= instance->options.logs[log_type];
/* Instance has no such log */ /**************************************************************************
if (logpath == NULL) Implementation for Create_instance.
return ER_NO_SUCH_LOG; **************************************************************************/
if (*logpath == '\0') Create_instance::Create_instance(Instance_map *instance_map_arg,
return ER_GUESS_LOGFILE; const LEX_STRING *instance_name_arg)
:Command(instance_map_arg),
instance_name(instance_name_arg)
{
}
if ((fd= my_open(logpath, O_RDONLY | O_BINARY, MYF(MY_WME))) >= 0) /*
{ This operation initializes Create_instance object.
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 */ SYNOPSYS
if (my_fstat(fd, &file_stat, MYF(0))) text [IN/OUT] a pointer to the text containing instance options.
goto err;
buff_size= (size - offset); RETURN
FALSE On success.
TRUE On error.
*/
read_buff.reserve(0, buff_size); bool Create_instance::init(const char **text)
{
return options.init() || parse_args(text);
}
/* 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) This operation parses CREATE INSTANCE options.
return ER_READ_FILE;
store_to_protocol_packet(&send_buff, read_buff.buffer,
&position, read_len);
close(fd);
}
else
return ER_OPEN_LOGFILE;
if (my_net_write(net, send_buff.buffer, (uint) position)) SYNOPSYS
goto err; text [IN/OUT] a pointer to the text containing instance options.
}
if (send_eof(net) || net_flush(net)) RETURN
goto err; FALSE On success.
TRUE On syntax error.
*/
return 0; bool Create_instance::parse_args(const char **text)
{
uint len;
err: /* Check if we have something (and trim leading spaces). */
return ER_OUT_OF_RESOURCES;
}
get_word(text, &len, NONSPACE);
/* implementation for Show_instance_log_files: */ if (len == 0)
return FALSE; /* OK: no option. */
Show_instance_log_files::Show_instance_log_files /* Main parsing loop. */
(Instance_map *instance_map_arg, const char *name, uint len)
:Command(instance_map_arg)
{
Instance *instance;
/* we make a search here, since we don't want to store the name */ while (TRUE)
if ((instance= instance_map->find(name, len))) {
instance_name= instance->options.instance_name; LEX_STRING option_name;
else char *option_name_str;
instance_name= NULL; char *option_value_str= NULL;
}
/* Looking for option name. */
/* get_word(text, &option_name.length, OPTION_NAME);
The method sends a table with a list of log files
used by the instance.
SYNOPSYS if (option_name.length == 0)
Show_instance_log_files::execute() return TRUE; /* Syntax error: option name expected. */
net The network connection to the client.
connection_id The ID of the client connection
RETURN option_name.str= (char *) *text;
ER_BAD_INSTANCE_NAME The instance name specified is not valid *text+= option_name.length;
ER_OUT_OF_RESOURCES some error occured
0 - ok
*/
int Show_instance_log_files::execute(struct st_net *net, ulong connection_id) /* Looking for equal sign. */
{
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;
if (!instance_name) skip_spaces(text);
return ER_BAD_INSTANCE_NAME;
/* create list of the fileds to be passed to send_fields */ if (**text == '=')
name_field.name= (char*) "Logfile"; {
++(*text); /* Skip an equal sign. */
/* Looking for option value. */
skip_spaces(text);
if (!**text)
return TRUE; /* Syntax error: EOS when option value expected. */
if (**text != '\'' && **text != '"')
{
/* Option value is a simple token. */
LEX_STRING option_value;
get_word(text, &option_value.length, ALPHANUM);
if (option_value.length == 0)
return TRUE; /* internal parser error. */
option_value.str= (char *) *text;
*text+= option_value.length;
if (!(option_value_str= Named_value::alloc_str(&option_value)))
return TRUE; /* out of memory during parsing. */
}
else
{
/* Option value is a string. */
if (parse_option_value(*text, &len, &option_value_str))
return TRUE; /* Syntax error: invalid string specification. */
*text+= len;
}
}
if (!option_value_str)
{
LEX_STRING empty_str= { C_STRING_WITH_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 (options.add_element(&option))
{
option.free();
return TRUE; /* out of memory during parsing. */
}
}
skip_spaces(text);
if (!**text)
return FALSE; /* OK: end of options. */
if (**text != ',')
return TRUE; /* Syntax error: comma expected. */
++(*text);
}
}
/*
Implementation of CREATE INSTANCE statement.
Possible error codes:
ER_MALFORMED_INSTANCE_NAME Instance name is malformed
ER_CREATE_EXISTING_INSTANCE There is an instance with the given name
ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
int Create_instance::execute(st_net *net, ulong connection_id)
{
int err_code;
/* 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 Drop_instance.
**************************************************************************/
Drop_instance::Drop_instance(Instance_map *instance_map_arg,
const LEX_STRING *instance_name_arg)
:Abstract_instance_cmd(instance_map_arg, instance_name_arg)
{
}
/*
Implementation of DROP INSTANCE statement.
Possible error codes:
ER_BAD_INSTANCE_NAME The instance with the given name does not exist
ER_DROP_ACTIVE_INSTANCE The specified instance is active
ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
int Drop_instance::execute_impl(st_net *net, Instance *instance)
{
int err_code;
/* Check that the instance is offline. */
if (instance_map->guardian->is_active(instance))
return ER_DROP_ACTIVE_INSTANCE;
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;
}
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 LEX_STRING *instance_name_arg,
Log_type log_type_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)
{
}
/*
Implementation of SHOW INSTANCE LOG statement.
Possible error codes:
ER_BAD_INSTANCE_NAME The instance with the given name does not exist
ER_OFFSET_ERROR We were requested to read negative number of
bytes from the log
ER_NO_SUCH_LOG The specified type of log is not available for
the given instance
ER_GUESS_LOGFILE IM wasn't able to figure out the log
placement, while it is enabled. Probably user
should specify the path to the logfile
explicitly.
ER_OPEN_LOGFILE Cannot open the logfile
ER_READ_FILE Cannot read the logfile
ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
int Show_instance_log::execute_impl(st_net *net, Instance *instance)
{
int err_code;
if ((err_code= check_params(instance)))
return err_code;
if ((err_code= write_header(net)) ||
(err_code= write_data(net, instance)))
return err_code;
return 0;
}
int Show_instance_log::send_ok_response(st_net *net, ulong connection_id)
{
if (send_eof(net) || net_flush(net))
return ER_OUT_OF_RESOURCES;
return 0;
}
int Show_instance_log::check_params(Instance *instance)
{
const char *logpath= instance->options.logs[log_type];
/* Cannot read negative number of bytes. */
if (offset > size)
return ER_OFFSET_ERROR;
/* Instance has no such log. */
if (logpath == NULL)
return ER_NO_SUCH_LOG;
if (*logpath == '\0')
return ER_GUESS_LOGFILE;
return 0;
}
int Show_instance_log::write_header(st_net *net)
{
LIST name;
LIST *field_list;
LEX_STRING name_field;
/* Create list of the fields to be passed to send_fields(). */
name_field.str= (char *) "Log";
name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field;
field_list= list_add(NULL, &name);
return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0;
}
int Show_instance_log::write_data(st_net *net, Instance *instance)
{
Buffer send_buff; /* buffer for packets */
uint pos= 0;
const char *logpath= instance->options.logs[log_type];
File fd;
size_t buff_size;
int read_len;
MY_STAT file_stat;
Buffer read_buff;
if ((fd= my_open(logpath, O_RDONLY | O_BINARY, MYF(MY_WME))) <= 0)
return ER_OPEN_LOGFILE;
/* my_fstat doesn't use the flag parameter */
if (my_fstat(fd, &file_stat, MYF(0)))
{
close(fd);
return ER_OUT_OF_RESOURCES;
}
/* calculate buffer size */
buff_size= (size - offset);
read_buff.reserve(0, buff_size);
/* read in one chunk */
read_len= (int)my_seek(fd, file_stat.st_size - size, MY_SEEK_SET, MYF(0));
if ((read_len= my_read(fd, (byte*) read_buff.buffer,
buff_size, MYF(0))) < 0)
{
close(fd);
return ER_READ_FILE;
}
close(fd);
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;
}
return 0;
}
/**************************************************************************
Implementation of Show_instance_log_files.
**************************************************************************/
Show_instance_log_files::Show_instance_log_files
(Instance_map *instance_map_arg,
const LEX_STRING *instance_name_arg)
:Abstract_instance_cmd(instance_map_arg, instance_name_arg)
{
}
/*
Implementation of SHOW INSTANCE LOG FILES statement.
Possible error codes:
ER_BAD_INSTANCE_NAME The instance with the given name does not exist
ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
int Show_instance_log_files::execute_impl(st_net *net, Instance *instance)
{
int err_code;
if ((err_code= write_header(net)) ||
(err_code= write_data(net, instance)))
return err_code;
return 0;
}
int Show_instance_log_files::send_ok_response(st_net *net, ulong connection_id)
{
if (send_eof(net) || net_flush(net))
return ER_OUT_OF_RESOURCES;
return 0;
}
int Show_instance_log_files::write_header(st_net *net)
{
LIST name, path, size;
LIST *field_list;
LEX_STRING name_field, path_field, size_field;
/* Create list of the fileds to be passed to send_fields(). */
name_field.str= (char *) "Logfile";
name_field.length= DEFAULT_FIELD_LENGTH; name_field.length= DEFAULT_FIELD_LENGTH;
name.data= &name_field; name.data= &name_field;
path_field.name= (char*) "Path";
path_field.str= (char *) "Path";
path_field.length= DEFAULT_FIELD_LENGTH; path_field.length= DEFAULT_FIELD_LENGTH;
path.data= &path_field; path.data= &path_field;
size_field.name= (char*) "File size";
size_field.str= (char *) "File size";
size_field.length= DEFAULT_FIELD_LENGTH; size_field.length= DEFAULT_FIELD_LENGTH;
size.data= &size_field; size.data= &size_field;
field_list= list_add(NULL, &size); field_list= list_add(NULL, &size);
field_list= list_add(field_list, &path); field_list= list_add(field_list, &path);
field_list= list_add(field_list, &name); 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-> int Show_instance_log_files::write_data(st_net *net, Instance *instance)
find(instance_name, strlen(instance_name))) == NULL) {
goto err; 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 Save some more space for the log file names. In fact all
to loop through the options, which we need to handle in some common way. we need is strlen("GENERAL_LOG") + 1
*/ */
struct log_files_st enum { LOG_NAME_BUFFER_SIZE= 20 };
{ char buff[LOG_NAME_BUFFER_SIZE];
const char *name;
const char *value; uint pos= 0;
} logs[]=
{ const char *log_path= "";
{"ERROR LOG", instance->options.logs[IM_LOG_ERROR]}, const char *log_size= "0";
{"GENERAL LOG", instance->options.logs[IM_LOG_GENERAL]},
{"SLOW LOG", instance->options.logs[IM_LOG_SLOW]}, if (!stat(log_files->value, &file_stat) &&
{NULL, NULL} MY_S_ISREG(file_stat.st_mode))
};
struct log_files_st *log_files;
for (log_files= logs; log_files->name; log_files++)
{ {
if (log_files->value != NULL) int10_to_str(file_stat.st_size, buff, 10);
{
struct stat file_stat; log_path= log_files->value;
/* log_size= buff;
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;
}
} }
}
if (send_eof(net) || net_flush(net)) if (store_to_protocol_packet(&send_buff, log_files->name, &pos) ||
goto err; 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; 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, class Instance_options_list
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)
{ {
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 */ public:
if ((instance= instance_map->find(name, len))) bool init();
{
instance_name= instance->options.instance_name;
/* add prefix for add_option */ const LEX_STRING *get_instance_name() const
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
{ {
instance_name= NULL; return instance_name.get_str();
instance_name_len= 0;
} }
public:
/*
This member is set and used only in Abstract_option_cmd::execute_impl().
Normally it is not used (and should not).
The problem is that construction and execution of commands are made not
in one transaction (not under one lock session). So, we can not initialize
instance in constructor and use it in execution.
*/
Instance *instance;
Named_value_arr options;
private:
Instance_name instance_name;
};
/**************************************************************************/
Instance_options_list::Instance_options_list(
const LEX_STRING *instance_name_arg)
:instance(NULL),
instance_name(instance_name_arg)
{
} }
/* bool Instance_options_list::init()
The method sends a table with a list of log files {
used by the instance. 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 Correct the option file. The "skip" option is used to remove the found
option. option.
SYNOPSYS
Abstract_option_cmd::correct_file()
skip Skip the option, being searched while writing the result file.
That is, to delete it.
RETURN 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 ER_ACCESS_OPTION_FILE Cannot access the option file
0 - ok
*/ */
int Set_option::correct_file(int skip) int Abstract_option_cmd::correct_file(Instance *instance, Named_value *option,
bool skip)
{ {
static const int mysys_to_im_error[]= { 0, ER_OUT_OF_RESOURCES, int err_code= modify_defaults_file(Options::Main::config_file,
ER_ACCESS_OPTION_FILE }; option->get_name(),
int error; option->get_value(),
instance->get_name()->str,
skip);
error= modify_defaults_file(Options::config_file, option, DBUG_ASSERT(err_code >= 0 && err_code <= 2);
option_value, instance_name, skip);
DBUG_ASSERT(error >= 0 && error <= 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 mysys_to_im_error[error]; return modify_defaults_to_im_error[err_code];
} }
/* /*
The method sets an option in the the default config file (/etc/my.cnf). Implementation of SET statement.
SYNOPSYS Possible error codes:
Set_option::do_command() ER_BAD_INSTANCE_NAME The instance with the given name does not exist
net The network connection to the client. ER_INCOMPATIBLE_OPTION The specified option can not be set for
mysqld-compatible instance
RETURN ER_INSTANCE_IS_ACTIVE The specified instance is active
0 - ok ER_OUT_OF_RESOURCES Not enough resources to complete the operation
1 - error occured
*/ */
int Set_option::do_command(struct st_net *net) int Abstract_option_cmd::execute(st_net *net, ulong connection_id)
{ {
int error; int err_code;
/* we must hold the instance_map mutex while changing config file */
instance_map->lock(); instance_map->lock();
error= correct_file(FALSE);
err_code= execute_impl(net, connection_id);
instance_map->unlock(); instance_map->unlock();
return error; return err_code;
} }
int Set_option::execute(struct st_net *net, ulong connection_id) Instance_options_list *
Abstract_option_cmd::get_instance_options_list(const LEX_STRING *instance_name)
{ {
if (instance_name != NULL) 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);
if (!lst)
return NULL;
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)
{ {
int val; Instance_options_list *lst=
(Instance_options_list *) hash_element(&instance_options_map, i);
val= do_command(net); lst->instance= instance_map->find(lst->get_instance_name());
if (val == 0) if (!lst->instance)
net_send_ok(net, connection_id, NULL); return ER_BAD_INSTANCE_NAME;
return val; if (instance_map->guardian->is_active(lst->instance))
return ER_INSTANCE_IS_ACTIVE;
} }
return ER_BAD_INSTANCE_NAME; /* 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;
} }
/* the only function from Unset_option we need to Implement */ /**************************************************************************
Implementation of Set_option.
**************************************************************************/
int Unset_option::do_command(struct st_net *net) Set_option::Set_option(Instance_map *instance_map_arg)
:Abstract_option_cmd(instance_map_arg)
{ {
return correct_file(TRUE);
} }
/* Implementation for Stop_instance: */ /*
This operation parses SET options.
Stop_instance::Stop_instance(Instance_map *instance_map_arg, SYNOPSYS
const char *name, uint len) text [IN/OUT] a pointer to the text containing options.
:Command(instance_map_arg)
RETURN
FALSE On success.
TRUE On syntax error.
*/
bool Set_option::parse_args(const char **text)
{ {
/* we make a search here, since we don't want to store the name */ uint len;
if ((instance= instance_map->find(name, len)))
instance_name= instance->options.instance_name; /* Check if we have something (and trim leading spaces). */
get_word(text, &len, NONSPACE);
if (len == 0)
return TRUE; /* Syntax error: no option. */
/* Main parsing loop. */
while (TRUE)
{
LEX_STRING instance_name;
LEX_STRING option_name;
char *option_name_str;
char *option_value_str= NULL;
/* Looking for instance name. */
get_word(text, &instance_name.length, ALPHANUM);
if (instance_name.length == 0)
return TRUE; /* Syntax error: instance name expected. */
instance_name.str= (char *) *text;
*text+= instance_name.length;
skip_spaces(text);
/* Check the the delimiter is a dot. */
if (**text != '.')
return TRUE; /* Syntax error: dot expected. */
++(*text);
/* Looking for option name. */
get_word(text, &option_name.length, OPTION_NAME);
if (option_name.length == 0)
return TRUE; /* Syntax error: option name expected. */
option_name.str= (char *) *text;
*text+= option_name.length;
/* Looking for equal sign. */
skip_spaces(text);
if (**text == '=')
{
++(*text); /* Skip an equal sign. */
/* Looking for option value. */
skip_spaces(text);
if (!**text)
return TRUE; /* Syntax error: EOS when option value expected. */
if (**text != '\'' && **text != '"')
{
/* Option value is a simple token. */
LEX_STRING option_value;
get_word(text, &option_value.length, ALPHANUM);
if (option_value.length == 0)
return TRUE; /* internal parser error. */
option_value.str= (char *) *text;
*text+= option_value.length;
if (!(option_value_str= Named_value::alloc_str(&option_value)))
return TRUE; /* out of memory during parsing. */
}
else
{
/* Option value is a string. */
if (parse_option_value(*text, &len, &option_value_str))
return TRUE; /* Syntax error: invalid string specification. */
*text+= len;
}
}
if (!option_value_str)
{
LEX_STRING empty_str= { C_STRING_WITH_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 Stop_instance::execute(struct st_net *net, ulong connection_id) int Set_option::process_option(Instance *instance, Named_value *option)
{ {
uint err_code; /* Check that the option is valid. */
if (instance->is_mysqld_compatible() &&
Instance_options::is_option_im_specific(option->get_name()))
{
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;
}
if (instance == 0) /* Update the configuration file. */
return ER_BAD_INSTANCE_NAME; /* haven't found an instance */
if (!(instance->options.nonguarded)) int err_code= correct_file(instance, option, FALSE);
instance_map->guardian->stop_guard(instance);
if ((err_code= instance->stop())) if (err_code)
return err_code; return err_code;
net_send_ok(net, connection_id, NULL); /* Update the internal cache. */
if (instance->options.set_option(option))
return ER_OUT_OF_RESOURCES;
return 0; return 0;
} }
int Syntax_error::execute(struct st_net *net, ulong connection_id) /**************************************************************************
Implementation of Unset_option.
**************************************************************************/
Unset_option::Unset_option(Instance_map *instance_map_arg)
:Abstract_option_cmd(instance_map_arg)
{
}
/*
This operation parses UNSET options.
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)
{
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. */
}
}
/*
Implementation of UNSET statement.
Possible error codes:
ER_BAD_INSTANCE_NAME The instance name specified is not valid
ER_INSTANCE_IS_ACTIVE The specified instance is active
ER_OUT_OF_RESOURCES Not enough resources to complete the operation
*/
int Unset_option::process_option(Instance *instance, Named_value *option)
{
/* Update the configuration file. */
int err_code= correct_file(instance, option, TRUE);
if (err_code)
return err_code;
/* Update the internal cache. */
instance->options.unset_option(option->get_name());
return 0;
}
/**************************************************************************
Implementation of Syntax_error.
**************************************************************************/
int Syntax_error::execute(st_net *net, ulong connection_id)
{ {
return ER_SYNTAX_ERROR; return ER_SYNTAX_ERROR;
} }
...@@ -16,10 +16,20 @@ ...@@ -16,10 +16,20 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 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 "command.h"
#include "instance.h" #include "instance.h"
#include "parse.h" #include "parse.h"
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface
#endif
struct LEX_STRING;
/* /*
Print all instances of this instance manager. Print all instances of this instance manager.
Grammar: SHOW ISTANCES Grammar: SHOW ISTANCES
...@@ -31,12 +41,16 @@ public: ...@@ -31,12 +41,16 @@ public:
Show_instances(Instance_map *instance_map_arg): Command(instance_map_arg) 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 Grammar: FLUSH INSTANCES
*/ */
...@@ -46,7 +60,43 @@ public: ...@@ -46,7 +60,43 @@ public:
Flush_instances(Instance_map *instance_map_arg): Command(instance_map_arg) 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: ...@@ -55,31 +105,40 @@ public:
Grammar: SHOW ISTANCE STATUS <instance_name> Grammar: SHOW ISTANCE STATUS <instance_name>
*/ */
class Show_instance_status : public Command class Show_instance_status : public Abstract_instance_cmd
{ {
public: public:
Show_instance_status(Instance_map *instance_map_arg, Show_instance_status(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);
}; };
/* /*
Print options if chosen instance. Print options of chosen instance.
Grammar: SHOW INSTANCE OPTIONS <instance_name> Grammar: SHOW INSTANCE OPTIONS <instance_name>
*/ */
class Show_instance_options : public Command class Show_instance_options : public Abstract_instance_cmd
{ {
public: public:
Show_instance_options(Instance_map *instance_map_arg, 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); protected:
const char *instance_name; 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: ...@@ -88,14 +147,15 @@ public:
Grammar: START INSTANCE <instance_name> Grammar: START INSTANCE <instance_name>
*/ */
class Start_instance : public Command class Start_instance : public Abstract_instance_cmd
{ {
public: 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); protected:
const char *instance_name; virtual int execute_impl(st_net *net, Instance *instance);
Instance *instance; virtual int send_ok_response(st_net *net, ulong connection_id);
}; };
...@@ -104,33 +164,95 @@ public: ...@@ -104,33 +164,95 @@ public:
Grammar: STOP INSTANCE <instance_name> Grammar: STOP INSTANCE <instance_name>
*/ */
class Stop_instance : public Command class Stop_instance : public Abstract_instance_cmd
{ {
public: 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; protected:
int execute(struct st_net *net, ulong connection_id); virtual int execute_impl(st_net *net, Instance *instance);
const char *instance_name; 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: 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: 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, private:
uint len, Log_type log_type_arg, const char *size_arg,
const char *offset_arg);
int execute(struct st_net *net, ulong connection_id);
Log_type log_type; Log_type log_type;
const char *instance_name;
uint size; uint size;
uint offset; uint offset;
}; };
...@@ -141,75 +263,112 @@ public: ...@@ -141,75 +263,112 @@ public:
Grammar: SHOW <instance_name> LOG FILES Grammar: SHOW <instance_name> LOG FILES
*/ */
class Show_instance_log_files : public Command class Show_instance_log_files : public Abstract_instance_cmd
{ {
public: public:
Show_instance_log_files(Instance_map *instance_map_arg, Show_instance_log_files(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:
const char *option; 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 Abstract class for option-management commands.
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.
*/ */
class Syntax_error : public Command class Instance_options_list;
class Abstract_option_cmd : public Command
{ {
public: 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. 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: public:
Set_option(Instance_map *instance_map_arg, const char *name, uint len, Set_option(Instance_map *instance_map_arg);
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);
protected: protected:
int correct_file(int skip); virtual bool parse_args(const char **text);
public: virtual int process_option(Instance *instance, Named_value *option);
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];
}; };
/* /*
Remove option of the instance from config file Remove option of the instance.
Grammar: UNSET instance_name.option Grammar: UNSET instance_name.option[, ...]
*/ */
class Unset_option: public Set_option class Unset_option: public Abstract_option_cmd
{ {
public: public:
Unset_option(Instance_map *instance_map_arg, const char *name, uint len, Unset_option(Instance_map *instance_map_arg);
const char *option_arg, uint option_len,
const char *option_value_arg, uint option_value_len): protected:
Set_option(instance_map_arg, name, len, option_arg, option_len, virtual bool parse_args(const char **text);
option_value_arg, option_value_len) virtual int process_option(Instance *instance, Named_value *option);
{}
int do_command(struct st_net *net);
}; };
/*
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 */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H */
#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
...@@ -21,16 +21,14 @@ ...@@ -21,16 +21,14 @@
#include "guardian.h" #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 <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <signal.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) pthread_handler_t guardian(void *arg)
...@@ -40,6 +38,37 @@ pthread_handler_t guardian(void *arg) ...@@ -40,6 +38,37 @@ pthread_handler_t guardian(void *arg)
return 0; 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, Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg,
Instance_map *instance_map_arg, Instance_map *instance_map_arg,
uint monitoring_interval_arg) : uint monitoring_interval_arg) :
...@@ -89,10 +118,17 @@ void Guardian_thread::process_instance(Instance *instance, ...@@ -89,10 +118,17 @@ void Guardian_thread::process_instance(Instance *instance,
if (current_node->state == STOPPING) if (current_node->state == STOPPING)
{ {
/* this brach is executed during shutdown */ /* 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; 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()) if (instance->is_crashed())
*guarded_instances= list_delete(*guarded_instances, node); *guarded_instances= list_delete(*guarded_instances, node);
else if ( (uint) (current_time - current_node->last_checked) > waitchild) else if ( (uint) (current_time - current_node->last_checked) > waitchild)
...@@ -159,7 +195,11 @@ void Guardian_thread::process_instance(Instance *instance, ...@@ -159,7 +195,11 @@ void Guardian_thread::process_instance(Instance *instance,
instance->options.instance_name); instance->options.instance_name);
} }
else else
{
log_info("guardian: cannot start instance %s. Abandoning attempts "
"to (re)start it", instance->options.instance_name);
current_node->state= CRASHED_AND_ABANDONED; current_node->state= CRASHED_AND_ABANDONED;
}
} }
break; break;
case CRASHED_AND_ABANDONED: case CRASHED_AND_ABANDONED:
...@@ -242,7 +282,9 @@ int Guardian_thread::is_stopped() ...@@ -242,7 +282,9 @@ int Guardian_thread::is_stopped()
SYNOPSYS SYNOPSYS
Guardian_thread::init() 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 RETURN
0 - ok 0 - ok
...@@ -261,12 +303,11 @@ int Guardian_thread::init() ...@@ -261,12 +303,11 @@ int Guardian_thread::init()
while ((instance= iterator.next())) while ((instance= iterator.next()))
{ {
if (!(instance->options.nonguarded)) if (instance->options.nonguarded)
if (guard(instance, TRUE)) /* do not lock guardian */ continue;
{
instance_map->unlock(); if (guard(instance, TRUE)) /* do not lock guardian */
return 1; return 1;
}
} }
return 0; return 0;
...@@ -334,24 +375,14 @@ int Guardian_thread::stop_guard(Instance *instance) ...@@ -334,24 +375,14 @@ int Guardian_thread::stop_guard(Instance *instance)
LIST *node; LIST *node;
pthread_mutex_lock(&LOCK_guardian); pthread_mutex_lock(&LOCK_guardian);
node= guarded_instances;
while (node != NULL) node= find_instance_node(instance);
{
/* if (node != NULL)
We compare only pointers, as we always use pointers from the guarded_instances= list_delete(guarded_instances, node);
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;
}
pthread_mutex_unlock(&LOCK_guardian); pthread_mutex_unlock(&LOCK_guardian);
/* if there is nothing to delete it is also fine */ /* if there is nothing to delete it is also fine */
return 0; return 0;
} }
...@@ -420,7 +451,7 @@ int Guardian_thread::stop_instances(bool stop_instances_arg) ...@@ -420,7 +451,7 @@ int Guardian_thread::stop_instances(bool stop_instances_arg)
void Guardian_thread::lock() void Guardian_thread::lock()
{ {
pthread_mutex_lock(&LOCK_guardian); pthread_mutex_lock(&LOCK_guardian);
} }
...@@ -428,3 +459,41 @@ void Guardian_thread::unlock() ...@@ -428,3 +459,41 @@ void Guardian_thread::unlock()
{ {
pthread_mutex_unlock(&LOCK_guardian); 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();
}
...@@ -17,11 +17,11 @@ ...@@ -17,11 +17,11 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h> #include <my_global.h>
#include "thread_registry.h"
#include <my_sys.h> #include <my_sys.h>
#include <my_list.h> #include <my_list.h>
#include "thread_registry.h"
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface #pragma interface
#endif #endif
...@@ -79,6 +79,8 @@ public: ...@@ -79,6 +79,8 @@ public:
time_t last_checked; 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, Guardian_thread(Thread_registry &thread_registry_arg,
Instance_map *instance_map_arg, Instance_map *instance_map_arg,
...@@ -94,11 +96,28 @@ public: ...@@ -94,11 +96,28 @@ public:
int guard(Instance *instance, bool nolock= FALSE); int guard(Instance *instance, bool nolock= FALSE);
/* Stop instance protection */ /* Stop instance protection */
int stop_guard(Instance *instance); int stop_guard(Instance *instance);
/* Returns true if guardian thread is stopped */ /* Returns TRUE if guardian thread is stopped */
int is_stopped(); int is_stopped();
void lock(); void lock();
void unlock(); 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: public:
pthread_cond_t COND_guardian; pthread_cond_t COND_guardian;
...@@ -108,6 +127,7 @@ private: ...@@ -108,6 +127,7 @@ private:
/* check instance state and act accordingly */ /* check instance state and act accordingly */
void process_instance(Instance *instance, GUARD_NODE *current_node, void process_instance(Instance *instance, GUARD_NODE *current_node,
LIST **guarded_instances, LIST *elem); LIST **guarded_instances, LIST *elem);
int stopped; int stopped;
private: private:
...@@ -115,9 +135,15 @@ private: ...@@ -115,9 +135,15 @@ private:
Thread_info thread_info; Thread_info thread_info;
LIST *guarded_instances; LIST *guarded_instances;
MEM_ROOT alloc; MEM_ROOT alloc;
enum { MEM_ROOT_BLOCK_SIZE= 512 };
/* this variable is set to TRUE when we want to stop Guardian thread */ /* this variable is set to TRUE when we want to stop Guardian thread */
bool shutdown_requested; 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 */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */
...@@ -20,18 +20,27 @@ ...@@ -20,18 +20,27 @@
#include "instance.h" #include "instance.h"
#include "mysql_manager_error.h" #include <my_global.h>
#include "log.h" #include <mysql.h>
#include "instance_map.h"
#include "priv.h" #include <signal.h>
#include "portability.h"
#ifndef __WIN__ #ifndef __WIN__
#include <sys/wait.h> #include <sys/wait.h>
#endif #endif
#include <my_sys.h>
#include <signal.h> #include "guardian.h"
#include <m_string.h> #include "instance_map.h"
#include <mysql.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, static void start_and_monitor_instance(Instance_options *old_instance_options,
...@@ -152,7 +161,7 @@ static int start_process(Instance_options *instance_options, ...@@ -152,7 +161,7 @@ static int start_process(Instance_options *instance_options,
switch (*pi) { switch (*pi) {
case 0: /* never happens on QNX */ 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 */ /* exec never returns */
exit(1); exit(1);
case -1: case -1:
...@@ -180,7 +189,7 @@ static int start_process(Instance_options *instance_options, ...@@ -180,7 +189,7 @@ static int start_process(Instance_options *instance_options,
char *cmdline= new char[cmdlen]; char *cmdline= new char[cmdlen];
if (cmdline == NULL) if (cmdline == NULL)
return 1; return 1;
cmdline[0]= 0; cmdline[0]= 0;
for (int i= 0; instance_options->argv[i] != 0; i++) for (int i= 0; instance_options->argv[i] != 0; i++)
{ {
...@@ -232,9 +241,7 @@ static int start_process(Instance_options *instance_options, ...@@ -232,9 +241,7 @@ static int start_process(Instance_options *instance_options,
static void start_and_monitor_instance(Instance_options *old_instance_options, static void start_and_monitor_instance(Instance_options *old_instance_options,
Instance_map *instance_map) Instance_map *instance_map)
{ {
enum { MAX_INSTANCE_NAME_LEN= 512 }; Instance_name instance_name(&old_instance_options->instance_name);
char instance_name_buff[MAX_INSTANCE_NAME_LEN];
uint instance_name_len;
Instance *current_instance; Instance *current_instance;
My_process_info process_info; My_process_info process_info;
...@@ -248,11 +255,8 @@ static void start_and_monitor_instance(Instance_options *old_instance_options, ...@@ -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 Save the instance name in the case if Instance object we
are using is destroyed. (E.g. by "FLUSH INSTANCES") 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)) if (start_process(old_instance_options, &process_info))
{ {
...@@ -266,15 +270,36 @@ static void start_and_monitor_instance(Instance_options *old_instance_options, ...@@ -266,15 +270,36 @@ static void start_and_monitor_instance(Instance_options *old_instance_options,
/* don't check for return value */ /* don't check for return value */
wait_process(&process_info); 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) if (current_instance)
current_instance->set_crash_flag_n_wake_all(); current_instance->set_crash_flag_n_wake_all();
instance_map->unlock();
return; 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() Instance_map *Instance::get_map()
{ {
return instance_map; return instance_map;
...@@ -309,11 +334,11 @@ int Instance::start() ...@@ -309,11 +334,11 @@ int Instance::start()
{ {
/* clear crash flag */ /* clear crash flag */
pthread_mutex_lock(&LOCK_instance); pthread_mutex_lock(&LOCK_instance);
crashed= 0; crashed= FALSE;
pthread_mutex_unlock(&LOCK_instance); pthread_mutex_unlock(&LOCK_instance);
if (!is_running()) if (configured && !is_running())
{ {
remove_pid(); remove_pid();
...@@ -339,8 +364,8 @@ int Instance::start() ...@@ -339,8 +364,8 @@ int Instance::start()
return 0; return 0;
} }
/* the instance is started already */ /* The instance is started already or misconfigured. */
return ER_INSTANCE_ALREADY_STARTED; return configured ? ER_INSTANCE_ALREADY_STARTED : ER_INSTANCE_MISCONFIGURED;
} }
/* /*
...@@ -363,7 +388,7 @@ void Instance::set_crash_flag_n_wake_all() ...@@ -363,7 +388,7 @@ void Instance::set_crash_flag_n_wake_all()
{ {
/* set instance state to crashed */ /* set instance state to crashed */
pthread_mutex_lock(&LOCK_instance); pthread_mutex_lock(&LOCK_instance);
crashed= 1; crashed= TRUE;
pthread_mutex_unlock(&LOCK_instance); pthread_mutex_unlock(&LOCK_instance);
/* /*
...@@ -378,7 +403,7 @@ void Instance::set_crash_flag_n_wake_all() ...@@ -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_mutex_init(&LOCK_instance, 0);
pthread_cond_init(&COND_instance_stopped, 0); pthread_cond_init(&COND_instance_stopped, 0);
...@@ -392,9 +417,9 @@ Instance::~Instance() ...@@ -392,9 +417,9 @@ Instance::~Instance()
} }
int Instance::is_crashed() bool Instance::is_crashed()
{ {
int val; bool val;
pthread_mutex_lock(&LOCK_instance); pthread_mutex_lock(&LOCK_instance);
val= crashed; val= crashed;
pthread_mutex_unlock(&LOCK_instance); pthread_mutex_unlock(&LOCK_instance);
...@@ -413,10 +438,17 @@ bool Instance::is_running() ...@@ -413,10 +438,17 @@ bool Instance::is_running()
bool return_val; bool return_val;
if (options.mysqld_port) 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; port= options.mysqld_port_val;
}
if (options.mysqld_socket) if (options.mysqld_socket)
socket= strchr(options.mysqld_socket, '=') + 1; socket= options.mysqld_socket;
/* no port was specified => instance falled back to default value */ /* no port was specified => instance falled back to default value */
if (!options.mysqld_port && !options.mysqld_socket) if (!options.mysqld_port && !options.mysqld_socket)
...@@ -469,8 +501,15 @@ int Instance::stop() ...@@ -469,8 +501,15 @@ int Instance::stop()
struct timespec timeout; struct timespec timeout;
uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY; 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; waitchild= options.shutdown_delay_val;
}
kill_instance(SIGTERM); kill_instance(SIGTERM);
/* sleep on condition to wait for SIGCHLD */ /* sleep on condition to wait for SIGCHLD */
...@@ -588,20 +627,33 @@ void Instance::kill_instance(int signum) ...@@ -588,20 +627,33 @@ void Instance::kill_instance(int signum)
} }
/* /*
We execute this function to initialize instance parameters. Initialize instance parameters.
Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY.
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); return options.init(name_arg);
} }
int Instance::complete_initialization(Instance_map *instance_map_arg, int Instance::complete_initialization(Instance_map *instance_map_arg,
const char *mysqld_path, const char *mysqld_path)
uint instance_type)
{ {
instance_map= instance_map_arg; 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.
*/
} }
...@@ -17,7 +17,10 @@ ...@@ -17,7 +17,10 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h> #include <my_global.h>
#include <m_string.h>
#include "instance_options.h" #include "instance_options.h"
#include "priv.h"
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface #pragma interface
...@@ -25,31 +28,120 @@ ...@@ -25,31 +28,120 @@
class Instance_map; 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 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: public:
Instance(); Instance();
~Instance(); ~Instance();
int init(const char *name); int init(const LEX_STRING *name_arg);
int complete_initialization(Instance_map *instance_map_arg, int complete_initialization(Instance_map *instance_map_arg,
const char *mysqld_path, uint instance_type); const char *mysqld_path);
bool is_running(); bool is_running();
int start(); int start();
int stop(); int stop();
/* send a signal to the instance */ /* send a signal to the instance */
void kill_instance(int signo); void kill_instance(int signo);
int is_crashed(); bool is_crashed();
void set_crash_flag_n_wake_all(); void set_crash_flag_n_wake_all();
Instance_map *get_map(); 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: public:
enum { DEFAULT_SHUTDOWN_DELAY= 35 }; enum { DEFAULT_SHUTDOWN_DELAY= 35 };
Instance_options options; Instance_options options;
private: 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 Mutex protecting the instance. Currently we use it to avoid the
double start of the instance. This happens when the instance is starting double start of the instance. This happens when the instance is starting
...@@ -66,4 +158,22 @@ private: ...@@ -66,4 +158,22 @@ private:
void remove_pid(); 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 */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */
...@@ -20,14 +20,20 @@ ...@@ -20,14 +20,20 @@
#include "instance_map.h" #include "instance_map.h"
#include <my_global.h>
#include <m_ctype.h>
#include <mysql_com.h>
#include <m_string.h>
#include "buffer.h" #include "buffer.h"
#include "guardian.h"
#include "instance.h" #include "instance.h"
#include "log.h" #include "log.h"
#include "manager.h"
#include "mysqld_error.h"
#include "mysql_manager_error.h"
#include "options.h" #include "options.h"
#include "priv.h"
#include <m_ctype.h>
#include <mysql_com.h>
#include <m_string.h>
/* /*
Note: As we are going to suppost different types of connections, 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, ...@@ -45,8 +51,8 @@ static byte* get_instance_key(const byte* u, uint* len,
my_bool __attribute__((unused)) t) my_bool __attribute__((unused)) t)
{ {
const Instance *instance= (const Instance *) u; const Instance *instance= (const Instance *) u;
*len= instance->options.instance_name_len; *len= instance->options.instance_name.length;
return (byte *) instance->options.instance_name; return (byte *) instance->options.instance_name.str;
} }
static void delete_instance(void *u) static void delete_instance(void *u)
...@@ -79,15 +85,59 @@ static void delete_instance(void *u) ...@@ -79,15 +85,59 @@ static void delete_instance(void *u)
static int process_option(void *ctx, const char *group, const char *option) 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_str, option);
return map->process_one_option(group, option);
} }
C_MODE_END 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. Process one option from the configuration file.
...@@ -103,34 +153,64 @@ C_MODE_END ...@@ -103,34 +153,64 @@ C_MODE_END
of the instance map object. 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; Instance *instance= NULL;
static const char prefix[]= { 'm', 'y', 's', 'q', 'l', 'd' };
if (strncmp(group, prefix, sizeof prefix) == 0 && if (!Instance::is_name_valid(group))
((my_isdigit(default_charset_info, group[sizeof prefix])) {
|| group[sizeof(prefix)] == '\0')) /*
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, delete instance;
strlen(group)))) return 1;
{
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 */
} }
return 0; if (instance->is_mysqld_compatible())
log_info("Warning: instance name '%s' is mysqld-compatible.",
(const char *) group->str);
err_instance: log_info("mysqld instance '%s' has been added successfully.",
delete instance; (const char *) group->str);
err: }
return 1;
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() ...@@ -181,7 +261,7 @@ void Instance_map::unlock()
- pass on the new map to the guardian thread: it will start - pass on the new map to the guardian thread: it will start
all instances that are marked `guarded' and not yet started. all instances that are marked `guarded' and not yet started.
Note, as the check whether an instance is started is currently 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 at the given port), this function has some peculiar
side-effects: side-effects:
* if the port number of a running instance was changed, the * if the port number of a running instance was changed, the
...@@ -194,9 +274,9 @@ void Instance_map::unlock() ...@@ -194,9 +274,9 @@ void Instance_map::unlock()
In order to avoid such side effects one should never call In order to avoid such side effects one should never call
FLUSH INSTANCES without prior stop of all running instances. FLUSH INSTANCES without prior stop of all running instances.
TODO NOTE: The operation should be invoked with the following locks acquired:
FLUSH INSTANCES should return an error if it's called - Guardian_thread;
while there is a running instance. - Instance_map;
*/ */
int Instance_map::flush_instances() int Instance_map::flush_instances()
...@@ -209,67 +289,169 @@ int Instance_map::flush_instances() ...@@ -209,67 +289,169 @@ int Instance_map::flush_instances()
guardian (2) reload the instance map (3) reinitialize the guardian guardian (2) reload the instance map (3) reinitialize the guardian
with new instances. with new instances.
*/ */
guardian->lock();
pthread_mutex_lock(&LOCK_instance_map);
hash_free(&hash); hash_free(&hash);
hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
get_instance_key, delete_instance, 0); get_instance_key, delete_instance, 0);
rc= load(); rc= load();
guardian->init(); guardian->init();
pthread_mutex_unlock(&LOCK_instance_map);
guardian->unlock();
return rc; return rc;
} }
Instance * bool Instance_map::is_there_active_instance()
Instance_map::find(const char *name, uint name_len)
{ {
Instance *instance; Instance *instance;
pthread_mutex_lock(&LOCK_instance_map); Iterator iterator(this);
instance= (Instance *) hash_search(&hash, (byte *) name, name_len);
pthread_mutex_unlock(&LOCK_instance_map); while ((instance= iterator.next()))
return instance; {
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; return my_hash_insert(&hash, (byte *) instance);
uint i= 0; }
if (hash.records == 0) /* no instances found */ int Instance_map::remove_instance(Instance *instance)
{ {
if ((instance= new Instance) == 0) return hash_delete(&hash, (byte *) instance);
goto err; }
if (instance->init("mysqld") || my_hash_insert(&hash, (byte *) instance))
goto err_instance;
/* int Instance_map::create_instance(const LEX_STRING *instance_name,
After an instance have been added to the instance_map, const Named_value_arr *options)
hash_free should handle it's deletion => goto err, not {
err_instance. Instance *instance= new Instance();
*/
if (instance->complete_initialization(this, mysqld_path, if (!instance)
DEFAULT_SINGLE_INSTANCE)) {
goto err; 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); log_error("Error: IM-option (%s) can not be used "
if (instance->complete_initialization(this, mysqld_path, USUAL_INSTANCE)) "in configuration of mysqld-compatible instance (%s).",
goto err; (const char *) option.get_name(),
i++; (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; 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() ...@@ -297,10 +479,10 @@ int Instance_map::load()
name and start looking for files named "my.cnf.cnf" in all name and start looking for files named "my.cnf.cnf" in all
default dirs. Which is not what we want. 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", snprintf(defaults_file_arg, FN_REFLEN, "--defaults-file=%s",
Options::config_file); Options::Main::config_file);
argv_options[1]= defaults_file_arg; argv_options[1]= defaults_file_arg;
argv_options[2]= '\0'; argv_options[2]= '\0';
...@@ -314,15 +496,12 @@ int Instance_map::load() ...@@ -314,15 +496,12 @@ int Instance_map::load()
If the routine failed, we'll simply fallback to defaults in If the routine failed, we'll simply fallback to defaults in
complete_initialization(). 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, (char ***) &argv, &args_used,
process_option, (void*) this)) process_option, (void*) this))
log_info("Falling back to compiled-in defaults"); log_info("Falling back to compiled-in defaults");
if (complete_initialization()) return complete_initialization();
return 1;
return 0;
} }
...@@ -343,3 +522,105 @@ Instance *Instance_map::Iterator::next() ...@@ -343,3 +522,105 @@ Instance *Instance_map::Iterator::next()
return NULL; 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;
}
...@@ -17,10 +17,6 @@ ...@@ -17,10 +17,6 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h> #include <my_global.h>
#include "protocol.h"
#include "guardian.h"
#include <my_sys.h> #include <my_sys.h>
#include <hash.h> #include <hash.h>
...@@ -28,10 +24,18 @@ ...@@ -28,10 +24,18 @@
#pragma interface #pragma interface
#endif #endif
struct LEX_STRING;
class Guardian_thread;
class Instance; class Instance;
class Named_value_arr;
extern int load_all_groups(char ***groups, const char *filename); extern int load_all_groups(char ***groups, const char *filename);
extern void free_groups(char **groups); 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 Instance_map - stores all existing instances
...@@ -56,22 +60,64 @@ public: ...@@ -56,22 +60,64 @@ public:
}; };
friend class Iterator; friend class Iterator;
public: 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(); 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 lock();
void unlock(); void unlock();
int init(); int init();
/* /*
Process a given option and assign it to appropricate instance. This is Process a given option and assign it to appropricate instance. This is
required for the option handler, passed to my_search_option_files(). 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(const char *default_mysqld_path_arg);
~Instance_map(); ~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: public:
const char *mysqld_path; const char *mysqld_path;
Guardian_thread *guardian; Guardian_thread *guardian;
...@@ -80,7 +126,7 @@ private: ...@@ -80,7 +126,7 @@ private:
/* loads options from config files */ /* loads options from config files */
int load(); int load();
/* inits instances argv's after all options have been loaded */ /* inits instances argv's after all options have been loaded */
int complete_initialization(); bool complete_initialization();
private: private:
enum { START_HASH_SIZE = 16 }; enum { START_HASH_SIZE = 16 };
pthread_mutex_t LOCK_instance_map; pthread_mutex_t LOCK_instance_map;
......
...@@ -20,27 +20,24 @@ ...@@ -20,27 +20,24 @@
#include "instance_options.h" #include "instance_options.h"
#include "parse_output.h" #include <my_global.h>
#include "buffer.h"
#include <my_sys.h> #include <my_sys.h>
#include <signal.h>
#include <m_string.h> #include <m_string.h>
#ifdef __WIN__ #include <signal.h>
#define NEWLINE_LEN 2
#else #include "buffer.h"
#define NEWLINE_LEN 1 #include "instance.h"
#endif #include "log.h"
#include "parse_output.h"
#include "priv.h"
/* Create "mysqld ..." command in the buffer */ /* Create "mysqld ..." command in the buffer */
static inline int create_mysqld_command(Buffer *buf, static inline int create_mysqld_command(Buffer *buf,
const char *mysqld_path_str, const LEX_STRING *mysqld_path,
uint mysqld_path_len, const LEX_STRING *option)
const char *option,
uint option_len)
{ {
int position= 0; int position= 0;
...@@ -49,13 +46,13 @@ static inline int create_mysqld_command(Buffer *buf, ...@@ -49,13 +46,13 @@ static inline int create_mysqld_command(Buffer *buf,
#ifdef __WIN__ #ifdef __WIN__
buf->append(position++, "\"", 1); buf->append(position++, "\"", 1);
#endif #endif
buf->append(position, mysqld_path_str, mysqld_path_len); buf->append(position, mysqld_path->str, mysqld_path->length);
position+= mysqld_path_len; position+= mysqld_path->length;
#ifdef __WIN__ #ifdef __WIN__
buf->append(position++, "\"", 1); buf->append(position++, "\"", 1);
#endif #endif
/* here the '\0' character is copied from the option string */ /* 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(); return buf->is_error();
} }
...@@ -63,6 +60,39 @@ static inline int create_mysqld_command(Buffer *buf, ...@@ -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 Get compiled-in value of default_option
...@@ -86,13 +116,13 @@ int Instance_options::get_default_option(char *result, size_t result_len, ...@@ -86,13 +116,13 @@ int Instance_options::get_default_option(char *result, size_t result_len,
const char *option_name) const char *option_name)
{ {
int rc= 1; 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' */ /* reserve space for the path + option + final '\0' */
Buffer cmd(mysqld_path_len + sizeof(verbose_option)); Buffer cmd(mysqld_path.length + verbose_option.length + 1);
if (create_mysqld_command(&cmd, mysqld_path, mysqld_path_len, if (create_mysqld_command(&cmd, &mysqld_path, &verbose_option))
verbose_option, sizeof(verbose_option)))
goto err; goto err;
/* +2 eats first "--" from the option string (E.g. "--datadir") */ /* +2 eats first "--" from the option string (E.g. "--datadir") */
...@@ -120,21 +150,19 @@ err: ...@@ -120,21 +150,19 @@ err:
int Instance_options::fill_instance_version() int Instance_options::fill_instance_version()
{ {
enum { MAX_VERSION_STRING_LENGTH= 160 }; char result[MAX_VERSION_LENGTH];
char result[MAX_VERSION_STRING_LENGTH]; LEX_STRING version_option=
char version_option[]= " --no-defaults --version"; { C_STRING_WITH_SIZE(" --no-defaults --version") };
int rc= 1; 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, if (create_mysqld_command(&cmd, &mysqld_path, &version_option))
version_option, sizeof(version_option)))
goto err; goto err;
bzero(result, MAX_VERSION_STRING_LENGTH); bzero(result, MAX_VERSION_LENGTH);
rc= parse_output_and_get_value(cmd.buffer, "Ver", rc= parse_output_and_get_value(cmd.buffer, "Ver", result,
result, MAX_VERSION_STRING_LENGTH, MAX_VERSION_LENGTH, GET_LINE);
GET_LINE);
if (*result != '\0') if (*result != '\0')
{ {
...@@ -145,6 +173,7 @@ int Instance_options::fill_instance_version() ...@@ -145,6 +173,7 @@ int Instance_options::fill_instance_version()
start= result; start= result;
while (my_isspace(default_charset_info, *start)) while (my_isspace(default_charset_info, *start))
++start; ++start;
mysqld_version= strdup_root(&alloc, start); mysqld_version= strdup_root(&alloc, start);
} }
err: err:
...@@ -200,8 +229,7 @@ int Instance_options::fill_log_options() ...@@ -200,8 +229,7 @@ int Instance_options::fill_log_options()
else else
{ {
/* below is safe, as --datadir always has a value */ /* below is safe, as --datadir always has a value */
strmake(datadir, strmake(datadir, mysqld_datadir, MAX_LOG_OPTION_LENGTH - 1);
strchr(mysqld_datadir, '=') + 1, MAX_LOG_OPTION_LENGTH - 1);
} }
if (gethostname(hostname,sizeof(hostname)-1) < 0) if (gethostname(hostname,sizeof(hostname)-1) < 0)
...@@ -287,7 +315,6 @@ err: ...@@ -287,7 +315,6 @@ err:
int Instance_options::get_pid_filename(char *result) int Instance_options::get_pid_filename(char *result)
{ {
const char *pid_file= mysqld_pid_file;
char datadir[MAX_PATH_LEN]; char datadir[MAX_PATH_LEN];
if (mysqld_datadir == NULL) if (mysqld_datadir == NULL)
...@@ -297,14 +324,10 @@ int Instance_options::get_pid_filename(char *result) ...@@ -297,14 +324,10 @@ int Instance_options::get_pid_filename(char *result)
return 1; return 1;
} }
else else
strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1, strxnmov(datadir, MAX_PATH_LEN - 1, mysqld_datadir, "/", NullS);
"/", NullS);
DBUG_ASSERT(mysqld_pid_file);
pid_file= strchr(pid_file, '=') + 1;
/* get the full path to the pidfile */ /* get the full path to the pidfile */
my_load_path(result, pid_file, datadir); my_load_path(result, mysqld_pid_file, datadir);
return 0; return 0;
} }
...@@ -333,23 +356,22 @@ pid_t Instance_options::get_pid() ...@@ -333,23 +356,22 @@ pid_t Instance_options::get_pid()
} }
int Instance_options::complete_initialization(const char *default_path, int Instance_options::complete_initialization(const char *default_path)
uint instance_type)
{ {
const char *tmp; const char *tmp;
char *end; 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; goto err;
// it's safe to cast this to char* since this is a buffer we are allocating // 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; end[-1]= 0;
mysqld_path_len= strlen(mysqld_path); mysqld_path.length= strlen(mysqld_path.str);
if (mysqld_port) if (mysqld_port)
mysqld_port_val= atoi(strchr(mysqld_port, '=') + 1); mysqld_port_val= atoi(mysqld_port);
if (shutdown_delay) if (shutdown_delay)
shutdown_delay_val= atoi(shutdown_delay); shutdown_delay_val= atoi(shutdown_delay);
...@@ -357,7 +379,7 @@ int Instance_options::complete_initialization(const char *default_path, ...@@ -357,7 +379,7 @@ int Instance_options::complete_initialization(const char *default_path,
if (!(tmp= strdup_root(&alloc, "--no-defaults"))) if (!(tmp= strdup_root(&alloc, "--no-defaults")))
goto err; goto err;
if (!(mysqld_pid_file)) if (!mysqld_pid_file)
{ {
char pidfilename[MAX_PATH_LEN]; char pidfilename[MAX_PATH_LEN];
char hostname[MAX_PATH_LEN]; char hostname[MAX_PATH_LEN];
...@@ -366,26 +388,27 @@ int Instance_options::complete_initialization(const char *default_path, ...@@ -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 If we created only one istance [mysqld], because no config. files were
found, we would like to model mysqld pid file values. found, we would like to model mysqld pid file values.
*/ */
if (!gethostname(hostname, sizeof(hostname) - 1)) if (!gethostname(hostname, sizeof(hostname) - 1))
{ {
if (instance_type & DEFAULT_SINGLE_INSTANCE) if (Instance::is_mysqld_compatible_name(&instance_name))
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname, strxnmov(pidfilename, MAX_PATH_LEN - 1, hostname, ".pid", NullS);
".pid", NullS);
else else
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name, strxnmov(pidfilename, MAX_PATH_LEN - 1, instance_name.str, "-",
"-", hostname, ".pid", NullS); hostname, ".pid", NullS);
} }
else else
{ {
if (instance_type & DEFAULT_SINGLE_INSTANCE) if (Instance::is_mysqld_compatible_name(&instance_name))
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", "mysql", strxnmov(pidfilename, MAX_PATH_LEN - 1, "mysql", ".pid", NullS);
".pid", NullS);
else else
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name, strxnmov(pidfilename, MAX_PATH_LEN - 1, instance_name.str, ".pid",
".pid", NullS); NullS);
} }
add_option(pidfilename); Named_value option((char *) "pid-file", pidfilename);
set_option(&option);
} }
if (get_pid_filename(pid_file_with_path)) if (get_pid_filename(pid_file_with_path))
...@@ -393,20 +416,37 @@ int Instance_options::complete_initialization(const char *default_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 */ /* we need to reserve space for the final zero + possible default options */
if (!(argv= (char**) if (!(argv= (char**)
alloc_root(&alloc, (options_array.elements + 1 alloc_root(&alloc, (get_num_options() + 1
+ MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*)))) + MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*))))
goto err; goto err;
filled_default_options= 0;
/* the path must be first in the argv */ /* the path must be first in the argv */
if (add_to_argv(mysqld_path)) if (add_to_argv(mysqld_path.str))
goto err; goto err;
if (add_to_argv(tmp)) if (add_to_argv(tmp))
goto err; goto err;
memcpy((gptr) (argv + filled_default_options), options_array.buffer, int arg_idx= filled_default_options;
options_array.elements*sizeof(char*)); for (int opt_idx= 0; opt_idx < get_num_options(); ++opt_idx)
argv[filled_default_options + options_array.elements]= 0; {
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()) if (fill_log_options() || fill_instance_version())
goto err; goto err;
...@@ -418,75 +458,91 @@ err: ...@@ -418,75 +458,91 @@ err:
} }
/* bool Instance_options::set_option(Named_value *option)
Assigns given value to the appropriate option from the class. {
bool err_status;
int idx= find_option(option->get_name());
char *option_name_str;
char *option_value_str;
SYNOPSYS if (!(option_name_str= Named_value::alloc_str(option->get_name())))
add_option() return TRUE;
option string with the option prefixed by --
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 if (idx < 0)
0 - ok err_status= options.add_element(&option_copy);
1 - error occured 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; int idx= find_option(option_name);
enum { SAVE_VALUE= 1, SAVE_WHOLE, SAVE_WHOLE_AND_ADD };
struct selected_options_st 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; const char *name;
uint length; uint name_len;
const char **value; const char **var;
uint type; } options_def[]=
} options[]=
{ {
{"--socket=", 9, &mysqld_socket, SAVE_WHOLE_AND_ADD}, {"socket", 6, &mysqld_socket},
{"--port=", 7, &mysqld_port, SAVE_WHOLE_AND_ADD}, {"port", 4, &mysqld_port},
{"--datadir=", 10, &mysqld_datadir, SAVE_WHOLE_AND_ADD}, {"datadir", 7, &mysqld_datadir},
{"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD}, {"pid-file", 8, &mysqld_pid_file},
{"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD}, {"nonguarded", 10, &nonguarded},
{"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE}, {"mysqld-path", 11, (const char **) &mysqld_path.str},
{"--nonguarded", 9, &nonguarded, SAVE_WHOLE}, {"shutdown-delay", 14, &shutdown_delay},
{"--shutdown_delay", 9, &shutdown_delay, SAVE_VALUE}, {NULL, 0, NULL}
{NULL, 0, NULL, 0}
}; };
struct selected_options_st *selected_options;
if (!(tmp= strdup_root(&alloc, option))) for (options_st *opt= options_def; opt->name; ++opt)
goto err; {
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) ...@@ -504,7 +560,10 @@ int Instance_options::add_to_argv(const char* option)
void Instance_options::print_argv() void Instance_options::print_argv()
{ {
int i; 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++) for (i=0; argv[i] != NULL; i++)
printf("argv: %s\n", argv[i]); printf("argv: %s\n", argv[i]);
} }
...@@ -515,17 +574,17 @@ void Instance_options::print_argv() ...@@ -515,17 +574,17 @@ void Instance_options::print_argv()
Return value: 0 - ok. 1 - unable to allocate memory. 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); 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; goto err;
if (!(instance_name= strmake_root(&alloc, (char*) instance_name_arg, if (!(instance_name.str= strmake_root(&alloc, instance_name_arg->str,
instance_name_len))) instance_name_arg->length)))
goto err; goto err;
return 0; return 0;
...@@ -538,6 +597,4 @@ err: ...@@ -538,6 +597,4 @@ err:
Instance_options::~Instance_options() Instance_options::~Instance_options()
{ {
free_root(&alloc, MYF(0)); free_root(&alloc, MYF(0));
delete_dynamic(&options_array);
} }
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
#include <my_global.h> #include <my_global.h>
#include <my_sys.h> #include <my_sys.h>
#include "parse.h" #include "parse.h"
#include "portability.h"
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface #pragma interface
...@@ -35,24 +35,26 @@ ...@@ -35,24 +35,26 @@
don't have to synchronize between threads. don't have to synchronize between threads.
*/ */
#define USUAL_INSTANCE 0
#define DEFAULT_SINGLE_INSTANCE 1
class Instance_options class Instance_options
{ {
public: public:
Instance_options() : /* The operation is used to check if the option is IM-specific or not. */
mysqld_version(0), mysqld_socket(0), mysqld_datadir(0), static bool is_option_im_specific(const char *option_name);
mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0),
mysqld_port_val(0), mysqld_path(0), nonguarded(0), shutdown_delay(0), public:
shutdown_delay_val(0), filled_default_options(0) Instance_options();
{}
~Instance_options(); ~Instance_options();
/* fills in argv */ /* 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); bool set_option(Named_value *option);
int init(const char *instance_name_arg); 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(); pid_t get_pid();
int get_pid_filename(char *result); int get_pid_filename(char *result);
int unlink_pidfile(); int unlink_pidfile();
...@@ -65,7 +67,6 @@ public: ...@@ -65,7 +67,6 @@ public:
*/ */
enum { MAX_PATH_LEN= 512 }; enum { MAX_PATH_LEN= 512 };
enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 2 }; enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 2 };
enum { MEM_ROOT_BLOCK_SIZE= 512 };
char pid_file_with_path[MAX_PATH_LEN]; char pid_file_with_path[MAX_PATH_LEN];
char **argv; char **argv;
/* /*
...@@ -76,31 +77,44 @@ public: ...@@ -76,31 +77,44 @@ public:
/* We need the some options, so we store them as a separate pointers */ /* We need the some options, so we store them as a separate pointers */
const char *mysqld_socket; const char *mysqld_socket;
const char *mysqld_datadir; const char *mysqld_datadir;
const char *mysqld_bind_address;
const char *mysqld_pid_file; const char *mysqld_pid_file;
const char *mysqld_port; const char *mysqld_port;
uint mysqld_port_val; uint mysqld_port_val;
const char *instance_name; LEX_STRING instance_name;
uint instance_name_len; LEX_STRING mysqld_path;
const char *mysqld_path;
uint mysqld_path_len;
const char *nonguarded; const char *nonguarded;
const char *shutdown_delay; const char *shutdown_delay;
uint shutdown_delay_val; uint shutdown_delay_val;
/* log enums are defined in parse.h */ /* log enums are defined in parse.h */
char *logs[3]; char *logs[3];
/* this value is computed and cashed here */
DYNAMIC_ARRAY options_array;
private: private:
int fill_log_options(); int fill_log_options();
int fill_instance_version(); int fill_instance_version();
int add_to_argv(const char *option); int add_to_argv(const char *option);
int get_default_option(char *result, size_t result_len, int get_default_option(char *result, size_t result_len,
const char *option_name); const char *option_name);
void update_var(const char *option_name, const char *option_value);
int find_option(const char *option_name);
private: private:
uint filled_default_options; uint filled_default_options;
MEM_ROOT alloc; 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 */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H */
...@@ -19,21 +19,23 @@ ...@@ -19,21 +19,23 @@
#endif #endif
#include "listener.h" #include "listener.h"
#include "priv.h"
#include <m_string.h> #include <my_global.h>
#include <mysql.h> #include <mysql.h>
#include <violite.h> #include <violite.h>
#include <sys/stat.h>
#ifndef __WIN__ #ifndef __WIN__
#include <sys/un.h> #include <sys/un.h>
#endif #endif
#include <sys/stat.h>
#include "thread_registry.h"
#include "options.h"
#include "instance_map.h" #include "instance_map.h"
#include "log.h" #include "log.h"
#include "mysql_connection.h" #include "mysql_connection.h"
#include "options.h"
#include "portability.h" #include "portability.h"
#include "priv.h"
#include "thread_registry.h"
/* /*
...@@ -62,8 +64,7 @@ private: ...@@ -62,8 +64,7 @@ private:
Listener_thread::Listener_thread(const Listener_thread_args &args) : Listener_thread::Listener_thread(const Listener_thread_args &args) :
Listener_thread_args(args.thread_registry, args.options, args.user_map, Listener_thread_args(args.thread_registry, args.user_map, args.instance_map)
args.instance_map)
,total_connection_count(0) ,total_connection_count(0)
,thread_info(pthread_self()) ,thread_info(pthread_self())
,num_sockets(0) ,num_sockets(0)
...@@ -234,14 +235,16 @@ int Listener_thread::create_tcp_socket() ...@@ -234,14 +235,16 @@ int Listener_thread::create_tcp_socket()
bzero(&ip_socket_address, sizeof(ip_socket_address)); bzero(&ip_socket_address, sizeof(ip_socket_address));
ulong im_bind_addr; 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); im_bind_addr= htonl(INADDR_ANY);
} }
else else
im_bind_addr= htonl(INADDR_ANY); 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_family= AF_INET;
ip_socket_address.sin_addr.s_addr= im_bind_addr; ip_socket_address.sin_addr.s_addr= im_bind_addr;
...@@ -295,7 +298,7 @@ create_unix_socket(struct sockaddr_un &unix_socket_address) ...@@ -295,7 +298,7 @@ create_unix_socket(struct sockaddr_un &unix_socket_address)
bzero(&unix_socket_address, sizeof(unix_socket_address)); bzero(&unix_socket_address, sizeof(unix_socket_address));
unix_socket_address.sun_family= AF_UNIX; 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)); sizeof(unix_socket_address.sun_path));
unlink(unix_socket_address.sun_path); // in case we have stale socket file unlink(unix_socket_address.sun_path); // in case we have stale socket file
......
...@@ -27,23 +27,19 @@ ...@@ -27,23 +27,19 @@
pthread_handler_t listener(void *arg); pthread_handler_t listener(void *arg);
class Thread_registry; class Thread_registry;
struct Options;
class User_map; class User_map;
class Instance_map; class Instance_map;
struct Listener_thread_args struct Listener_thread_args
{ {
Thread_registry &thread_registry; Thread_registry &thread_registry;
const Options &options;
const User_map &user_map; const User_map &user_map;
Instance_map &instance_map; Instance_map &instance_map;
Listener_thread_args(Thread_registry &thread_registry_arg, Listener_thread_args(Thread_registry &thread_registry_arg,
const Options &options_arg,
const User_map &user_map_arg, const User_map &user_map_arg,
Instance_map &instance_map_arg) : Instance_map &instance_map_arg) :
thread_registry(thread_registry_arg) thread_registry(thread_registry_arg)
,options(options_arg)
,user_map(user_map_arg) ,user_map(user_map_arg)
,instance_map(instance_map_arg) ,instance_map(instance_map_arg)
{} {}
......
...@@ -14,14 +14,14 @@ ...@@ -14,14 +14,14 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
#include "log.h" #include "log.h"
#include "portability.h"
#include <stdarg.h> #include <my_global.h>
#include <m_string.h> #include <m_string.h>
#include <my_sys.h> #include <my_sys.h>
#include <stdarg.h>
/* /*
TODO: TODO:
- add flexible header support - add flexible header support
...@@ -71,7 +71,7 @@ static inline void log(FILE *file, const char *format, va_list args) ...@@ -71,7 +71,7 @@ static inline void log(FILE *file, const char *format, va_list args)
{ {
int size= sizeof(buff_stack) * 2; int size= sizeof(buff_stack) * 2;
buff_msg= (char*) my_malloc(size, MYF(0)); buff_msg= (char*) my_malloc(size, MYF(0));
while (true) while (TRUE)
{ {
if (buff_msg == 0) if (buff_msg == 0)
{ {
......
...@@ -14,39 +14,55 @@ ...@@ -14,39 +14,55 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
#include "manager.h" #include "manager.h"
#include "priv.h" #include <my_global.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 <m_string.h> #include <m_string.h>
#include <signal.h> #include <my_sys.h>
#include <thr_alarm.h> #include <thr_alarm.h>
#include <signal.h>
#ifndef __WIN__ #ifndef __WIN__
#include <sys/wait.h> #include <sys/wait.h>
#endif #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) static int create_pid_file(const char *pid_file_name)
{ {
if (FILE *pid_file= my_fopen(pid_file_name, FILE *pid_file;
O_WRONLY | O_CREAT | O_BINARY, MYF(0)))
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()); log_error("Error: can not write to pid file '%s': %s (errno: %d)",
my_fclose(pid_file, MYF(0)); (const char *) pid_file_name,
return 0; (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)); my_fclose(pid_file, MYF(0));
return 1;
return 0;
} }
#ifndef __WIN__ #ifndef __WIN__
...@@ -82,14 +98,14 @@ bool have_signal; ...@@ -82,14 +98,14 @@ bool have_signal;
void onsignal(int signo) void onsignal(int signo)
{ {
have_signal= true; have_signal= TRUE;
} }
void set_signals(sigset_t *set) void set_signals(sigset_t *set)
{ {
signal(SIGINT, onsignal); signal(SIGINT, onsignal);
signal(SIGTERM, onsignal); signal(SIGTERM, onsignal);
have_signal= false; have_signal= FALSE;
} }
int my_sigwait(const sigset_t *set, int *sig) int my_sigwait(const sigset_t *set, int *sig)
...@@ -109,10 +125,15 @@ 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. listener thread, write pid file and enter into signal handling.
See also comments in mysqlmanager.cc to picture general Instance Manager See also comments in mysqlmanager.cc to picture general Instance Manager
architecture. 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; Thread_registry thread_registry;
/* /*
All objects created in the manager() function live as long as All objects created in the manager() function live as long as
...@@ -121,26 +142,55 @@ void manager(const Options &options) ...@@ -121,26 +142,55 @@ void manager(const Options &options)
*/ */
User_map user_map; 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, Guardian_thread guardian_thread(thread_registry,
&instance_map, &instance_map,
options.monitoring_interval); Options::Main::monitoring_interval);
Listener_thread_args listener_args(thread_registry, options, user_map, Listener_thread_args listener_args(thread_registry, user_map, instance_map);
instance_map);
manager_pid= getpid(); manager_pid= getpid();
instance_map.guardian= &guardian_thread; 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; 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; 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 */ /* write pid file */
if (create_pid_file(options.pid_file_name)) if (create_pid_file(Options::Main::pid_file_name))
return; return; /* necessary logging has been already done. */
sigset_t mask; sigset_t mask;
set_signals(&mask); set_signals(&mask);
...@@ -198,7 +248,15 @@ void manager(const Options &options) ...@@ -198,7 +248,15 @@ void manager(const Options &options)
shutdown_complete= FALSE; 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 " log_error("Cannot init instances repository. This might be caused by "
"the wrong config file options. For instance, missing mysqld " "the wrong config file options. For instance, missing mysqld "
...@@ -240,7 +298,7 @@ void manager(const Options &options) ...@@ -240,7 +298,7 @@ void manager(const Options &options)
{ {
if (!guardian_thread.is_stopped()) if (!guardian_thread.is_stopped())
{ {
bool stop_instances= true; bool stop_instances= TRUE;
guardian_thread.request_shutdown(stop_instances); guardian_thread.request_shutdown(stop_instances);
pthread_cond_signal(&guardian_thread.COND_guardian); pthread_cond_signal(&guardian_thread.COND_guardian);
} }
...@@ -254,7 +312,7 @@ void manager(const Options &options) ...@@ -254,7 +312,7 @@ void manager(const Options &options)
err: err:
/* delete the pid file */ /* delete the pid file */
my_delete(options.pid_file_name, MYF(0)); my_delete(Options::Main::pid_file_name, MYF(0));
#ifndef __WIN__ #ifndef __WIN__
/* free alarm structures */ /* free alarm structures */
...@@ -262,4 +320,3 @@ err: ...@@ -262,4 +320,3 @@ err:
/* don't pthread_exit to kill all threads who did not shut down in time */ /* don't pthread_exit to kill all threads who did not shut down in time */
#endif #endif
} }
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
struct Options; void manager();
void manager(const Options &options);
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H
...@@ -14,15 +14,14 @@ ...@@ -14,15 +14,14 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
#include "messages.h" #include "messages.h"
#include <my_global.h>
#include <mysql_com.h>
#include "mysqld_error.h" #include "mysqld_error.h"
#include "mysql_manager_error.h" #include "mysql_manager_error.h"
#include <mysql_com.h>
#include <assert.h>
static const char *mysqld_error_message(unsigned sql_errno) static const char *mysqld_error_message(unsigned sql_errno)
{ {
...@@ -70,6 +69,23 @@ 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"; "in the instance options";
case ER_ACCESS_OPTION_FILE: case ER_ACCESS_OPTION_FILE:
return "Cannot open the option file to edit. Check permissions"; 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: default:
DBUG_ASSERT(0); DBUG_ASSERT(0);
return 0; return 0;
......
...@@ -20,22 +20,24 @@ ...@@ -20,22 +20,24 @@
#include "mysql_connection.h" #include "mysql_connection.h"
#include "priv.h" #include <m_string.h>
#include "mysql_manager_error.h" #include <m_string.h>
#include "mysqld_error.h" #include <my_global.h>
#include "thread_registry.h" #include <mysql_com.h>
#include <mysql.h>
#include <my_sys.h>
#include <violite.h>
#include "command.h"
#include "log.h" #include "log.h"
#include "user_map.h"
#include "protocol.h"
#include "messages.h" #include "messages.h"
#include "command.h" #include "mysqld_error.h"
#include "mysql_manager_error.h"
#include "parse.h" #include "parse.h"
#include "priv.h"
#include <mysql.h> #include "protocol.h"
#include <violite.h> #include "thread_registry.h"
#include <mysql_com.h> #include "user_map.h"
#include <m_string.h>
#include <my_sys.h>
Mysql_connection_thread_args::Mysql_connection_thread_args( Mysql_connection_thread_args::Mysql_connection_thread_args(
...@@ -56,7 +58,7 @@ 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 See also comments in mysqlmanager.cc to picture general Instance Manager
architecture. architecture.
We use conventional technique to work with classes without exceptions: 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 a user must call cleanup(). All other methods are valid only between
init() and cleanup(). init() and cleanup().
*/ */
...@@ -190,8 +192,6 @@ void Mysql_connection_thread::run() ...@@ -190,8 +192,6 @@ void Mysql_connection_thread::run()
int Mysql_connection_thread::check_connection() int Mysql_connection_thread::check_connection()
{ {
ulong pkt_len=0; // to hold client reply length 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: */ /* buffer for the first packet */ /* packet contains: */
char buff[MAX_VERSION_LENGTH + 1 + // server version, 0-ended char buff[MAX_VERSION_LENGTH + 1 + // server version, 0-ended
...@@ -202,8 +202,8 @@ int Mysql_connection_thread::check_connection() ...@@ -202,8 +202,8 @@ int Mysql_connection_thread::check_connection()
char *pos= buff; char *pos= buff;
ulong server_flags; ulong server_flags;
memcpy(pos, mysqlmanager_version, mysqlmanager_version_length + 1); memcpy(pos, mysqlmanager_version.str, mysqlmanager_version.length + 1);
pos+= mysqlmanager_version_length + 1; pos+= mysqlmanager_version.length + 1;
int4store((uchar*) pos, connection_id); int4store((uchar*) pos, connection_id);
pos+= 4; pos+= 4;
...@@ -271,12 +271,14 @@ int Mysql_connection_thread::check_connection() ...@@ -271,12 +271,14 @@ int Mysql_connection_thread::check_connection()
const char *user= pos; const char *user= pos;
const char *password= strend(user)+1; const char *password= strend(user)+1;
ulong password_len= *password++; ulong password_len= *password++;
LEX_STRING user_name= { (char *) user, password - user - 2 };
if (password_len != SCRAMBLE_LENGTH) if (password_len != SCRAMBLE_LENGTH)
{ {
net_send_error(&net, ER_ACCESS_DENIED_ERROR); net_send_error(&net, ER_ACCESS_DENIED_ERROR);
return 1; 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); net_send_error(&net, ER_ACCESS_DENIED_ERROR);
return 1; return 1;
...@@ -312,7 +314,7 @@ int Mysql_connection_thread::do_command() ...@@ -312,7 +314,7 @@ int Mysql_connection_thread::do_command()
packet= (char*) net.read_pos; packet= (char*) net.read_pos;
enum enum_server_command command= (enum enum_server_command) enum enum_server_command command= (enum enum_server_command)
(uchar) *packet; (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); connection_id, packet_length, command);
return dispatch_command(command, packet + 1, packet_length - 1); return dispatch_command(command, packet + 1, packet_length - 1);
} }
...@@ -336,7 +338,7 @@ int Mysql_connection_thread::dispatch_command(enum enum_server_command command, ...@@ -336,7 +338,7 @@ int Mysql_connection_thread::dispatch_command(enum enum_server_command command,
if (Command *command= parse_command(&instance_map, packet)) if (Command *command= parse_command(&instance_map, packet))
{ {
int res= 0; 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); res= command->execute(&net, connection_id);
delete command; delete command;
if (!res) if (!res)
...@@ -380,5 +382,5 @@ pthread_handler_t mysql_connection(void *arg) ...@@ -380,5 +382,5 @@ pthread_handler_t mysql_connection(void *arg)
} }
/* /*
vim: fdm=marker vim: fdm=marker
*/ */
...@@ -29,5 +29,13 @@ ...@@ -29,5 +29,13 @@
#define ER_ACCESS_OPTION_FILE 3008 #define ER_ACCESS_OPTION_FILE 3008
#define ER_OFFSET_ERROR 3009 #define ER_OFFSET_ERROR 3009
#define ER_READ_FILE 3010 #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 */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H */
...@@ -15,25 +15,29 @@ ...@@ -15,25 +15,29 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h> #include <my_global.h>
#include "manager.h"
#include "options.h"
#include "log.h"
#include <my_sys.h> #include <my_sys.h>
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef __WIN__ #ifndef __WIN__
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
#include <sys/wait.h> #include <sys/wait.h>
#endif #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__ #ifdef __WIN__
#include "windowsservice.h" #include "windowsservice.h"
#endif #endif
/* /*
Few notes about Instance Manager architecture: Few notes about Instance Manager architecture:
Instance Manager consisits of two processes: the angel process, and the Instance Manager consisits of two processes: the angel process, and the
...@@ -59,13 +63,12 @@ ...@@ -59,13 +63,12 @@
*/ */
static void init_environment(char *progname); static void init_environment(char *progname);
#ifndef __WIN__ #ifndef __WIN__
static void daemonize(const char *log_file_name); 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 struct passwd *check_user(const char *user);
static int set_user(const char *user, struct passwd *user_info); static int set_user(const char *user, struct passwd *user_info);
#else
int HandleServiceOptions(Options options);
#endif #endif
...@@ -81,41 +84,61 @@ int main(int argc, char *argv[]) ...@@ -81,41 +84,61 @@ int main(int argc, char *argv[])
{ {
int return_value= 1; int return_value= 1;
init_environment(argv[0]); init_environment(argv[0]);
Options options;
if (options.load(argc, argv)) if ((return_value= Options::load(argc, argv)))
goto err; goto main_end;
if (Options::User_management::cmd)
{
return_value= Options::User_management::cmd->execute();
goto main_end;
}
#ifndef __WIN__ #ifndef __WIN__
struct passwd *user_info; 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)) if (set_user(Options::Daemon::user, user_info))
goto err; {
return_value= 1;
goto main_end;
}
} }
if (options.run_as_service) if (Options::Daemon::run_as_service)
{ {
/* forks, and returns only in child */ /* 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 */ /* forks again, and returns only in child: parent becomes angel */
angel(options); angel();
} }
manager();
#else #else
if (!options.stand_alone)
if (!Options::Service::stand_alone)
{ {
if (HandleServiceOptions(options)) if (HandleServiceOptions())
goto err; {
return_value= 1;
goto main_end;
}
} }
else else
{
manager();
}
#endif #endif
manager(options);
return_value= 0; return_value= 0;
err: main_end:
options.cleanup(); Options::cleanup();
my_end(0); my_end(0);
return return_value; return return_value;
} }
...@@ -200,7 +223,7 @@ static void init_environment(char *progname) ...@@ -200,7 +223,7 @@ static void init_environment(char *progname)
MY_INIT(progname); MY_INIT(progname);
log_init(); log_init();
umask(0117); umask(0117);
srand(time(0)); srand((unsigned int) time(0));
} }
...@@ -298,7 +321,7 @@ void terminate(int signo) ...@@ -298,7 +321,7 @@ void terminate(int signo)
Angel process will exit silently if mysqlmanager exits normally. Angel process will exit silently if mysqlmanager exits normally.
*/ */
static void angel(const Options &options) static void angel()
{ {
/* install signal handlers */ /* install signal handlers */
sigset_t zeromask; // to sigsuspend in parent sigset_t zeromask; // to sigsuspend in parent
......
...@@ -20,45 +20,88 @@ ...@@ -20,45 +20,88 @@
#include "options.h" #include "options.h"
#include "priv.h" #include <my_global.h>
#include "portability.h"
#include <my_sys.h> #include <my_sys.h>
#include <my_getopt.h> #include <my_getopt.h>
#include <m_string.h>
#include <mysql_com.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 QUOTE2(x) #x
#define QUOTE(x) QUOTE2(x) #define QUOTE(x) QUOTE2(x)
#ifdef __WIN__ #ifdef __WIN__
char Options::install_as_service;
char Options::remove_service; /* Define holders for default values. */
char Options::stand_alone;
char windows_config_file[FN_REFLEN]; static char win_dflt_config_file_name[FN_REFLEN];
char default_password_file_name[FN_REFLEN]; static char win_dflt_password_file_name[FN_REFLEN];
char default_log_file_name[FN_REFLEN]; static char win_dflt_pid_file_name[FN_REFLEN];
const char *Options::config_file= windows_config_file; static char win_dflt_socket_file_name[FN_REFLEN];
#else
char Options::run_as_service; static char win_dflt_mysqld_path[FN_REFLEN];
const char *Options::user= 0; /* No default value */
const char *default_password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME); /* Define and initialize Windows-specific options. */
const char *default_log_file_name= QUOTE(DEFAULT_LOG_FILE_NAME);
const char *Options::config_file= QUOTE(DEFAULT_CONFIG_FILE); 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 #endif
const char *Options::log_file_name= default_log_file_name;
const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME); /* Remember if the config file was forced. */
const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME);
const char *Options::password_file_name= default_password_file_name; bool Options::Main::is_forced_default_file= FALSE;
const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH);
const char *Options::bind_address= 0; /* No default value */ /* Define and initialize common options. */
uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL;
uint Options::port_number= DEFAULT_PORT; const char *Options::Main::bind_address= NULL; /* No default value */
/* just to declare */ 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; char **Options::saved_argv= NULL;
/* Remember if the config file was forced */
bool Options::is_forced_default_file= 0;
#ifndef DBUG_OFF #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 #endif
/* /*
...@@ -67,23 +110,33 @@ const char *Options::default_dbug_option= "d:t:i:O,im.trace"; ...@@ -67,23 +110,33 @@ const char *Options::default_dbug_option= "d:t:i:O,im.trace";
*/ */
enum options { enum options {
OPT_PASSWD= 'P',
OPT_USERNAME= 'u',
OPT_PASSWORD= 'p',
OPT_LOG= 256, OPT_LOG= 256,
OPT_PID_FILE, OPT_PID_FILE,
OPT_SOCKET, OPT_SOCKET,
OPT_PASSWORD_FILE, OPT_PASSWORD_FILE,
OPT_MYSQLD_PATH, OPT_MYSQLD_PATH,
#ifndef __WIN__ #ifdef __WIN__
OPT_RUN_AS_SERVICE,
OPT_USER,
#else
OPT_INSTALL_SERVICE, OPT_INSTALL_SERVICE,
OPT_REMOVE_SERVICE, OPT_REMOVE_SERVICE,
OPT_STAND_ALONE, OPT_STAND_ALONE,
#else
OPT_RUN_AS_SERVICE,
OPT_USER,
#endif #endif
OPT_MONITORING_INTERVAL, OPT_MONITORING_INTERVAL,
OPT_PORT, OPT_PORT,
OPT_WAIT_TIMEOUT, 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[] = static struct my_option my_long_options[] =
...@@ -91,94 +144,151 @@ static struct my_option my_long_options[] = ...@@ -91,94 +144,151 @@ static struct my_option my_long_options[] =
{ "help", '?', "Display this help and exit.", { "help", '?', "Display this help and exit.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, 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.", { "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 }, 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 #ifndef DBUG_OFF
{"debug", '#', "Debug log.", {"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}, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif #endif
{ "default-mysqld-path", OPT_MYSQLD_PATH, "Where to look for MySQL" { "default-mysqld-path", OPT_MYSQLD_PATH, "Where to look for MySQL"
" Server binary.", " Server binary.",
(gptr *) &Options::default_mysqld_path, (gptr *) &Options::Main::default_mysqld_path,
(gptr *) &Options::default_mysqld_path, (gptr *) &Options::Main::default_mysqld_path,
0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0 }, 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__ #ifdef __WIN__
{ "install", OPT_INSTALL_SERVICE, "Install as system service.", { "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 }, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
#endif #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.", { "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 }, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
#endif
{ "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor" { "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor"
" instances in seconds.", " instances in seconds.",
(gptr *) &Options::monitoring_interval, (gptr *) &Options::Main::monitoring_interval,
(gptr *) &Options::monitoring_interval, (gptr *) &Options::Main::monitoring_interval,
0, GET_UINT, REQUIRED_ARG, DEFAULT_MONITORING_INTERVAL, 0, GET_UINT, REQUIRED_ARG, DEFAULT_MONITORING_INTERVAL,
0, 0, 0, 0, 0 }, 0, 0, 0, 0, 0 },
{ "passwd", 'P', "Prepare entry for passwd file and exit.", 0, 0, 0, { "mysqld-safe-compatible", OPT_MYSQLD_SAFE_COMPATIBLE,
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, "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" { "password-file", OPT_PASSWORD_FILE,
" and passwords here.", "Look for Instance Manager users and passwords here.",
(gptr *) &Options::password_file_name, (gptr *) &Options::Main::password_file_name,
(gptr *) &Options::password_file_name, (gptr *) &Options::Main::password_file_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{ "pid-file", OPT_PID_FILE, "Pid file to use.", { "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 }, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{ "port", OPT_PORT, "Port number to use for connections", { "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 }, 0, GET_UINT, REQUIRED_ARG, DEFAULT_PORT, 0, 0, 0, 0, 0 },
#ifdef __WIN__ #ifdef __WIN__
{ "remove", OPT_REMOVE_SERVICE, "Remove system service.", { "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}, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0},
#else #else
{ "run-as-service", OPT_RUN_AS_SERVICE, { "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 }, 0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
#endif #endif
{ "socket", OPT_SOCKET, "Socket file to use for connection.", { "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 }, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
#ifdef __WIN__ #ifdef __WIN__
{ "standalone", OPT_STAND_ALONE, "Run the application in stand alone mode.", { "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}, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0},
#else #else
{ "user", OPT_USER, "Username to start mysqlmanager", { "user", OPT_USER, "Username to start mysqlmanager",
(gptr *) &Options::user, (gptr *) &Options::Daemon::user,
(gptr *) &Options::user, (gptr *) &Options::Daemon::user,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
#endif #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, { "version", 'V', "Output version information and exit.", 0, 0, 0,
GET_NO_ARG, NO_ARG, 0, 0, 0, 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 " { "wait-timeout", OPT_WAIT_TIMEOUT, "The number of seconds IM waits "
"for activity on a connection before closing it.", "for activity on a connection before closing it.",
(gptr *) &net_read_timeout, (gptr *) &net_read_timeout, 0, GET_ULONG, (gptr *) &net_read_timeout,
REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0 }, (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 } { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }
}; };
static void version() 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); SYSTEM_TYPE, MACHINE_TYPE);
} }
...@@ -206,37 +316,6 @@ static void usage() ...@@ -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 C_MODE_START
static my_bool static my_bool
...@@ -248,16 +327,52 @@ get_one_option(int optid, ...@@ -248,16 +327,52 @@ get_one_option(int optid,
case 'V': case 'V':
version(); version();
exit(0); exit(0);
case 'P': case OPT_PASSWD:
passwd(); case OPT_ADD_USER:
exit(0); 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 '?': case '?':
usage(); usage();
exit(0); exit(0);
case '#': case '#':
#ifndef DBUG_OFF #ifndef DBUG_OFF
DBUG_SET(argument ? argument : Options::default_dbug_option); DBUG_SET(argument ? argument : Options::Debug::config_str);
DBUG_SET_INITIAL(argument ? argument : Options::default_dbug_option); DBUG_SET_INITIAL(argument ? argument : Options::Debug::config_str);
#endif #endif
break; break;
} }
...@@ -283,8 +398,8 @@ int Options::load(int argc, char **argv) ...@@ -283,8 +398,8 @@ int Options::load(int argc, char **argv)
{ {
if (is_prefix(argv[1], "--defaults-file=")) if (is_prefix(argv[1], "--defaults-file="))
{ {
Options::config_file= strchr(argv[1], '=') + 1; Main::config_file= strchr(argv[1], '=') + 1;
Options::is_forced_default_file= 1; Main::is_forced_default_file= TRUE;
} }
if (is_prefix(argv[1], "--defaults-extra-file=") || if (is_prefix(argv[1], "--defaults-extra-file=") ||
is_prefix(argv[1], "--no-defaults")) is_prefix(argv[1], "--no-defaults"))
...@@ -293,59 +408,92 @@ int Options::load(int argc, char **argv) ...@@ -293,59 +408,92 @@ int Options::load(int argc, char **argv)
fprintf(stderr, "The --defaults-extra-file and --no-defaults options" fprintf(stderr, "The --defaults-extra-file and --no-defaults options"
" are not supported by\n" " are not supported by\n"
"Instance Manager. Program aborted.\n"); "Instance Manager. Program aborted.\n");
goto err; return ERR_INVALID_USAGE;
} }
} }
#ifdef __WIN__ #ifdef __WIN__
if (setup_windows_defaults()) if (setup_windows_defaults())
goto err; {
fprintf(stderr, "Internal error: could not setup default values.\n");
return ERR_OUT_OF_MEMORY;
}
#endif #endif
/* load_defaults will reset saved_argv with a new allocated list */ /* load_defaults will reset saved_argv with a new allocated list */
saved_argv= argv; saved_argv= argv;
/* config-file options are prepended to command-line ones */ /* 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, log_info("Loading config file '%s'...",
get_one_option)) != 0) (const char *) Main::config_file);
goto err;
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 0;
return 1;
} }
void Options::cleanup() void Options::cleanup()
{ {
/* free_defaults returns nothing */ if (saved_argv)
if (Options::saved_argv != NULL) free_defaults(saved_argv);
free_defaults(Options::saved_argv);
delete User_management::cmd;
} }
#ifdef __WIN__ #ifdef __WIN__
int Options::setup_windows_defaults() static int setup_windows_defaults()
{ {
if (!GetModuleFileName(NULL, default_password_file_name, char module_full_name[FN_REFLEN];
sizeof(default_password_file_name))) char dir_name[FN_REFLEN];
return 1; char base_name[FN_REFLEN];
char *filename= strstr(default_password_file_name, ".exe"); char im_name[FN_REFLEN];
strcpy(filename, ".passwd"); char *base_name_ptr;
char *ptr;
if (!GetModuleFileName(NULL, default_log_file_name,
sizeof(default_log_file_name))) /* 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; 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; return 0;
} }
......
...@@ -17,49 +17,86 @@ ...@@ -17,49 +17,86 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* /*
Options - all possible options for the instance manager grouped in one Options - all possible command-line options for the Instance Manager grouped
struct. in one struct.
*/ */
#include <my_global.h> #include <my_global.h>
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface #pragma interface
#endif #endif
class User_management_cmd;
struct Options struct Options
{ {
#ifdef __WIN__ /*
static char install_as_service; NOTE: handle_options() expects value of my_bool type for GET_BOOL
static char remove_service; accessor (i.e. bool must not be used).
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;
/* argv pointer returned by load_defaults() to be used by free_defaults() */ struct User_management
static char **saved_argv; {
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 #ifndef DBUG_OFF
static const char *default_dbug_option; struct Debug
{
static const char *config_str;
};
#endif #endif
int load(int argc, char **argv); #ifndef __WIN__
void cleanup();
#ifdef __WIN__ struct Daemon
int setup_windows_defaults(); {
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 #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 #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H
...@@ -17,12 +17,12 @@ ...@@ -17,12 +17,12 @@
#include "parse.h" #include "parse.h"
#include "commands.h" #include "commands.h"
#include <string.h>
enum Token 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_FILES,
TOK_FLUSH, TOK_FLUSH,
TOK_GENERAL, TOK_GENERAL,
...@@ -50,6 +50,8 @@ struct tokens_st ...@@ -50,6 +50,8 @@ struct tokens_st
static struct tokens_st tokens[]= { static struct tokens_st tokens[]= {
{6, "CREATE"},
{4, "DROP"},
{5, "ERROR"}, {5, "ERROR"},
{5, "FILES"}, {5, "FILES"},
{5, "FLUSH"}, {5, "FLUSH"},
...@@ -67,6 +69,37 @@ static struct tokens_st tokens[]= { ...@@ -67,6 +69,37 @@ static struct tokens_st tokens[]= {
{5, "UNSET"} {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 Returns token no if word corresponds to some token, otherwise returns
...@@ -104,53 +137,200 @@ Token shift_token(const char **text, uint *word_len) ...@@ -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); get_word(text, &token->length);
if (*word_len == 0) if (token->length == 0)
return 1; return 1;
*id= *text; token->str= (char *) *text;
return 0; 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) Command *parse_command(Instance_map *map, const char *text)
{ {
uint word_len; uint word_len;
const char *instance_name; LEX_STRING instance_name;
uint instance_name_len;
const char *option;
uint option_len;
const char *option_value;
uint option_value_len;
const char *log_size;
Command *command; Command *command;
const char *saved_text= text; const char *saved_text= text;
bool skip= false;
const char *tmp;
Token tok1= shift_token(&text, &word_len); Token tok1= shift_token(&text, &word_len);
switch (tok1) { switch (tok1) {
case TOK_START: // fallthrough case TOK_START: // fallthrough
case TOK_STOP: case TOK_STOP:
case TOK_CREATE:
case TOK_DROP:
if (shift_token(&text, &word_len) != TOK_INSTANCE) if (shift_token(&text, &word_len) != TOK_INSTANCE)
goto syntax_error; goto syntax_error;
get_word(&text, &word_len); get_word(&text, &word_len);
if (word_len == 0) if (word_len == 0)
goto syntax_error; goto syntax_error;
instance_name= text; instance_name.str= (char *) text;
instance_name_len= word_len; instance_name.length= word_len;
text+= 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) if (tok1 == TOK_CREATE)
command= new Start_instance(map, instance_name, instance_name_len); {
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 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; break;
case TOK_FLUSH: case TOK_FLUSH:
if (shift_token(&text, &word_len) != TOK_INSTANCES) if (shift_token(&text, &word_len) != TOK_INSTANCES)
...@@ -163,53 +343,28 @@ Command *parse_command(Instance_map *map, const char *text) ...@@ -163,53 +343,28 @@ Command *parse_command(Instance_map *map, const char *text)
command= new Flush_instances(map); command= new Flush_instances(map);
break; break;
case TOK_UNSET: case TOK_UNSET:
skip= true;
case TOK_SET: case TOK_SET:
{
Abstract_option_cmd *cmd;
if (get_text_id(&text, &instance_name_len, &instance_name)) if (tok1 == TOK_SET)
goto syntax_error; cmd= new Set_option(map);
text+= instance_name_len; else
cmd= new Unset_option(map);
/* the next token should be a dot */
get_word(&text, &word_len);
if (*text != '.')
goto syntax_error;
text++;
get_word(&text, &option_len, NONSPACE); if (!cmd)
option= text; return NULL; /* Report ER_OUT_OF_RESOURCES. */
if ((tmp= strchr(text, '=')) != NULL)
option_len= tmp - text;
text+= option_len;
get_word(&text, &word_len); if (cmd->init(&text))
if (*text == '=') {
{ delete cmd;
text++; /* skip '=' */ goto syntax_error;
get_word(&text, &option_value_len, NONSPACE); }
option_value= text;
text+= option_value_len;
}
else
{
option_value= "";
option_value_len= 0;
}
/* should be empty */ command= cmd;
get_word(&text, &word_len, NONSPACE);
if (word_len)
goto syntax_error;
if (skip) break;
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;
case TOK_SHOW: case TOK_SHOW:
switch (shift_token(&text, &word_len)) { switch (shift_token(&text, &word_len)) {
case TOK_INSTANCES: case TOK_INSTANCES:
...@@ -222,30 +377,35 @@ Command *parse_command(Instance_map *map, const char *text) ...@@ -222,30 +377,35 @@ Command *parse_command(Instance_map *map, const char *text)
switch (Token tok2= shift_token(&text, &word_len)) { switch (Token tok2= shift_token(&text, &word_len)) {
case TOK_OPTIONS: case TOK_OPTIONS:
case TOK_STATUS: case TOK_STATUS:
if (get_text_id(&text, &instance_name_len, &instance_name)) if (get_text_id(&text, &instance_name))
goto syntax_error; goto syntax_error;
text+= instance_name_len; text+= instance_name.length;
/* check that this is the end of the command */ /* check that this is the end of the command */
get_word(&text, &word_len, NONSPACE); get_word(&text, &word_len, NONSPACE);
if (word_len) if (word_len)
goto syntax_error; goto syntax_error;
if (tok2 == TOK_STATUS) if (tok2 == TOK_STATUS)
command= new Show_instance_status(map, instance_name, command= new Show_instance_status(map, &instance_name);
instance_name_len);
else else
command= new Show_instance_options(map, instance_name, command= new Show_instance_options(map, &instance_name);
instance_name_len);
break; break;
default: default:
goto syntax_error; goto syntax_error;
} }
break; break;
default: default:
instance_name= text - word_len; instance_name.str= (char *) text - word_len;
instance_name_len= word_len; instance_name.length= word_len;
if (instance_name_len) if (instance_name.length)
{ {
Log_type log_type; 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)) { switch (shift_token(&text, &word_len)) {
case TOK_LOG: case TOK_LOG:
switch (Token tok3= shift_token(&text, &word_len)) { switch (Token tok3= shift_token(&text, &word_len)) {
...@@ -254,8 +414,7 @@ Command *parse_command(Instance_map *map, const char *text) ...@@ -254,8 +414,7 @@ Command *parse_command(Instance_map *map, const char *text)
/* check that this is the end of the command */ /* check that this is the end of the command */
if (word_len) if (word_len)
goto syntax_error; goto syntax_error;
command= new Show_instance_log_files(map, instance_name, command= new Show_instance_log_files(map, &instance_name);
instance_name_len);
break; break;
case TOK_ERROR: case TOK_ERROR:
case TOK_GENERAL: case TOK_GENERAL:
...@@ -275,12 +434,14 @@ Command *parse_command(Instance_map *map, const char *text) ...@@ -275,12 +434,14 @@ Command *parse_command(Instance_map *map, const char *text)
goto syntax_error; goto syntax_error;
} }
/* get the size of the log we want to retrieve */ /* 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; goto syntax_error;
text+= word_len; text+= log_size_str.length;
/* this parameter is required */ /* this parameter is required */
if (!word_len) if (!log_size_str.length)
goto syntax_error; goto syntax_error;
/* the next token should be comma, or nothing */ /* the next token should be comma, or nothing */
get_word(&text, &word_len); get_word(&text, &word_len);
switch (*text) { switch (*text) {
...@@ -290,23 +451,41 @@ Command *parse_command(Instance_map *map, const char *text) ...@@ -290,23 +451,41 @@ Command *parse_command(Instance_map *map, const char *text)
get_word(&text, &word_len); get_word(&text, &word_len);
if (!word_len) if (!word_len)
goto syntax_error; goto syntax_error;
log_offset_str.str= (char *) text;
log_offset_str.length= word_len;
text+= 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); get_word(&text, &word_len, NONSPACE);
/* check that this is the end of the command */ /* check that this is the end of the command */
if (word_len) if (word_len)
goto syntax_error; goto syntax_error;
break; break;
case '\0': case '\0':
command= new Show_instance_log(map, instance_name,
instance_name_len, log_type,
log_size, NULL);
break; /* this is ok */ break; /* this is ok */
default: 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; 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; break;
default: default:
goto syntax_error; goto syntax_error;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <my_global.h> #include <my_global.h>
#include <my_sys.h> #include <my_sys.h>
#include <m_string.h>
class Command; class Command;
class Instance_map; class Instance_map;
...@@ -29,10 +30,148 @@ enum Log_type ...@@ -29,10 +30,148 @@ enum Log_type
IM_LOG_SLOW 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 */ /* 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 tries to find next word in the text
...@@ -41,7 +180,7 @@ enum { ALPHANUM= 1, NONSPACE }; ...@@ -41,7 +180,7 @@ enum { ALPHANUM= 1, NONSPACE };
*/ */
inline void get_word(const char **text, uint *word_len, inline void get_word(const char **text, uint *word_len,
int seek_method= ALPHANUM) enum_seek_method seek_method= ALPHANUM)
{ {
const char *word_end; const char *word_end;
...@@ -51,13 +190,23 @@ inline void get_word(const char **text, uint *word_len, ...@@ -51,13 +190,23 @@ inline void get_word(const char **text, uint *word_len,
word_end= *text; word_end= *text;
if (seek_method == ALPHANUM) switch (seek_method) {
case ALPHANUM:
while (my_isalnum(default_charset_info, *word_end)) while (my_isalnum(default_charset_info, *word_end))
++word_end; ++word_end;
else break;
case NONSPACE:
while (!my_isspace(default_charset_info, *word_end) && while (!my_isspace(default_charset_info, *word_end) &&
(*word_end != '\0')) (*word_end != '\0'))
++word_end; ++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; *word_len= word_end - *text;
} }
......
...@@ -14,13 +14,15 @@ ...@@ -14,13 +14,15 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
#include "parse.h"
#include "parse_output.h" #include "parse_output.h"
#include <stdio.h> #include <my_global.h>
#include <my_sys.h> #include <my_sys.h>
#include <m_string.h> #include <m_string.h>
#include <stdio.h>
#include "parse.h"
#include "portability.h" #include "portability.h"
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
#define GET_VALUE 1 #define GET_VALUE 1
#define GET_LINE 2 #define GET_LINE 2
......
...@@ -16,11 +16,25 @@ ...@@ -16,11 +16,25 @@
/*TODO: fix this */ /*TODO: fix this */
#define PROTOCOL_VERSION 10 #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; typedef int pid_t;
#undef popen #undef popen
#define popen(A,B) _popen(A,B) #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 /* __WIN__ */
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PORTABILITY_H */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PORTABILITY_H */
......
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "priv.h"
#include <my_global.h> #include <my_global.h>
#include <mysql_com.h> #include <mysql_com.h>
#include "priv.h"
#include "portability.h"
#if defined(__ia64__) || defined(__ia64) #if defined(__ia64__) || defined(__ia64)
/* /*
...@@ -43,9 +43,7 @@ bool linuxthreads; ...@@ -43,9 +43,7 @@ bool linuxthreads;
The following string must be less then 80 characters, as The following string must be less then 80 characters, as
mysql_connection.cc relies on it mysql_connection.cc relies on it
*/ */
const char mysqlmanager_version[] = "0.2-alpha"; const LEX_STRING mysqlmanager_version= { C_STRING_WITH_SIZE("1.0-beta") };
const int mysqlmanager_version_length= sizeof(mysqlmanager_version) - 1;
const unsigned char protocol_version= PROTOCOL_VERSION; const unsigned char protocol_version= PROTOCOL_VERSION;
......
...@@ -16,13 +16,17 @@ ...@@ -16,13 +16,17 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 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> #include <sys/types.h>
#ifdef __WIN__
#include "portability.h" #ifndef __WIN__
#else
#include <unistd.h> #include <unistd.h>
#endif #endif
#include "my_pthread.h"
#include "portability.h"
/* IM-wide platform-independent defines */ /* IM-wide platform-independent defines */
#define SERVER_DEFAULT_PORT 3306 #define SERVER_DEFAULT_PORT 3306
...@@ -31,6 +35,21 @@ ...@@ -31,6 +35,21 @@
/* three-week timeout should be enough */ /* three-week timeout should be enough */
#define LONG_TIMEOUT ((ulong) 3600L*24L*21L) #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) */ /* the pid of the manager process (of the signal thread on the LinuxThreads) */
extern pid_t manager_pid; extern pid_t manager_pid;
...@@ -42,8 +61,7 @@ extern pid_t manager_pid; ...@@ -42,8 +61,7 @@ extern pid_t manager_pid;
extern bool linuxthreads; extern bool linuxthreads;
#endif #endif
extern const char mysqlmanager_version[]; extern const LEX_STRING mysqlmanager_version;
extern const int mysqlmanager_version_length;
/* MySQL client-server protocol version: substituted from configure */ /* MySQL client-server protocol version: substituted from configure */
extern const unsigned char protocol_version; extern const unsigned char protocol_version;
......
...@@ -163,7 +163,7 @@ int send_fields(struct st_net *net, LIST *fields) ...@@ -163,7 +163,7 @@ int send_fields(struct st_net *net, LIST *fields)
Buffer send_buff; Buffer send_buff;
char small_buff[4]; char small_buff[4];
uint position= 0; uint position= 0;
NAME_WITH_LENGTH *field; LEX_STRING *field;
/* send the number of fileds */ /* send the number of fileds */
net_store_length(small_buff, (uint) list_length(fields)); net_store_length(small_buff, (uint) list_length(fields));
...@@ -173,7 +173,7 @@ int send_fields(struct st_net *net, LIST *fields) ...@@ -173,7 +173,7 @@ int send_fields(struct st_net *net, LIST *fields)
while (tmp) while (tmp)
{ {
position= 0; position= 0;
field= (NAME_WITH_LENGTH *) tmp->data; field= (LEX_STRING *) tmp->data;
store_to_protocol_packet(&send_buff, store_to_protocol_packet(&send_buff,
(char*) "", &position); /* catalog name */ (char*) "", &position); /* catalog name */
...@@ -184,9 +184,9 @@ int send_fields(struct st_net *net, LIST *fields) ...@@ -184,9 +184,9 @@ int send_fields(struct st_net *net, LIST *fields)
store_to_protocol_packet(&send_buff, store_to_protocol_packet(&send_buff,
(char*) "", &position); /* table name alias */ (char*) "", &position); /* table name alias */
store_to_protocol_packet(&send_buff, store_to_protocol_packet(&send_buff,
field->name, &position); /* column name */ field->str, &position); /* column name */
store_to_protocol_packet(&send_buff, store_to_protocol_packet(&send_buff,
field->name, &position); /* column name alias */ field->str, &position); /* column name alias */
send_buff.reserve(position, 12); send_buff.reserve(position, 12);
if (send_buff.is_error()) if (send_buff.is_error())
goto err; goto err;
......
...@@ -20,11 +20,6 @@ ...@@ -20,11 +20,6 @@
#include <my_list.h> #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 */ /* default field length to be used in various field-realted functions */
enum { DEFAULT_FIELD_LENGTH= 20 }; enum { DEFAULT_FIELD_LENGTH= 20 };
......
...@@ -20,11 +20,12 @@ ...@@ -20,11 +20,12 @@
#include "thread_registry.h" #include "thread_registry.h"
#include "log.h" #include <my_global.h>
#include <thr_alarm.h>
#include <assert.h>
#include <signal.h> #include <signal.h>
#include <thr_alarm.h>
#include "log.h"
#ifndef __WIN__ #ifndef __WIN__
...@@ -52,7 +53,7 @@ Thread_info::Thread_info(pthread_t thread_id_arg) : ...@@ -52,7 +53,7 @@ Thread_info::Thread_info(pthread_t thread_id_arg) :
*/ */
Thread_registry::Thread_registry() : Thread_registry::Thread_registry() :
shutdown_in_progress(false) shutdown_in_progress(FALSE)
,sigwait_thread_pid(pthread_self()) ,sigwait_thread_pid(pthread_self())
{ {
pthread_mutex_init(&LOCK_thread_registry, 0); pthread_mutex_init(&LOCK_thread_registry, 0);
...@@ -186,7 +187,7 @@ void Thread_registry::deliver_shutdown() ...@@ -186,7 +187,7 @@ void Thread_registry::deliver_shutdown()
set_timespec(shutdown_time, 1); set_timespec(shutdown_time, 1);
pthread_mutex_lock(&LOCK_thread_registry); pthread_mutex_lock(&LOCK_thread_registry);
shutdown_in_progress= true; shutdown_in_progress= TRUE;
#ifndef __WIN__ #ifndef __WIN__
/* to stop reading from the network we need to flush alarm queue */ /* to stop reading from the network we need to flush alarm queue */
......
#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;
}
#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
...@@ -19,32 +19,31 @@ ...@@ -19,32 +19,31 @@
#endif #endif
#include "user_map.h" #include "user_map.h"
#include "exit_codes.h"
#include <mysql_com.h>
#include <m_string.h>
#include "log.h" #include "log.h"
struct User User::User(const LEX_STRING *user_name_arg, const char *password)
{ {
char user[USERNAME_LENGTH + 1]; user_length= strmake(user, user_name_arg->str, USERNAME_LENGTH + 1) - user;
uint8 user_length;
uint8 salt[SCRAMBLE_LENGTH];
int init(const char *line);
};
set_password(password);
}
int User::init(const char *line) int User::init(const char *line)
{ {
const char *name_begin, *name_end, *password; const char *name_begin, *name_end, *password;
int line_ending_len= 1; int password_length;
if (line[0] == '\'' || line[0] == '"') if (line[0] == '\'' || line[0] == '"')
{ {
name_begin= line + 1; name_begin= line + 1;
name_end= strchr(name_begin, line[0]); name_end= strchr(name_begin, line[0]);
if (name_end == 0 || name_end[1] != ':') 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; password= name_end + 2;
} }
else else
...@@ -52,33 +51,47 @@ int User::init(const char *line) ...@@ -52,33 +51,47 @@ int User::init(const char *line)
name_begin= line; name_begin= line;
name_end= strchr(name_begin, ':'); name_end= strchr(name_begin, ':');
if (name_end == 0) 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; password= name_end + 1;
} }
user_length= name_end - name_begin; user_length= name_end - name_begin;
if (user_length > USERNAME_LENGTH) if (user_length > USERNAME_LENGTH)
goto err; {
log_info("Error: user name is too long (%d). Max length: %d. "
/* "User line: '%s'.",
assume that newline characater is present (int) user_length,
we support reading password files that end in \n or \r\n on (int) USERNAME_LENGTH,
either platform. (const char *) line);
*/ return 1;
if (password[strlen(password)-2] == '\r') }
line_ending_len= 2;
if (strlen(password) != (uint) (SCRAMBLED_PASSWORD_CHAR_LENGTH + password_length= strlen(password);
line_ending_len)) if (password_length > SCRAMBLED_PASSWORD_CHAR_LENGTH)
goto err; {
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); memcpy(user, name_begin, user_length);
user[user_length]= 0; user[user_length]= 0;
memcpy(scrambled_password, password, password_length);
scrambled_password[password_length]= 0;
get_salt_from_password(salt, password); get_salt_from_password(salt, password);
log_info("loaded user %s", user);
log_info("loaded user '%s'.", user);
return 0; 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) ...@@ -101,30 +114,70 @@ static void delete_user(void *u)
C_MODE_END 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() int User_map::init()
{ {
enum { START_HASH_SIZE= 16 }; enum { START_HASH_SIZE= 16 };
if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
get_user_key, delete_user, 0)) get_user_key, delete_user, 0))
return 1; return 1;
initialized= TRUE;
return 0; return 0;
} }
User_map::User_map()
:initialized(FALSE)
{
}
User_map::~User_map() 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 Load password database.
construction.
In case of failure, puts error message to the log file and returns 1 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; FILE *file;
char line[USERNAME_LENGTH + SCRAMBLED_PASSWORD_CHAR_LENGTH + char line[USERNAME_LENGTH + SCRAMBLED_PASSWORD_CHAR_LENGTH +
2 + /* for possible quotes */ 2 + /* for possible quotes */
...@@ -134,33 +187,172 @@ int User_map::load(const char *password_file_name) ...@@ -134,33 +187,172 @@ int User_map::load(const char *password_file_name)
User *user; User *user;
int rc= 1; 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) 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 */ if (err_msg)
log_info("[WARNING] can't open password file %s: errno=%d, %s", password_file_name, {
errno, strerror(errno)); snprintf(err_msg_buf, ERR_MSG_BUF_SIZE,
return 0; "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)) 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 */ /* skip comments and empty lines */
if (line[0] == '#' || line[0] == '\n' && if (!user_line[0] || user_line[0] == '#')
(line[1] == '\0' || line[1] == '\r'))
continue; continue;
if ((user= new User) == 0) if ((user= new User) == 0)
goto done; {
if (user->init(line) || my_hash_insert(&hash, (byte *) user)) my_fclose(file, MYF(0));
goto err_init_user;
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; log_info("the password database loaded successfully.");
goto done;
err_init_user:
delete user;
done:
my_fclose(file, MYF(0)); 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: ...@@ -172,13 +364,33 @@ done:
2 - user not found 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 *scrambled_password,
const char *scramble) const const char *scramble) const
{ {
const User *user= (const User *) hash_search((HASH *) &hash, const User *user= find_user(user_name);
(byte *) user_name, length); return user ? check_scramble(scrambled_password, scramble, user->salt) : 2;
if (user) }
return check_scramble(scrambled_password, scramble, user->salt);
return 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;
} }
...@@ -18,14 +18,35 @@ ...@@ -18,14 +18,35 @@
#include <my_global.h> #include <my_global.h>
#include <my_sys.h> #include <my_sys.h>
#include <mysql_com.h>
#include <m_string.h>
#include <hash.h> #include <hash.h>
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface #pragma interface
#endif #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 User_map -- all users and passwords
*/ */
...@@ -33,15 +54,51 @@ ...@@ -33,15 +54,51 @@
class User_map class User_map
{ {
public: 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(); ~User_map();
int init(); int init();
int load(const char *password_file_name); int load(const char *password_file_name, const char **err_msg);
int authenticate(const char *user_name, uint length, int save(const char *password_file_name, const char **err_msg);
int authenticate(const LEX_STRING *user_name,
const char *scrambled_password, const char *scrambled_password,
const char *scramble) const; 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: private:
HASH hash; HASH hash;
bool initialized;
friend class Iterator;
}; };
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H
...@@ -409,15 +409,13 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ...@@ -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; ulong old_sql_mode= thd->variables.sql_mode;
ha_rows old_select_limit= thd->variables.select_limit; ha_rows old_select_limit= thd->variables.select_limit;
sp_rcontext *old_spcont= thd->spcont; sp_rcontext *old_spcont= thd->spcont;
char definer_user_name_holder[USERNAME_LENGTH + 1]; char definer_user_name_holder[USERNAME_LENGTH + 1];
LEX_STRING_WITH_INIT definer_user_name(definer_user_name_holder, LEX_STRING definer_user_name= { definer_user_name_holder, USERNAME_LENGTH };
USERNAME_LENGTH);
char definer_host_name_holder[HOSTNAME_LENGTH + 1]; char definer_host_name_holder[HOSTNAME_LENGTH + 1];
LEX_STRING_WITH_INIT definer_host_name(definer_host_name_holder, LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH };
HOSTNAME_LENGTH);
int ret; int ret;
thd->variables.sql_mode= sql_mode; thd->variables.sql_mode= sql_mode;
......
...@@ -1850,10 +1850,10 @@ void ...@@ -1850,10 +1850,10 @@ void
sp_head::set_definer(const char *definer, uint definerlen) sp_head::set_definer(const char *definer, uint definerlen)
{ {
char user_name_holder[USERNAME_LENGTH + 1]; 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]; 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, parse_user(definer, definerlen, user_name.str, &user_name.length,
host_name.str, &host_name.length); host_name.str, &host_name.length);
......
...@@ -34,8 +34,11 @@ static Geometry::Class_info **ci_collection_end= ...@@ -34,8 +34,11 @@ static Geometry::Class_info **ci_collection_end=
Geometry::Class_info::Class_info(const char *name, int type_id, Geometry::Class_info::Class_info(const char *name, int type_id,
void(*create_func)(void *)): 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; ci_collection[type_id]= this;
} }
......
...@@ -200,7 +200,7 @@ public: ...@@ -200,7 +200,7 @@ public:
class Class_info class Class_info
{ {
public: public:
LEX_STRING_WITH_INIT m_name; LEX_STRING m_name;
int m_type_id; int m_type_id;
void (*m_create_func)(void *); void (*m_create_func)(void *);
Class_info(const char *name, int type_id, void(*create_func)(void *)); Class_info(const char *name, int type_id, void(*create_func)(void *));
......
...@@ -24,8 +24,6 @@ ...@@ -24,8 +24,6 @@
#define NOT_FIXED_DEC 31 #define NOT_FIXED_DEC 31
#endif #endif
#define STRING_WITH_LEN(X) ((const char*) X), ((uint) (sizeof(X) - 1))
class String; class String;
int sortcmp(const String *a,const String *b, CHARSET_INFO *cs); int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
String *copy_if_not_alloced(String *a,String *b,uint32 arg_length); String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
......
...@@ -1412,8 +1412,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, ...@@ -1412,8 +1412,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
} }
if (table.triggers) if (table.triggers)
{ {
LEX_STRING_WITH_INIT old_table_name(old_table, strlen(old_table)); LEX_STRING old_table_name= { (char *) STRING_WITH_LEN(old_table) };
LEX_STRING_WITH_INIT new_table_name(new_table, strlen(new_table)); LEX_STRING new_table_name= { (char *) STRING_WITH_LEN(new_table) };
/* /*
Since triggers should be in the same schema as their subject tables Since triggers should be in the same schema as their subject tables
moving table with them between two schemas raises too many questions. moving table with them between two schemas raises too many questions.
......
...@@ -20,22 +20,6 @@ ...@@ -20,22 +20,6 @@
struct st_table; struct st_table;
class Field; 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 { typedef struct st_date_time_format {
uchar positions[8]; uchar positions[8];
char time_separator; /* Separator between hour and minute */ char time_separator; /* Separator between hour and minute */
......
...@@ -265,7 +265,10 @@ case "$mode" in ...@@ -265,7 +265,10 @@ case "$mode" in
then then
# Give extra arguments to mysqld with the my.cnf file. This script may # Give extra arguments to mysqld with the my.cnf file. This script may
# be overwritten at next upgrade. # 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 wait_for_pid created
# Make lock for RedHat / SuSE # Make lock for RedHat / SuSE
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment