From a5435ea78ab3d62223fd94ebd7c730f8ded30f1b Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 23 Oct 2004 11:32:52 +0400 Subject: [PATCH 01/11] Intermediate commit - just to make new files visible to bk in the new tree server-tools/instance-manager/Makefile.am: Fixed IM linking to avoid using both mysys and libmysql as the define the same symbols and therefore conflict server-tools/instance-manager/listener.cc: Added ability to listen network ports server-tools/instance-manager/listener.h: Various additions to the Listener_thread_args server-tools/instance-manager/log.cc: merge server-tools/instance-manager/log.h: merge server-tools/instance-manager/manager.cc: Fixes and additions to enable guardian functionality server-tools/instance-manager/manager.h: Changed manager() signature server-tools/instance-manager/mysqlmanager.cc: Various fixes server-tools/instance-manager/options.cc: Added handling of default values for new options in the Options struct. (such as default_user, default_password, monitoring_interval e.t.c) server-tools/instance-manager/options.h: Added new options to the Options struct sql/net_serv.cc: Added MYSQL_INSTANCE_MANAGER defines to enable alarm handling in the IM server-tools/instance-manager/buffer.cc: Simple implementation of variable-length buffer server-tools/instance-manager/command.cc: Abstract command. All commands are derived from Command class server-tools/instance-manager/commands.h: Interfaces for all commands we have server-tools/instance-manager/factory.cc: Commands factory. This class hides command instantiation. The idea is to handle various protocols this way. (different commands for different protocols server-tools/instance-manager/guardian.cc: Guardian thread implementation (monitor and restart instances in case of a failure server-tools/instance-manager/guardian.h: Guardian_thread and Guardian_thread_args class interface. The Guardian_thread is responsible for monitoring and restarting instances server-tools/instance-manager/instance.cc: Instance class contains methods and data to manage a single instance server-tools/instance-manager/instance.h: This file contains class an instance class interface. The class is responsible for starting/stopping an instance server-tools/instance-manager/instance_map.cc: The instance repository. This class is also responsible for initialization of Instance class objects. server-tools/instance-manager/instance_options.cc: The Instance_options class contains all methods to get and handle options of an instance server-tools/instance-manager/mysql_connection.cc: The class responsible for handling MySQL client/server protocol connections server-tools/instance-manager/mysql_manager_error.h: The list of Instance Manger-specific errors server-tools/instance-manager/parse.cc: Simple query parser server-tools/instance-manager/parse.h: Parser interface server-tools/instance-manager/protocol.cc: Here implemented functions used to handle mysql client/server protocol server-tools/instance-manager/protocol.h: Interface for MySQL client/server protocol server-tools/instance-manager/thread_registry.cc: Thread registry stores information about every thread. It's main function is to provide graceful shutdown for all threads. server-tools/instance-manager/user_map.h: User map contains hash with user names and passwords --- server-tools/instance-manager/Makefile.am | 94 +++- server-tools/instance-manager/buffer.cc | 91 ++++ server-tools/instance-manager/buffer.h | 57 +++ server-tools/instance-manager/command.cc | 43 ++ server-tools/instance-manager/command.h | 49 ++ server-tools/instance-manager/commands.cc | 183 +++++++ server-tools/instance-manager/commands.h | 129 +++++ server-tools/instance-manager/factory.cc | 60 +++ server-tools/instance-manager/factory.h | 45 ++ server-tools/instance-manager/guardian.cc | 180 +++++++ server-tools/instance-manager/guardian.h | 80 ++++ server-tools/instance-manager/instance.cc | 152 ++++++ server-tools/instance-manager/instance.h | 60 +++ server-tools/instance-manager/instance_map.cc | 450 ++++++++++++++++++ server-tools/instance-manager/instance_map.h | 76 +++ .../instance-manager/instance_options.cc | 212 +++++++++ .../instance-manager/instance_options.h | 76 +++ server-tools/instance-manager/listener.cc | 354 +++++++++++--- server-tools/instance-manager/listener.h | 29 +- server-tools/instance-manager/log.cc | 6 +- server-tools/instance-manager/log.h | 9 +- server-tools/instance-manager/manager.cc | 143 +++++- server-tools/instance-manager/manager.h | 6 +- server-tools/instance-manager/messages.cc | 73 +++ server-tools/instance-manager/messages.h | 26 + .../instance-manager/mysql_connection.cc | 388 +++++++++++++++ .../instance-manager/mysql_connection.h | 60 +++ .../instance-manager/mysql_manager_error.h | 27 ++ server-tools/instance-manager/mysqlmanager.cc | 67 +-- server-tools/instance-manager/options.cc | 88 +++- server-tools/instance-manager/options.h | 12 +- server-tools/instance-manager/parse.cc | 200 ++++++++ server-tools/instance-manager/parse.h | 23 + server-tools/instance-manager/priv.cc | 34 ++ server-tools/instance-manager/priv.h | 60 +++ server-tools/instance-manager/protocol.cc | 171 +++++++ server-tools/instance-manager/protocol.h | 43 ++ .../instance-manager/thread_registry.cc | 206 ++++++++ .../instance-manager/thread_registry.h | 116 +++++ server-tools/instance-manager/user_map.cc | 172 +++++++ server-tools/instance-manager/user_map.h | 45 ++ sql/net_serv.cc | 14 +- 42 files changed, 4261 insertions(+), 148 deletions(-) create mode 100644 server-tools/instance-manager/buffer.cc create mode 100644 server-tools/instance-manager/buffer.h create mode 100644 server-tools/instance-manager/command.cc create mode 100644 server-tools/instance-manager/command.h create mode 100644 server-tools/instance-manager/commands.cc create mode 100644 server-tools/instance-manager/commands.h create mode 100644 server-tools/instance-manager/factory.cc create mode 100644 server-tools/instance-manager/factory.h create mode 100644 server-tools/instance-manager/guardian.cc create mode 100644 server-tools/instance-manager/guardian.h create mode 100644 server-tools/instance-manager/instance.cc create mode 100644 server-tools/instance-manager/instance.h create mode 100644 server-tools/instance-manager/instance_map.cc create mode 100644 server-tools/instance-manager/instance_map.h create mode 100644 server-tools/instance-manager/instance_options.cc create mode 100644 server-tools/instance-manager/instance_options.h create mode 100644 server-tools/instance-manager/messages.cc create mode 100644 server-tools/instance-manager/messages.h create mode 100644 server-tools/instance-manager/mysql_connection.cc create mode 100644 server-tools/instance-manager/mysql_connection.h create mode 100644 server-tools/instance-manager/mysql_manager_error.h create mode 100644 server-tools/instance-manager/parse.cc create mode 100644 server-tools/instance-manager/parse.h create mode 100644 server-tools/instance-manager/priv.cc create mode 100644 server-tools/instance-manager/priv.h create mode 100644 server-tools/instance-manager/protocol.cc create mode 100644 server-tools/instance-manager/protocol.h create mode 100644 server-tools/instance-manager/thread_registry.cc create mode 100644 server-tools/instance-manager/thread_registry.h create mode 100644 server-tools/instance-manager/user_map.cc create mode 100644 server-tools/instance-manager/user_map.h diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 731c8503831..522ca6166cc 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -1,30 +1,98 @@ +# Copyright (C) 2004 MySQL AB +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; 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 + INCLUDES= -I$(top_srcdir)/include +DEFS= -DMYSQL_INSTANCE_MANAGER + # As all autoconf variables depend from ${prefix} and being resolved only when -# make is run, we can't put these defines to a header file (e.g. to +# make is run, we can not put these defines to a header file (e.g. to # default_options.h, generated from default_options.h.in) # See automake/autoconf docs for details -noinst_LIBRARIES= liboptions.a +noinst_LIBRARIES= liboptions.a libnet.a + liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_PID_FILE_NAME="$(localstatedir)/mysqlmanager.pid" \ -DDEFAULT_LOG_FILE_NAME="$(localstatedir)/mysqlmanager.log" \ - -DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" + -DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" \ + -DDEFAULT_PASSWORD_FILE_NAME="$(sysconfdir)/mysqlmanager.passwd" \ + -DDEFAULT_MYSQLD_PATH="$(bindir)/mysqld$(EXEEXT)" \ + -DDEFAULT_USER="root" \ + -DDEFAULT_PASSWORD="" \ + -DDEFAULT_MONITORING_INTERVAL="5" \ + -DDEFAULT_PORT="3406" \ + -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ -liboptions_a_SOURCES= options.h options.cc +liboptions_a_SOURCES= options.h options.cc priv.h priv.cc -bin_PROGRAMS = mysqlmanager +# MySQL sometimes uses symlinks to reuse code +# All symlinked files are grouped in libnet.a -mysqlmanager_SOURCES= mysqlmanager.cc manager.h manager.cc log.h log.cc \ - listener.h listener.cc \ - thread_repository.h thread_repository.cc +nodist_libnet_a_SOURCES= password.c pack.c sql_state.c net_serv.cc +nodist_libnet_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER -mysqlmanager_LDADD= liboptions.a \ - $(top_builddir)/mysys/libmysys.a \ - $(top_builddir)/strings/libmystrings.a \ - $(top_builddir)/dbug/libdbug.a +CLEANFILES= net_serv.cc password.c pack.c sql_state.c -tags: +net_serv.cc: Makefile + rm -f $(srcdir)/net_serv.cc + @LN_CP_F@ $(top_srcdir)/sql/net_serv.cc $(srcdir)/net_serv.cc + +password.c: Makefile + rm -f $(srcdir)/password.c + @LN_CP_F@ $(top_srcdir)/sql/password.c $(srcdir)/password.c + +pack.c: Makefile + rm -f $(srcdir)/pack.c + @LN_CP_F@ $(top_srcdir)/sql-common/pack.c $(srcdir)/pack.c + +sql_state.c: Makefile + rm -f $(srcdir)/sql_state.c + @LN_CP_F@ $(top_srcdir)/sql/sql_state.c $(srcdir)/sql_state.c + +bin_PROGRAMS= mysqlmanager + +mysqlmanager_SOURCES= mysqlmanager.cc manager.h manager.cc log.h log.cc \ + thread_registry.h thread_registry.cc \ + listener.h listener.cc \ + mysql_connection.h mysql_connection.cc \ + protocol.h protocol.cc \ + user_map.h user_map.cc \ + messages.h messages.cc \ + $(top_srcdir)/sql/sql_string.cc \ + command.h command.cc \ + commands.h commands.cc \ + factory.h factory.cc \ + instance.h instance.cc \ + instance_map.h instance_map.cc\ + instance_options.h instance_options.cc \ + buffer.h buffer.cc parse.cc parse.h \ + guardian.cc guardian.h common_structures.h \ + mysql_manager_error.h + +mysqlmanager_LDADD= liboptions.a \ + libnet.a \ + $(top_builddir)/vio/libvio.a \ + $(top_builddir)/mysys/libmysys.a \ + $(top_builddir)/strings/libmystrings.a \ + $(top_builddir)/dbug/libdbug.a \ + $(top_builddir)/libmysql/libmysqlclient.la + + +tags: ctags -R *.h *.cc # Don't update the files from bitkeeper diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc new file mode 100644 index 00000000000..39e255300cf --- /dev/null +++ b/server-tools/instance-manager/buffer.cc @@ -0,0 +1,91 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "buffer.h" +#include + + +/* + Puts the given string to the buffer. + + SYNOPSYS + put_to_buffer() + start_pos start position in the buffer + string string to be put in the buffer + len_arg the length of the string. This way we can avoid some + strlens. + + DESCRIPTION + + The method puts a string into the buffer, starting from start_pos . + In the case when the buffer is too small it reallocs the buffer. The + total size of the buffer is restricted with 16. + + RETURN + 0 - ok + 1 - The buffer came to 16Mb barrier +*/ + +int Buffer::put_to_buffer(char *start_pos, const char *string, uint len_arg) +{ + if (check_and_add(start_pos - buffer, len_arg)) + return 1; + + strnmov(start_pos, string, len_arg); + return 0; +} + + +/* + Checks whether the current buffer size is ok to put a string of the length + "len_arg" starting from "position" and reallocs it if no. + + SYNOPSYS + check_and_add() + position the number starting byte on the buffer to store a buffer + len_arg the length of the string. + + DESCRIPTION + + The method checks whether it is possible to pus a string of teh "len_arg" + length into the buffer, starting from "position" byte. In the case when the + buffer is too small it reallocs the buffer. The total size of the buffer is + restricted with 16 Mb. + + RETURN + 0 - ok + 1 - The buffer came to 16Mb barrier +*/ + +int Buffer::check_and_add(uint position, uint len_arg) +{ + if (position + len_arg >= MAX_BUFFER_SIZE) + return 1; + + if (position + len_arg>= buffer_size) + { + buffer= (char *) realloc(buffer, + min(MAX_BUFFER_SIZE, + max((uint) buffer_size*1.5, + position + len_arg))); + buffer_size= (uint) buffer_size*1.5; + } + return 0; +} diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h new file mode 100644 index 00000000000..8781abebd71 --- /dev/null +++ b/server-tools/instance-manager/buffer.h @@ -0,0 +1,57 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#include + +#ifdef __GNUC__ +#pragma interface +#endif + +/* + This class is a simple implementation of the buffer of varying size. + It is used to store MySQL client-server protocol packets. This is why + the maximum buffer size if 16Mb. (See internals manual section + 7. MySQL Client/Server Protocol) +*/ + +class Buffer +{ +private: + enum { BUFFER_INITIAL_SIZE= 4096 }; + /* maximum buffer size is 16Mb */ + enum { MAX_BUFFER_SIZE= 16777216 }; + uint buffer_size; +public: + Buffer() + { + buffer=(char *) malloc(BUFFER_INITIAL_SIZE); + buffer_size= BUFFER_INITIAL_SIZE; + } + + ~Buffer() + { + free(buffer); + } + +public: + char *buffer; + int put_to_buffer(char *start_pos, const char *string, uint len_arg); + int check_and_add(uint position, uint len_arg); +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H */ diff --git a/server-tools/instance-manager/command.cc b/server-tools/instance-manager/command.cc new file mode 100644 index 00000000000..71415a038f0 --- /dev/null +++ b/server-tools/instance-manager/command.cc @@ -0,0 +1,43 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "command.h" + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "protocol.h" +#include "instance_map.h" + +Command::Command(Command_factory *factory_arg) + :factory(factory_arg) +{} + +Command::~Command() +{} + +#ifdef __GNUC__ +FIX_GCC_LINKING_PROBLEM +#endif diff --git a/server-tools/instance-manager/command.h b/server-tools/instance-manager/command.h new file mode 100644 index 00000000000..25b418ca8fe --- /dev/null +++ b/server-tools/instance-manager/command.h @@ -0,0 +1,49 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include + +/* Class responsible for allocation and deallocation of im classes. */ + +class Command_factory; + +/* + Command - entry point for any command. + GangOf4: 'Command' design pattern +*/ + +class Command +{ +public: + Command(Command_factory *factory_arg= 0); + virtual ~Command(); + + /* method of executing: */ + virtual int execute(struct st_net *net, ulong connection_id) = 0; + +protected: + Command_factory *factory; +}; + +#define CONST_STR(a) String(a,sizeof(a),&my_charset_latin1) + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H */ diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc new file mode 100644 index 00000000000..e990f04216d --- /dev/null +++ b/server-tools/instance-manager/commands.cc @@ -0,0 +1,183 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#include "command.h" +#include "factory.h" +#include "commands.h" +#include "instance.h" +#include "instance_map.h" +#include "messages.h" + + +/* implementation for Show_instances: */ + +int Show_instances::execute(struct st_net *net, ulong connection_id) +{ + if (factory->instance_map.show_instances(net)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +/* implementation for Flush_instances: */ + +int Flush_instances::execute(struct st_net *net, ulong connection_id) +{ + if (factory->instance_map.flush_instances()) + return ER_OUT_OF_RESOURCES; + + net_send_ok(net, connection_id); + return 0; +} + + +/* implementation for Show_instance_status: */ + +Show_instance_status::Show_instance_status(Command_factory *factory, + const char *name, uint len) + :Command(factory) +{ + Instance *instance; + + /* we make a search here, since we don't want t store the name */ + if (instance= (factory->instance_map).find(name, len)) + { + instance_name= instance->options.instance_name; + } + else instance_name= NULL; +} + + +int Show_instance_status::execute(struct st_net *net, ulong connection_id) +{ + if (instance_name != NULL) + { + if (factory->instance_map.show_instance_status(net, instance_name)) + return ER_OUT_OF_RESOURCES; + return 0; + } + else + { + return ER_BAD_INSTANCE_NAME; + } +} + + +/* Implementation for Show_instance_options */ + +Show_instance_options::Show_instance_options(Command_factory *factory, + const char *name, uint len): + Command(factory) +{ + Instance *instance; + + /* we make a search here, since we don't want t store the name */ + if (instance= (factory->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) +{ + if (instance_name != NULL) + { + if (factory->instance_map.show_instance_options(net, instance_name)) + return ER_OUT_OF_RESOURCES; + return 0; + } + else + { + return ER_BAD_INSTANCE_NAME; + } +} + + +/* Implementation for Start_instance */ + +Start_instance::Start_instance(Command_factory *factory, + const char *name, uint len) + :Command(factory) +{ + /* we make a search here, since we don't want t store the name */ + if (instance= (factory->instance_map).find(name, len)) + instance_name= instance->options.instance_name; +} + + +int Start_instance::execute(struct st_net *net, ulong connection_id) +{ + uint err_code; + if (instance == 0) + { + return ER_BAD_INSTANCE_NAME; /* haven't found an instance */ + } + else + { + if (err_code= instance->start()) + return err_code; + + if (instance->options.is_guarded != NULL) + factory->instance_map.guardian->guard(instance->options.instance_name, + instance->options.instance_name_len); + + net_send_ok(net, connection_id); + return 0; + } +} + + +/* Implementation for Stop_instance: */ + +Stop_instance::Stop_instance(Command_factory *factory, + const char *name, uint len) + :Command(factory) +{ + /* we make a search here, since we don't want t store the name */ + if (instance= (factory->instance_map).find(name, len)) + instance_name= instance->options.instance_name; +} + + +int Stop_instance::execute(struct st_net *net, ulong connection_id) +{ + uint err_code; + + if (instance == 0) + { + return ER_BAD_INSTANCE_NAME; /* haven't found an instance */ + } + else + { + if (instance->options.is_guarded != NULL) + factory->instance_map.guardian-> + stop_guard(instance_name, instance->options.instance_name_len); + if (err_code= instance->stop()) + return err_code; + + net_send_ok(net, connection_id); + return 0; + } +} + + +int Syntax_error::execute(struct st_net *net, ulong connection_id) +{ + return ER_SYNTAX_ERROR; +} diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h new file mode 100644 index 00000000000..bab61f04b17 --- /dev/null +++ b/server-tools/instance-manager/commands.h @@ -0,0 +1,129 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#include "instance.h" +#include "my_global.h" + +/* + Print all instances of this instance manager. + Grammar: SHOW ISTANCES +*/ + +class Show_instances : public Command +{ +public: + Show_instances(Command_factory *factory): Command(factory) + {} + + int execute(struct st_net *net, ulong connection_id); +}; + + +/* + Reread configuration file and refresh instance map. + Grammar: FLUSH INSTANCES +*/ + +class Flush_instances : public Command +{ +public: + Flush_instances(Command_factory *factory): Command(factory) + {} + + int execute(struct st_net *net, ulong connection_id); +}; + + +/* + Print status of an instance. + Grammar: SHOW ISTANCE STATUS +*/ + +class Show_instance_status : public Command +{ +public: + + Show_instance_status(Command_factory *factory, const char *name, uint len); + + int execute(struct st_net *net, ulong connection_id); + const char *instance_name; +}; + + +/* + Print options if chosen instance. + Grammar: SHOW INSTANCE OPTIONS +*/ + +class Show_instance_options : public Command +{ +public: + + Show_instance_options(Command_factory *factory, const char *name, uint len); + + int execute(struct st_net *net, ulong connection_id); + const char *instance_name; +}; + + +/* + Start an instance. + Grammar: START INSTANCE +*/ + +class Start_instance : public Command +{ +public: + Start_instance(Command_factory *factory, const char *name, uint len); + + Instance *instance; + int execute(struct st_net *net, ulong connection_id); + const char *instance_name; +}; + + +/* + Stop an instance. + Grammar: STOP INSTANCE +*/ + +class Stop_instance : public Command +{ +public: + Stop_instance(Command_factory *factory, const char *name, uint len); + + Instance *instance; + int execute(struct st_net *net, ulong connection_id); + const char *instance_name; +}; + + +/* + Syntax error command. +*/ + +class Syntax_error : public Command +{ +public: + Syntax_error() + {} + + int execute(struct st_net *net, ulong connection_id); +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H */ diff --git a/server-tools/instance-manager/factory.cc b/server-tools/instance-manager/factory.cc new file mode 100644 index 00000000000..691aca0c7ea --- /dev/null +++ b/server-tools/instance-manager/factory.cc @@ -0,0 +1,60 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#include "factory.h" +#include "my_global.h" + +#include +#include + + +Show_instances *Command_factory::new_Show_instances() +{ + return new Show_instances(this); +} + +Flush_instances *Command_factory::new_Flush_instances() +{ + return new Flush_instances(this); +} + +Show_instance_status *Command_factory:: + new_Show_instance_status(const char *name, uint len) +{ + return new Show_instance_status(this, name, len); +} + +Show_instance_options *Command_factory:: + new_Show_instance_options(const char *name, uint len) +{ + return new Show_instance_options(this, name, len); +} + +Start_instance *Command_factory:: + new_Start_instance(const char *name, uint len) +{ + return new Start_instance(this, name, len); +} + +Stop_instance *Command_factory::new_Stop_instance(const char *name, uint len) +{ + return new Stop_instance(this, name, len); +} + +Syntax_error *Command_factory::new_Syntax_error() +{ + return new Syntax_error(); +} diff --git a/server-tools/instance-manager/factory.h b/server-tools/instance-manager/factory.h new file mode 100644 index 00000000000..0a1b955d156 --- /dev/null +++ b/server-tools/instance-manager/factory.h @@ -0,0 +1,45 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_FACTORY_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_FACTORY_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#include "command.h" +#include "commands.h" +#include "instance_map.h" + +/* + This class could be used to handle various protocols. We could pass to + the parser various derived classes. I.e Mylsq_command_factory, + Http_command_factory e.t.c. Also see comment in the instance_map.cc +*/ + +class Command_factory +{ +public: + Command_factory(Instance_map &instance_map): instance_map(instance_map) + {} + + Show_instances *new_Show_instances (); + Show_instance_status *new_Show_instance_status (const char *name, uint len); + Show_instance_options *new_Show_instance_options (const char *name, uint len); + Start_instance *new_Start_instance (const char *name, uint len); + Stop_instance *new_Stop_instance (const char *name, uint len); + Flush_instances *new_Flush_instances (); + Syntax_error *new_Syntax_error (); + + Instance_map &instance_map; +}; +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_FACTORY_H */ diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc new file mode 100644 index 00000000000..ebc59d67906 --- /dev/null +++ b/server-tools/instance-manager/guardian.cc @@ -0,0 +1,180 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "guardian.h" +#include "instance_map.h" +#include + +C_MODE_START + +pthread_handler_decl(guardian, arg) +{ + Guardian_thread *guardian_thread= (Guardian_thread *) arg; + guardian_thread->run(); + return 0; +} + +C_MODE_END + + +Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, + Instance_map *instance_map_arg, + uint monitoring_interval_arg) : + Guardian_thread_args(thread_registry_arg, instance_map_arg, + monitoring_interval_arg), + thread_info(pthread_self()) +{ + pthread_mutex_init(&LOCK_guardian, 0); + thread_registry.register_thread(&thread_info); + init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); + guarded_instances= NULL; +} + + +Guardian_thread::~Guardian_thread() +{ + /* delay guardian destruction to the moment when no one needs it */ + pthread_mutex_lock(&LOCK_guardian); + free_root(&alloc, MYF(0)); + thread_registry.unregister_thread(&thread_info); + pthread_mutex_unlock(&LOCK_guardian); + pthread_mutex_destroy(&LOCK_guardian); +} + + +/* + Run guardian thread + + SYNOPSYS + run() + + DESCRIPTION + + Check for all guarded instances and restart them if needed. If everything + is fine go and sleep for some time. + + RETURN + The function return no value +*/ + +void Guardian_thread::run() +{ + Instance *instance; + LIST *loop; + int i=0; + + my_thread_init(); + + while (!thread_registry.is_shutdown()) + { + pthread_mutex_lock(&LOCK_guardian); + loop= guarded_instances; + while (loop != NULL) + { + instance= (Instance *) loop->data; + if (instance != NULL) + { + if (!instance->is_running()) + instance->start(); + } + loop= loop->next; + } + pthread_mutex_unlock(&LOCK_guardian); + sleep(monitoring_interval); + } + + my_thread_end(); +} + + +/* + Start instance guarding + + SYNOPSYS + guard() + instance_name the name of the instance to be guarded + name_len the length of the name + + DESCRIPTION + + The instance is added to the list of guarded instances. + + RETURN + 0 - ok + 1 - error occured +*/ + +int Guardian_thread::guard(const char *instance_name, uint name_len) +{ + LIST *lst; + Instance *instance; + + lst= (LIST *) alloc_root(&alloc, sizeof(LIST)); + if (lst == NULL) return 1; + instance= instance_map->find(instance_name, name_len); + /* we store the pointers to instances from the instance_map's MEM_ROOT */ + lst->data= (void *) instance; + + pthread_mutex_lock(&LOCK_guardian); + guarded_instances= list_add(guarded_instances, lst); + pthread_mutex_unlock(&LOCK_guardian); + + return 0; +} + + +/* + TODO: perhaps it would make sense to create a pool of the LIST elements + elements and give them upon request. Now we are loosing a bit of memory when + guarded instance was stopped and then restarted (since we cannot free just + a piece of the MEM_ROOT). +*/ + +int Guardian_thread::stop_guard(const char *instance_name, uint name_len) +{ + LIST *lst; + Instance *instance; + + instance= instance_map->find(instance_name, name_len); + + lst= guarded_instances; + if (lst == NULL) return 1; + + pthread_mutex_lock(&LOCK_guardian); + while (lst != NULL) + { + /* + We compare only pointers, as we always use pointers from the + instance_map's MEM_ROOT. + */ + if ((Instance *) lst->data == instance) + { + guarded_instances= list_delete(guarded_instances, lst); + pthread_mutex_unlock(&LOCK_guardian); + return 0; + } + else lst= lst->next; + } + pthread_mutex_unlock(&LOCK_guardian); + /* if there is nothing to delete it is also fine */ + return 0; +} + diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h new file mode 100644 index 00000000000..5d0cdcd7c92 --- /dev/null +++ b/server-tools/instance-manager/guardian.h @@ -0,0 +1,80 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#include +#include +#include + +#ifdef __GNUC__ +#pragma interface +#endif + +class Instance_map; + +#include "thread_registry.h" +#include "instance.h" + +C_MODE_START + +pthread_handler_decl(guardian, arg); + +C_MODE_END + + +struct Guardian_thread_args +{ + Thread_registry &thread_registry; + Instance_map *instance_map; + uint monitoring_interval; + + Guardian_thread_args(Thread_registry &thread_registry_arg, + Instance_map *instance_map_arg, + uint monitoring_interval_arg) : + thread_registry(thread_registry_arg), + instance_map(instance_map_arg), + monitoring_interval(monitoring_interval_arg) + {} +}; + + +/* + The guardian thread is responsible for monitoring and restarting of guarded + instances. +*/ + +class Guardian_thread: public Guardian_thread_args +{ +public: + Guardian_thread(Thread_registry &thread_registry_arg, + Instance_map *instance_map_arg, + uint monitoring_interval_arg); + ~Guardian_thread(); + void run(); + int init(); + int guard(const char *instance_name, uint name_len); + int stop_guard(const char *instance_name, uint name_len); + +private: + pthread_mutex_t LOCK_guardian; + Thread_info thread_info; + LIST *guarded_instances; + MEM_ROOT alloc; + enum { MEM_ROOT_BLOCK_SIZE= 512 }; +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */ diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc new file mode 100644 index 00000000000..32e77429572 --- /dev/null +++ b/server-tools/instance-manager/instance.cc @@ -0,0 +1,152 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "instance.h" +#include "mysql_manager_error.h" +#include +#include +#include + + +/* + The method starts an instance. + + SYNOPSYS + start() + + RETURN + 0 ok + ER_CANNOT_START_INSTANCE Cannot start instance + ER_INSTANCE_ALREADY_STARTED The instance on the specified port/socket + is already started +*/ + +int Instance::start() +{ + + if (!is_running()) + { + switch (fork()) { + case 0: + if (fork()) /* zombie protection */ + exit(0); /* parent goes bye-bye */ + else + { + execv(options.mysqld_path, options.argv); + exit(1); + } + case -1: + return ER_CANNOT_START_INSTANCE; + default: + return 0; + } + } + + /* the instance is started already */ + return ER_INSTANCE_ALREADY_STARTED; +} + +int Instance::cleanup() +{ + /* + We cannot close connection in destructor, as mysql_close needs alarm + services which are definitely unavailaible at the time of destructor + call. + */ + if (is_connected) + mysql_close(&mysql); + return 0; +} + +Instance::~Instance() +{ + pthread_mutex_destroy(&LOCK_instance); +} + +bool Instance::is_running() +{ + pthread_mutex_lock(&LOCK_instance); + if (!is_connected) + { + mysql_init(&mysql); + if (mysql_real_connect(&mysql, LOCAL_HOST, options.mysqld_user, + options.mysqld_password, + NullS, atoi(strchr(options.mysqld_port, '=') + 1), + strchr(options.mysqld_socket, '=') + 1, 0)) + { + is_connected= TRUE; + pthread_mutex_unlock(&LOCK_instance); + return TRUE; + } + mysql_close(&mysql); + pthread_mutex_unlock(&LOCK_instance); + return FALSE; + } + else if (!mysql_ping(&mysql)) + { + pthread_mutex_unlock(&LOCK_instance); + return TRUE; + } + pthread_mutex_unlock(&LOCK_instance); + return FALSE; +} + + +/* + Stop an instance. + + SYNOPSYS + stop() + + RETURN: + 0 ok + ER_INSTANCE_IS_NOT_STARTED Looks like the instance it is not started + ER_STOP_INSTANCE mysql_shutdown reported an error +*/ + +int Instance::stop() +{ + if (is_running()) + { + if (mysql_shutdown(&mysql, SHUTDOWN_DEFAULT)) + goto err; + + mysql_close(&mysql); + is_connected= FALSE; + return 0; + } + + return ER_INSTANCE_IS_NOT_STARTED; +err: + return ER_STOP_INSTANCE; +} + + +/* + We execute this function to initialize instance parameters. + Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY. +*/ + +int Instance::init(const char *name_arg) +{ + pthread_mutex_init(&LOCK_instance, 0); + + return options.init(name_arg); +} diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h new file mode 100644 index 00000000000..6733985116a --- /dev/null +++ b/server-tools/instance-manager/instance.h @@ -0,0 +1,60 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#include +#include +#include +#include "instance_options.h" + +#ifdef __GNUC__ +#pragma interface +#endif + +class Instance +{ +public: + Instance(): is_connected(FALSE) + {} + ~Instance(); + + int init(const char *name); + + /* check if the instance is running and set up mysql connection if yes */ + bool is_running(); + int start(); + int stop(); + int cleanup(); + +public: + Instance_options options; + + /* connection to the instance */ + MYSQL mysql; + +private: + /* + Mutex protecting the instance. Currently we use it to avoid the + double start of the instance. This happens when the instance is starting + and we issue the start command once more. + */ + pthread_mutex_t LOCK_instance; + /* Here we store the state of the following connection */ + bool is_connected; +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */ diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc new file mode 100644 index 00000000000..89731eb27b9 --- /dev/null +++ b/server-tools/instance-manager/instance_map.cc @@ -0,0 +1,450 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "instance_map.h" +#include "buffer.h" +#include "instance.h" +#include +#include +#include +#include + +/* + TODO: Currently there are some mysql-connection specific functions. + As we are going to suppost different types of connections, we shouldn't + have them here in future. To avoid it we could put such + connection-specific functions to the Command-derived class instead. + The command could be easily constructed for a specific connection if + we would provide a special factory for each connection. +*/ + +C_MODE_START + +/* Procedure needed for HASH initialization */ + +static byte* get_instance_key(const byte* u, uint* len, + my_bool __attribute__((unused)) t) +{ + const Instance *instance= (const Instance *) u; + *len= instance->options.instance_name_len; + return (byte *) instance->options.instance_name; +} + +static void delete_instance(void *u) +{ + Instance *instance= (Instance *) u; + delete instance; +} + +/* + The option handler to pass to the process_default_option_files finction. + + SYNOPSYS + process_option() + ctx Handler context. Here it is an instance_map structure. + group_name The name of the group the option belongs to. + option The very option to be processed. It is already + prepared to be used in argv (has -- prefix) + + DESCRIPTION + + This handler checks whether a group is an instance group and adds + an option to the appropriate instance class. If this is the first + occurence of an instance name, we'll also create the instance + with such name and add it to the instance map. + + RETURN + 0 - ok + 1 - error occured +*/ + +static int process_option(void * ctx, const char *group, const char *option) +{ + Instance_map *map= NULL; + Instance *instance= NULL; + static const char prefix[]= { 'm', 'y', 's', 'q', 'l', 'd' }; + + map = (Instance_map*) ctx; + if (strncmp(group, prefix, sizeof prefix) == 0 && + (my_isdigit(default_charset_info, group[sizeof prefix]))) + { + if ((instance= map->find(group, strlen(group))) == NULL) + { + if ((instance= new Instance) == 0) + goto err_new_instance; + if (instance->init(group)) + goto err; + if (map->add_instance(instance)) + goto err; + } + + if (instance->options.add_option(option)) + goto err; + } + + return 0; + +err: + delete instance; +err_new_instance: + return 1; +} + +C_MODE_END + + +Instance_map::Instance_map() +{ + hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, + get_instance_key, delete_instance, 0); + pthread_mutex_init(&LOCK_instance_map, 0); +} + + +Instance_map::~Instance_map() +{ + pthread_mutex_lock(&LOCK_instance_map); + hash_free(&hash); + pthread_mutex_unlock(&LOCK_instance_map); + pthread_mutex_destroy(&LOCK_instance_map); +} + + +int Instance_map::flush_instances() +{ + int rc; + + pthread_mutex_lock(&LOCK_instance_map); + hash_free(&hash); + hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, + get_instance_key, delete_instance, 0); + rc= load(); + pthread_mutex_unlock(&LOCK_instance_map); + return rc; +} + + +int Instance_map::show_instance_options(struct st_net *net, + const char *instance_name) +{ + enum { MAX_VERSION_LENGTH= 40 }; + Buffer send_buff; /* buffer for packets */ + LIST name, option; + LIST *field_list; + NAME_WITH_LENGTH name_field, option_field; + uint position=0; + + /* create list of the fileds to be passed to send_fields */ + name_field.name= (char *) "option_name"; + name_field.length= 20; + name.data= &name_field; + option_field.name= (char *) "value"; + option_field.length= 20; + option.data= &option_field; + field_list= list_add(NULL, &option); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + + if ((instance= find(instance_name, strlen(instance_name))) == NULL) + goto err; + store_to_string(&send_buff, (char *) "instance_name", &position); + store_to_string(&send_buff, (char *) instance_name, &position); + my_net_write(net, send_buff.buffer, (uint) position); + if (instance->options.mysqld_path != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "mysqld_path", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_path, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + if (instance->options.mysqld_user != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "admin_user", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_user, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + if (instance->options.mysqld_password != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "admin_password", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_password, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + /* loop through the options stored in DYNAMIC_ARRAY */ + for (int i= 0; i < instance->options.options_array.elements; i++) + { + char *tmp_option, *option_value; + get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i); + option_value= strchr(tmp_option, '='); + /* split the option string into two parts */ + *option_value= 0; + position= 0; + store_to_string(&send_buff, tmp_option + 2, &position); + store_to_string(&send_buff, option_value + 1, &position); + /* join name and the value into the same option again */ + *option_value= '='; + my_net_write(net, send_buff.buffer, (uint) position); + } + } + + send_eof(net); + net_flush(net); + + return 0; + +err: + return 1; +} + +/* return the list of running guarded instances */ +int Instance_map::init_guardian() +{ + Instance *instance; + uint i= 0; + + while (i < hash.records) + { + instance= (Instance *) hash_element(&hash, i); + if ((instance->options.is_guarded != NULL) && (instance->is_running())) + if (guardian->guard(instance->options.instance_name, + instance->options.instance_name_len)) + return 1; + i++; + } + + return 0; +} + + +/* + The method sends a list of instances in the instance map to the client. + + SYNOPSYS + show_instances() + net The network connection to the client. + + RETURN + 0 - ok + 1 - error occured +*/ + +int Instance_map::show_instances(struct st_net *net) +{ + Buffer send_buff; /* buffer for packets */ + LIST name, status; + NAME_WITH_LENGTH name_field, status_field; + LIST *field_list; + uint position=0; + + name_field.name= (char *) "instance_name"; + name_field.length= 20; + name.data= &name_field; + status_field.name= (char *) "status"; + status_field.length= 20; + status.data= &status_field; + field_list= list_add(NULL, &status); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + uint i= 0; + + pthread_mutex_lock(&LOCK_instance_map); + while (i < hash.records) + { + position= 0; + instance= (Instance *) hash_element(&hash, i); + store_to_string(&send_buff, instance->options.instance_name, &position); + if (instance->is_running()) + store_to_string(&send_buff, (char *) "online", &position); + else + store_to_string(&send_buff, (char *) "offline", &position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; + i++; + } + pthread_mutex_unlock(&LOCK_instance_map); + } + if (send_eof(net)) + goto err; + if (net_flush(net)) + goto err; + + return 0; +err: + return 1; +} + + +/* + The method sends a table with a status of requested instance to the client. + + SYNOPSYS + show_instance_status() + net The network connection to the client. + instance_name The name of the instance. + + RETURN + 0 - ok + 1 - error occured +*/ + +int Instance_map::show_instance_status(struct st_net *net, + const char *instance_name) +{ + enum { MAX_VERSION_LENGTH= 40 }; + Buffer send_buff; /* buffer for packets */ + LIST name, status, version; + LIST *field_list; + NAME_WITH_LENGTH name_field, status_field, version_field; + uint position=0; + + /* create list of the fileds to be passed to send_fields */ + name_field.name= (char *) "instance_name"; + name_field.length= 20; + name.data= &name_field; + status_field.name= (char *) "status"; + status_field.length= 20; + status.data= &status_field; + version_field.name= (char *) "version"; + version_field.length= MAX_VERSION_LENGTH; + version.data= &version_field; + field_list= list_add(NULL, &version); + field_list= list_add(field_list, &status); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + + store_to_string(&send_buff, (char *) instance_name, &position); + if ((instance= find(instance_name, strlen(instance_name))) == NULL) + goto err; + if (instance->is_running()) + { + store_to_string(&send_buff, (char *) "online", &position); + store_to_string(&send_buff, mysql_get_server_info(&(instance->mysql)), &position); + } + else + { + store_to_string(&send_buff, (char *) "offline", &position); + store_to_string(&send_buff, (char *) "unknown", &position); + } + + + my_net_write(net, send_buff.buffer, (uint) position); + } + + send_eof(net); + net_flush(net); + +err: + return 0; +} + + +int Instance_map::add_instance(Instance *instance) +{ + return my_hash_insert(&hash, (byte *) instance); +} + + +Instance * +Instance_map::find(const char *name, uint name_len) +{ + Instance *instance; + pthread_mutex_lock(&LOCK_instance_map); + instance= (Instance *) hash_search(&hash, (byte *) name, name_len); + pthread_mutex_unlock(&LOCK_instance_map); + return instance; +} + + +void Instance_map::complete_initialization() +{ + Instance *instance; + uint i= 0; + + while (i < hash.records) + { + instance= (Instance *) hash_element(&hash, i); + instance->options.complete_initialization(mysqld_path, user, password); + i++; + } +} + + +int Instance_map::cleanup() +{ + Instance *instance; + uint i= 0; + + while (i < hash.records) + { + instance= (Instance *) hash_element(&hash, i); + instance->cleanup(); + i++; + } +} + + +Instance * +Instance_map::find(uint instance_number) +{ + Instance *instance; + char name[80]; + + sprintf(name, "mysqld%i", instance_number); + pthread_mutex_lock(&LOCK_instance_map); + instance= (Instance *) hash_search(&hash, (byte *) name, strlen(name)); + pthread_mutex_unlock(&LOCK_instance_map); + return instance; +} + + +/* load options from config files and create appropriate instance structures */ + +int Instance_map::load() +{ + int error; + + error= process_default_option_files("my", process_option, (void *) this); + + complete_initialization(); + + return error; +} diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h new file mode 100644 index 00000000000..965261f2920 --- /dev/null +++ b/server-tools/instance-manager/instance_map.h @@ -0,0 +1,76 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#include +#include +#include + +#ifdef __GNUC__ +#pragma interface +#endif + +#include "protocol.h" +#include "guardian.h" + +class Instance; +extern int load_all_groups(char ***groups, const char *filename); +extern void free_groups(char **groups); + + +/* + Instance_map - stores all existing instances +*/ + +class Instance_map +{ +public: + /* returns a pointer to the instance or NULL, if there is no such instance */ + Instance *find(const char *name, uint name_len); + Instance *find(uint instance_number); + + int show_instances(struct st_net *net); + int show_instance_status(struct st_net *net, const char *instance_name); + int show_instance_options(struct st_net *net, const char *instance_name); + int flush_instances(); + int init_guardian(); + int cleanup(); + + Instance_map(); + ~Instance_map(); + + /* loads options from config files */ + int load(); + /* adds instance to internal hash */ + int add_instance(Instance *instance); + /* inits instances argv's after all options have been loaded */ + void complete_initialization(); + +public: + const char *mysqld_path; + /* user an password to shutdown MySQL */ + const char *user; + const char *password; + Guardian_thread *guardian; + +private: + enum { START_HASH_SIZE = 16 }; + pthread_mutex_t LOCK_instance_map; + HASH hash; +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H */ diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc new file mode 100644 index 00000000000..7586566de9d --- /dev/null +++ b/server-tools/instance-manager/instance_options.cc @@ -0,0 +1,212 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "instance_options.h" +#include +#include +#include +#include + +int Instance_options::complete_initialization(const char *default_path, + const char *default_user, + const char *default_password) +{ + /* we need to reserve space for the final zero + possible default options */ + if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1 + + MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*)))) + goto err; + + + if (mysqld_path == NULL) + { + if (!(mysqld_path= strdup_root(&alloc, default_path))) + goto err; + } + + /* this option must be first in the argv */ + if (add_to_argv(mysqld_path)) + goto err; + + /* the following options are not for argv */ + if (mysqld_user == NULL) + { + if (!(mysqld_user= strdup_root(&alloc, default_user))) + goto err; + } + + if (mysqld_password == NULL) + { + if (!(mysqld_password= strdup_root(&alloc, default_password))) + goto err; + } + + memcpy((gptr) (argv + filled_default_options), options_array.buffer, + options_array.elements*sizeof(char*)); + argv[filled_default_options + options_array.elements]= 0; + + return 0; + +err: + return 1; +} + + +/* + Assigns given value to the appropriate option from the class. + + SYNOPSYS + add_option() + option string with the option prefixed by -- + + DESCRIPTION + + The method is called from the option handling routine. + + RETURN + 0 - ok + 1 - error occured +*/ + +int Instance_options::add_option(const char* option) +{ + uint elements_count=0; + static const char socket[]= "--socket="; + static const char port[]= "--port="; + static const char datadir[]= "--datadir="; + static const char language[]= "--bind-address="; + static const char pid_file[]= "--pid-file="; + static const char path[]= "--mysqld_path="; + static const char user[]= "--admin_user="; + static const char password[]= "--admin_password="; + static const char guarded[]= "--guarded"; + char *tmp; + + if (!(tmp= strdup_root(&alloc, option))) + goto err; + + /* To get rid the final zero in a string we subtract 1 from sizeof value */ + if (strncmp(tmp, socket, sizeof socket - 1) == 0) + { + mysqld_socket= tmp; + goto add_options; + } + + if (strncmp(tmp, port, sizeof port - 1) == 0) + { + mysqld_port= tmp; + goto add_options; + } + + if (strncmp(tmp, datadir, sizeof datadir - 1) == 0) + { + mysqld_datadir= tmp; + goto add_options; + } + + if (strncmp(tmp, language, sizeof language - 1) == 0) + { + mysqld_bind_address= tmp; + goto add_options; + } + + if (strncmp(tmp, pid_file, sizeof pid_file - 1) == 0) + { + mysqld_pid_file= tmp; + goto add_options; + } + + /* + We don't need a prefix in the next three optios. + We also don't need to add them to argv array => + return instead of goto. + */ + + if (strncmp(tmp, path, sizeof path - 1) == 0) + { + mysqld_path= strchr(tmp, '=') + 1; + return 0; + } + + if (strncmp(tmp, user, sizeof user - 1) == 0) + { + mysqld_user= strchr(tmp, '=') + 1; + return 0; + } + + if (strncmp(tmp, password, sizeof password - 1) == 0) + { + mysqld_password= strchr(tmp, '=') + 1; + return 0; + } + + if (strncmp(tmp, guarded, sizeof guarded - 1) == 0) + { + is_guarded= tmp; + return 0; + } + +add_options: + insert_dynamic(&options_array,(gptr) &tmp); + return 0; + +err: + return 1; +} + +int Instance_options::add_to_argv(const char* option) +{ + DBUG_ASSERT(filled_default_options < (MAX_NUMBER_OF_DEFAULT_OPTIONS + 1)); + + if (option != NULL) + argv[filled_default_options++]= (char *) option; + return 0; +} + + +/* + We execute this function to initialize some options. + Return value: 0 - ok. 1 - unable to allocate memory. +*/ + +int Instance_options::init(const char *instance_name_arg) +{ + instance_name_len= strlen(instance_name_arg); + + init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); + + my_init_dynamic_array(&options_array, sizeof(char *), 0, 32); + + if (!(instance_name= strmake_root(&alloc, (char *) instance_name_arg, + instance_name_len))) + goto err; + + return 0; + +err: + return 1; +} + + +Instance_options::~Instance_options() +{ + free_root(&alloc, MYF(0)); + delete_dynamic(&options_array); +} + diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h new file mode 100644 index 00000000000..5034e775cd0 --- /dev/null +++ b/server-tools/instance-manager/instance_options.h @@ -0,0 +1,76 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#include +#include + +#ifdef __GNUC__ +#pragma interface +#endif + + +/* + This class contains options of an instance and methods to operate them. + + We do not provide this class with the means of synchronization as it is + supposed that options for instances are all loaded at once during the + instance_map initilization and we do not change them later. This way we + don't have to synchronize between threads. +*/ + +class Instance_options +{ +public: + Instance_options() : mysqld_socket(0), mysqld_datadir(0), + mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), + mysqld_user(0), mysqld_password(0), is_guarded(0), filled_default_options(0) + {} + ~Instance_options(); + /* fills in argv */ + int complete_initialization(const char *default_path, + const char *default_user, + const char *default_password); + + int add_option(const char* option); + int init(const char *instance_name_arg); + +public: + enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 3 }; + enum { MEM_ROOT_BLOCK_SIZE= 512 }; + char **argv; + /* We need the some options, so we store them as a separate pointers */ + const char *mysqld_socket; + const char *mysqld_datadir; + const char *mysqld_bind_address; + const char *mysqld_pid_file; + const char *mysqld_port; + uint instance_name_len; + const char *instance_name; + const char *mysqld_path; + const char *mysqld_user; + const char *mysqld_password; + const char *is_guarded; + DYNAMIC_ARRAY options_array; +private: + int add_to_argv(const char *option); +private: + uint filled_default_options; + MEM_ROOT alloc; +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H */ diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 4bbfaf0b81a..749a31ea525 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -1,72 +1,306 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +#ifdef __GNUC__ +#pragma implementation +#endif + #include "listener.h" -#include "thread_repository.h" + +#include +#include +#include +#include + +#include "thread_registry.h" +#include "options.h" +#include "instance_map.h" #include "log.h" +#include "mysql_connection.h" + + +/* + Listener_thread - incapsulates listening functionality +*/ + +class Listener_thread: public Listener_thread_args +{ +public: + Listener_thread(const Listener_thread_args &args); + ~Listener_thread(); + void run(); +private: + ulong total_connection_count; + Thread_info thread_info; +private: + void handle_new_mysql_connection(Vio *vio); +}; + + +Listener_thread::Listener_thread(const Listener_thread_args &args) : + Listener_thread_args(args.thread_registry, args.options, args.user_map, + args.instance_map) + ,total_connection_count(0) + ,thread_info(pthread_self()) +{ + thread_registry.register_thread(&thread_info); +} + + +Listener_thread::~Listener_thread() +{ + thread_registry.unregister_thread(&thread_info); +} + + +/* + Listener_thread::run() - listen all supported sockets and spawn a thread + to handle incoming connection. + Using 'die' in case of syscall failure is OK now - we don't hold any + resources and 'die' kills the signal thread automatically. To be rewritten + one day. + See also comments in mysqlmanager.cc to picture general Instance Manager + architecture. +*/ + +void Listener_thread::run() +{ + enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size + int flags; + int arg= 1; /* value to be set by setsockopt */ + /* I. prepare 'listen' sockets */ + + int ip_socket= socket(AF_INET, SOCK_STREAM, 0); + if (ip_socket == INVALID_SOCKET) + { + log_error("Listener_thead::run(): socket(AF_INET) failed, %s", + strerror(errno)); + thread_registry.request_shutdown(); + return; + } + + struct sockaddr_in ip_socket_address; + memset(&ip_socket_address, 0, sizeof(ip_socket_address)); + + ulong im_bind_addr; + if (options.bind_address != 0) + { + if ((im_bind_addr= (ulong) inet_addr(options.bind_address)) == INADDR_NONE) + im_bind_addr= htonl(INADDR_ANY); + } + else im_bind_addr= htonl(INADDR_ANY); + uint im_port= options.port_number; + + ip_socket_address.sin_family= AF_INET; + ip_socket_address.sin_addr.s_addr = im_bind_addr; + + + ip_socket_address.sin_port= (unsigned short) + htons((unsigned short) im_port); + + setsockopt(ip_socket, SOL_SOCKET, SO_REUSEADDR, (char*) &arg, sizeof(arg)); + if (bind(ip_socket, (struct sockaddr *) &ip_socket_address, + sizeof(ip_socket_address))) + { + log_error("Listener_thread::run(): bind(ip socket) failed, '%s'", + strerror(errno)); + thread_registry.request_shutdown(); + return; + } + + if (listen(ip_socket, LISTEN_BACK_LOG_SIZE)) + { + log_error("Listener_thread::run(): listen(ip socket) failed, %s", + strerror(errno)); + thread_registry.request_shutdown(); + return; + } + flags= fcntl(ip_socket, F_GETFL, 0); + fcntl(ip_socket, F_SETFL, flags | O_NONBLOCK); + + log_info("accepting connections on ip socket"); + + /*--------------------------------------------------------------*/ + int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0); + if (unix_socket == INVALID_SOCKET) + { + log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s", + strerror(errno)); + thread_registry.request_shutdown(); + return; + } + + struct sockaddr_un unix_socket_address; + memset(&unix_socket_address, 0, sizeof(unix_socket_address)); + + unix_socket_address.sun_family= AF_UNIX; + strmake(unix_socket_address.sun_path, options.socket_file_name, + sizeof(unix_socket_address.sun_path)); + unlink(unix_socket_address.sun_path); // in case we have stale socket file + + { + /* + POSIX specifies default permissions for a pathname created by bind + to be 0777. We need everybody to have access to the socket. + */ + mode_t old_mask= umask(0); + if (bind(unix_socket, (struct sockaddr *) &unix_socket_address, + sizeof(unix_socket_address))) + { + log_error("Listener_thread::run(): bind(unix socket) failed, " + "socket file name is '%s', error '%s'", + unix_socket_address.sun_path, strerror(errno)); + thread_registry.request_shutdown(); + return; + } + umask(old_mask); + + if (listen(unix_socket, LISTEN_BACK_LOG_SIZE)) + { + log_error("Listener_thread::run(): listen(unix socket) failed, %s", + strerror(errno)); + thread_registry.request_shutdown(); + return; + } + + /* set the socket nonblocking */ + flags= fcntl(unix_socket, F_GETFL, 0); + fcntl(unix_socket, F_SETFL, flags | O_NONBLOCK); + } + log_info("accepting connections on unix socket %s", + unix_socket_address.sun_path); + + /* II. Listen sockets and spawn childs */ + + { + int n= max(unix_socket, ip_socket) + 1; + fd_set read_fds; + + FD_ZERO(&read_fds); + FD_SET(unix_socket, &read_fds); + FD_SET(ip_socket, &read_fds); + + while (thread_registry.is_shutdown() == false) + { + fd_set read_fds_arg= read_fds; + int rc= select(n, &read_fds_arg, 0, 0, 0); + if (rc == -1 && errno != EINTR) + log_error("Listener_thread::run(): select() failed, %s", + strerror(errno)); + else + { + /* Assuming that rc > 0 as we asked to wait forever */ + if (FD_ISSET(unix_socket, &read_fds_arg)) + { + int client_fd= accept(unix_socket, 0, 0); + /* accept may return -1 (failure or spurious wakeup) */ + if (client_fd >= 0) // connection established + { + if (Vio *vio= vio_new(client_fd, VIO_TYPE_SOCKET, 1)) + handle_new_mysql_connection(vio); + else + { + shutdown(client_fd, SHUT_RDWR); + close(client_fd); + } + } + } + else + if (FD_ISSET(ip_socket, &read_fds_arg)) + { + int client_fd= accept(ip_socket, 0, 0); + /* accept may return -1 (failure or spurious wakeup) */ + if (client_fd >= 0) // connection established + { + if (Vio *vio= vio_new(client_fd, VIO_TYPE_TCPIP, 0)) + { + handle_new_mysql_connection(vio); + } + else + { + shutdown(client_fd, SHUT_RDWR); + close(client_fd); + } + } + } + } + } + } + + /* III. Release all resources and exit */ + + log_info("Listener_thread::run(): shutdown requested, exiting..."); + + close(unix_socket); + unlink(unix_socket_address.sun_path); +} + + +/* + Create new mysql connection. Created thread is responsible for deletion of + the Mysql_connection_thread_args and Vio instances passed to it. + SYNOPSYS + handle_new_mysql_connection() +*/ + +void Listener_thread::handle_new_mysql_connection(Vio *vio) +{ + if (Mysql_connection_thread_args *mysql_thread_args= + new Mysql_connection_thread_args(vio, thread_registry, user_map, + ++total_connection_count, + instance_map) + ) + { + /* + Initialize thread attributes to create detached thread; it seems + easier to do it ad-hoc than have a global variable for attributes. + */ + pthread_t mysql_thd_id; + pthread_attr_t mysql_thd_attr; + pthread_attr_init(&mysql_thd_attr); + pthread_attr_setdetachstate(&mysql_thd_attr, PTHREAD_CREATE_DETACHED); + if (pthread_create(&mysql_thd_id, &mysql_thd_attr, mysql_connection, + mysql_thread_args)) + { + delete mysql_thread_args; + vio_delete(vio); + log_error("handle_one_mysql_connection(): pthread_create(mysql) failed"); + } + pthread_attr_destroy(&mysql_thd_attr); + } + else + vio_delete(vio); +} + C_MODE_START + pthread_handler_decl(listener, arg) { - Thread_info info(pthread_self()); - Thread_repository &thread_repository= - ((Listener_thread_args *) arg)->thread_repository; - thread_repository.register_thread(&info); - - while (true) - { - log_info("listener is alive"); - sleep(2); - if (thread_repository.is_shutdown()) - break; - } - log_info("listener(): shutdown requested, exiting..."); - - thread_repository.unregister_thread(&info); + Listener_thread_args *args= (Listener_thread_args *) arg; + Listener_thread listener(*args); + listener.run(); + /* + args is a stack variable because listener thread lives as long as the + manager process itself + */ return 0; } + C_MODE_END -#if 0 - while (true) - { - } - /* - Dummy manager implementation: listens on a UNIX socket and - starts echo server in a dedicated thread for each accepted connection. - Enough to test startup/shutdown/options/logging of the instance manager. - */ - - int fd= socket(AF_UNIX, SOCK_STREAM, 0); - - if (!fd) - die("socket(): failed"); - - struct sockaddr_un address; - bzero(&address, sizeof(address)); - address.sun_family= AF_UNIX; - strcpy(address.sun_path, socket_path); - int opt= 1; - - if (unlink(socket_path) || - bind(fd, (struct sockaddr *) &address, sizeof(address)) || - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) - die("unlink | bind | setsockopt failed"); - - if (listen(fd, 5)) - die("listen() failed"); - - int client_fd; - while ((client_fd= accept(fd, 0, 0)) != -1); - { - printf("accepted\n"); - const char *message= "\n10hel"; - send(client_fd, message, strlen(message), 0); - - int sleep_seconds= argc > 1 && atoi(argv[1]) ? atoi(argv[1]) : 1; - printf("sleeping %d seconds\n", sleep_seconds); - sleep(sleep_seconds); - close(client_fd); - } - printf("accept(): failed\n"); - close(fd); -#endif diff --git a/server-tools/instance-manager/listener.h b/server-tools/instance-manager/listener.h index 9165e5a0ee7..7a8af49e2b3 100644 --- a/server-tools/instance-manager/listener.h +++ b/server-tools/instance-manager/listener.h @@ -1,6 +1,6 @@ #ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H #define INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 @@ -23,23 +23,34 @@ #include #include + C_MODE_START pthread_handler_decl(listener, arg); C_MODE_END -class Thread_repository; +class Thread_registry; +class Options; +class User_map; +class Instance_map; struct Listener_thread_args { - Thread_repository &thread_repository; - const char *socket_file_name; + Thread_registry &thread_registry; + const Options &options; + const User_map &user_map; + Instance_map &instance_map; - Listener_thread_args(Thread_repository &thread_repository_arg, - const char *socket_file_name_arg) : - thread_repository(thread_repository_arg), - socket_file_name(socket_file_name_arg) {} + Listener_thread_args(Thread_registry &thread_registry_arg, + const Options &options_arg, + const User_map &user_map_arg, + Instance_map &instance_map_arg) : + thread_registry(thread_registry_arg) + ,options(options_arg) + ,user_map(user_map_arg) + ,instance_map(instance_map_arg) + {} }; -#endif +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H diff --git a/server-tools/instance-manager/log.cc b/server-tools/instance-manager/log.cc index 7eb0f15b2a4..f89f5e425b8 100644 --- a/server-tools/instance-manager/log.cc +++ b/server-tools/instance-manager/log.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 @@ -139,12 +139,12 @@ void print_error(const char *format, ...) } /* - init_logs() + log_init() RETURN VALUE 0 ok !0 error */ - + void log_init() { /* diff --git a/server-tools/instance-manager/log.h b/server-tools/instance-manager/log.h index 7b69e516edb..a1441d5bd71 100644 --- a/server-tools/instance-manager/log.h +++ b/server-tools/instance-manager/log.h @@ -1,6 +1,6 @@ #ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_LOG_H #define INCLUDES_MYSQL_INSTANCE_MANAGER_LOG_H -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 @@ -22,11 +22,8 @@ Two logging streams are supported: error log and info log. Additionally libdbug may be used for debug information output. ANSI C buffered I/O is used to perform logging. - Logging may be performed in two modes: - - console application mode (default), stdout/stderr is used for logging - init_logs() must be called to initialize logging environment - - daemon mode, without controlling terminal, call - init_logs_in_daemon_mode() to initialize + Logging is performed via stdout/stder, so one can reopen them to point to + ordinary files. To initialize loggin environment log_init() must be called. Rationale: - no MYSQL_LOG as it has BIN mode, and not easy to fetch from sql_class.h diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 06e181d52d5..cb51197d52a 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 @@ -17,19 +17,68 @@ #include "manager.h" #include +#include +#include #include +#include -#include "thread_repository.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" -void manager(const char *socket_file_name) +/* + manager - entry point to the main instance manager process: start + listener thread, write pid file and enter into signal handling. + See also comments in mysqlmanager.cc to picture general Instance Manager + architecture. +*/ + +void manager(const Options &options) { - Thread_repository thread_repository; - Listener_thread_args listener_args(thread_repository, socket_file_name); + Thread_registry thread_registry; + /* + All objects created in the manager() function live as long as + thread_registry lives, and thread_registry is alive until there are + working threads. + */ + + User_map user_map; + Instance_map instance_map; + Guardian_thread guardian_thread(thread_registry, + &instance_map, + options.monitoring_interval); + + instance_map.mysqld_path= options.default_mysqld_path; + instance_map.user= options.default_admin_user; + instance_map.password= options.default_admin_password; + instance_map.guardian= &guardian_thread; + instance_map.load(); + + Listener_thread_args listener_args(thread_registry, options, user_map, + instance_map); + + + if (user_map.load(options.password_file_name)) + return; /* write pid file */ + if (FILE *pid_file= my_fopen(options.pid_file_name, + O_WRONLY | O_CREAT | O_BINARY, MYF(0))) + { + fprintf(pid_file, "%d\n", (int) getpid()); + my_fclose(pid_file, MYF(0)); + } + else + { + log_error("can't create pid file %s: errno=%d, %s", + options.pid_file_name, errno, strerror(errno)); + return; + } /* block signals */ sigset_t mask; @@ -37,26 +86,98 @@ void manager(const char *socket_file_name) sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGHUP); + /* + We want this signal to be blocked in all theads but the signal + one. It is needed for the thr_alarm subsystem to work. + */ + sigaddset(&mask,THR_SERVER_ALARM); /* all new threads will inherite this signal mask */ pthread_sigmask(SIG_BLOCK, &mask, NULL); + + /* create the listener */ { - /* create the listener */ pthread_t listener_thd_id; pthread_attr_t listener_thd_attr; + int rc; pthread_attr_init(&listener_thd_attr); pthread_attr_setdetachstate(&listener_thd_attr, PTHREAD_CREATE_DETACHED); - if (pthread_create(&listener_thd_id, &listener_thd_attr, listener, - &listener_args)) - die("manager(): pthread_create(listener) failed"); + rc= pthread_create(&listener_thd_id, &listener_thd_attr, listener, + &listener_args); + pthread_attr_destroy(&listener_thd_attr); + if (rc) + { + log_error("manager(): pthread_create(listener) failed"); + goto err; + } + } + + /* create guardian thread */ + { + pthread_t guardian_thd_id; + pthread_attr_t guardian_thd_attr; + int rc; + + pthread_attr_init(&guardian_thd_attr); + pthread_attr_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED); + rc= pthread_create(&guardian_thd_id, &guardian_thd_attr, guardian, + &guardian_thread); + pthread_attr_destroy(&guardian_thd_attr); + if (rc) + { + log_error("manager(): pthread_create(guardian) failed"); + goto err; + } + + } + /* To work nicely with LinuxThreads, the signal thread is the first thread in the process. */ int signo; - sigwait(&mask, &signo); - thread_repository.deliver_shutdown(); + bool shutdown_complete; + + shutdown_complete= FALSE; + /* + In our case the signal thread also implements functions of alarm thread. + Here we init alarm thread functionality. We suppose that we won't have + more then 10 alarms at the same time. + */ + init_thr_alarm(10); + /* + Now we can init the list of guarded instances. We have to do it after + alarm structures initialization as we have to use net_* functions while + making the list. And they in their turn need alarms for timeout suppport. + */ + instance_map.init_guardian(); + + while (!shutdown_complete) + { + sigwait(&mask, &signo); + switch (signo) + { + case THR_SERVER_ALARM: + process_alarm(signo); + break; + default: + thread_registry.deliver_shutdown(); + shutdown_complete= TRUE; + break; + } + } + +err: + /* delete the pid file */ + my_delete(options.pid_file_name, MYF(0)); + + /* close permanent connections to the running instances */ + instance_map.cleanup(); + + /* free alarm structures */ + end_thr_alarm(1); /* don't pthread_exit to kill all threads who did not shut down in time */ } + diff --git a/server-tools/instance-manager/manager.h b/server-tools/instance-manager/manager.h index 2f30813180a..d73f4b35f18 100644 --- a/server-tools/instance-manager/manager.h +++ b/server-tools/instance-manager/manager.h @@ -1,6 +1,6 @@ #ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H #define INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 @@ -16,6 +16,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -void manager(const char *socket_file_name); +class Options; + +void manager(const Options &options); #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H diff --git a/server-tools/instance-manager/messages.cc b/server-tools/instance-manager/messages.cc new file mode 100644 index 00000000000..cc07352f58a --- /dev/null +++ b/server-tools/instance-manager/messages.cc @@ -0,0 +1,73 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ +#include "messages.h" + +#include +#include + +#include + + +static const char *mysqld_error_message(unsigned sql_errno) +{ + switch (sql_errno) { + case ER_HANDSHAKE_ERROR: + return "Bad handshake"; + case ER_OUT_OF_RESOURCES: + return "Out of memory; Check if mysqld or some other process" + " uses all available memory. If not you may have to use" + " 'ulimit' to allow mysqld to use more memory or you can" + " add more swap space"; + case ER_ACCESS_DENIED_ERROR: + return "Access denied. Bad username/password pair"; + case ER_NOT_SUPPORTED_AUTH_MODE: + return "Client does not support authentication protocol requested by" + " server; consider upgrading MySQL client"; + case ER_UNKNOWN_COM_ERROR: + return "Unknown command"; + case ER_SYNTAX_ERROR: + return "You have an error in your command syntax. Check the manual that" + " corresponds to your MySQL Instance Manager version for the right" + " syntax to use"; + case ER_BAD_INSTANCE_NAME: + return "Bad instance name. Check that the instance with such a name exists"; + case ER_INSTANCE_IS_NOT_STARTED: + return "Cannot stop instance. Perhaps the instance is not started or you" + " have specified wrong username/password in the config file"; + case ER_INSTANCE_ALREADY_STARTED: + return "The instance is already started"; + case ER_CANNOT_START_INSTANCE: + return "Cannot start instance. Possible reasons are wrong instance options" + " or resources shortage"; + case ER_STOP_INSTANCE: + return "Cannot stop instance"; + default: + DBUG_ASSERT(0); + return 0; + } +} + + +const char *message(unsigned sql_errno) +{ + return mysqld_error_message(sql_errno); +} + + +const char *errno_to_sqlstate(unsigned sql_errno) +{ + return mysql_errno_to_sqlstate(sql_errno); +} diff --git a/server-tools/instance-manager/messages.h b/server-tools/instance-manager/messages.h new file mode 100644 index 00000000000..4cc15b5efe4 --- /dev/null +++ b/server-tools/instance-manager/messages.h @@ -0,0 +1,26 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_MESSAGES_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_MESSAGES_H +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +#include "mysqld_error.h" +#include "mysql_manager_error.h" + +const char *message(unsigned sql_errno); + +const char *errno_to_sqlstate(unsigned sql_errno); + +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MESSAGES_H diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc new file mode 100644 index 00000000000..2a617675c26 --- /dev/null +++ b/server-tools/instance-manager/mysql_connection.cc @@ -0,0 +1,388 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include "mysql_connection.h" +#include "priv.h" + +#include +#include +#include +#include +#include + +#include "thread_registry.h" +#include "log.h" +#include "user_map.h" +#include "protocol.h" +#include "messages.h" +#include "command.h" +#include "factory.h" +#include "parse.h" + +Command *parse_command(Command_factory * factory, const char *text); + + +/* + MySQL connection - handle one connection with mysql command line client + See also comments in mysqlmanager.cc to picture general Instance Manager + architecture. + We use conventional technique to work with classes without exceptions: + class acquires all vital resource in init(); Thus if init() succeed, + a user must call cleanup(). All other methods are valid only between + init() and cleanup(). +*/ + +class Mysql_connection_thread: public Mysql_connection_thread_args +{ +public: + Mysql_connection_thread(const Mysql_connection_thread_args &args); + + int init(); + void cleanup(); + + void run(); + + ~Mysql_connection_thread(); +private: + Thread_info thread_info; + NET net; + struct rand_struct rand_st; + char scramble[SCRAMBLE_LENGTH + 1]; + uint status; + ulong client_capabilities; +private: + /* Names are conventionally the same as in mysqld */ + int check_connection(); + int check_user(const char *user, const char *password); + int do_command(); + int dispatch_command(enum enum_server_command command, + const char *text, uint len); +}; + + +Mysql_connection_thread::Mysql_connection_thread( + const Mysql_connection_thread_args &args) : + Mysql_connection_thread_args(args.vio, + args.thread_registry, + args.user_map, + args.connection_id, + args.instance_map) + ,thread_info(pthread_self()) +{ + thread_registry.register_thread(&thread_info); +} + + +/* + NET subsystem requieres its user to provide my_net_local_init extern + C function (exactly as declared below). my_net_local_init is called by + my_net_init and is supposed to set NET controlling variables. + See also priv.h for variables description. +*/ + +C_MODE_START + +void my_net_local_init(NET *net) +{ + net->max_packet= net_buffer_length; + net->read_timeout= net_read_timeout; + net->write_timeout= net_write_timeout; + net->retry_count= net_retry_count; + net->max_packet_size= max_allowed_packet; +} + +C_MODE_END + + +/* + Every resource, which we can fail to acquire, is allocated in init(). + This function is complementary to cleanup(). +*/ + +int Mysql_connection_thread::init() +{ + /* Allocate buffers for network I/O */ + if (my_net_init(&net, vio)) + return 1; + net.return_status= &status; + /* Initialize random number generator */ + { + ulong seed1= (ulong) &rand_st + rand(); + ulong seed2= rand() + time(0); + randominit(&rand_st, seed1, seed2); + } + /* Fill scramble - server's random message used for handshake */ + create_random_string(scramble, SCRAMBLE_LENGTH, &rand_st); + /* We don't support transactions, every query is atomic */ + status= SERVER_STATUS_AUTOCOMMIT; + return 0; +} + + +void Mysql_connection_thread::cleanup() +{ + net_end(&net); +} + + +Mysql_connection_thread::~Mysql_connection_thread() +{ + /* vio_delete closes the socket if necessary */ + vio_delete(vio); + thread_registry.unregister_thread(&thread_info); +} + + +void Mysql_connection_thread::run() +{ + log_info("accepted mysql connection %d", connection_id); + + my_thread_init(); + + if (check_connection()) + { + my_thread_end(); + return; + } + + log_info("connection %d is checked successfully", connection_id); + + vio_keepalive(vio, TRUE); + + while (!net.error && net.vio && !thread_registry.is_shutdown()) + { + if (do_command()) + break; + } + + my_thread_end(); +} + + +int Mysql_connection_thread::check_connection() +{ + ulong pkt_len=0; // to hold client reply length + + /* buffer for the first packet */ /* packet contains: */ + char buff[mysqlmanager_version_length + 1 + // server version, 0-ended + 4 + // connection id + SCRAMBLE_LENGTH + 2 + // scramble (in 2 pieces) + 18]; // server variables: flags, + // charset number, status, + char *pos= buff; + ulong server_flags; + + memcpy(pos, mysqlmanager_version, mysqlmanager_version_length + 1); + pos+= mysqlmanager_version_length + 1; + + int4store((uchar*) pos, connection_id); + pos+= 4; + + /* + Old clients does not understand long scrambles, but can ignore packet + tail: that's why first part of the scramble is placed here, and second + part at the end of packet (even though we don't support old clients, + we must follow standard packet format.) + */ + memcpy(pos, scramble, SCRAMBLE_LENGTH_323); + pos+= SCRAMBLE_LENGTH_323; + *pos++= '\0'; + + server_flags= CLIENT_LONG_FLAG | CLIENT_PROTOCOL_41 | + CLIENT_SECURE_CONNECTION; + + /* + 18-bytes long section for various flags/variables + + Every flag occupies a bit in first half of ulong; int2store will + gracefully pick up all flags. + */ + int2store(pos, server_flags); + pos+= 2; + *pos++= (char) default_charset_info->number; // global mysys variable + int2store(pos, status); // connection status + pos+= 2; + bzero(pos, 13); // not used now + pos+= 13; + + /* second part of the scramble, null-terminated */ + memcpy(pos, scramble + SCRAMBLE_LENGTH_323, + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1); + pos+= SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1; + + /* write connection message and read reply */ + enum { MIN_HANDSHAKE_SIZE= 2 }; + if (net_write_command(&net, protocol_version, "", 0, buff, pos - buff) || + (pkt_len= my_net_read(&net)) == packet_error || + pkt_len < MIN_HANDSHAKE_SIZE) + { + net_send_error(&net, ER_HANDSHAKE_ERROR); + return 1; + } + + client_capabilities= uint2korr(net.read_pos); + if (!(client_capabilities & CLIENT_PROTOCOL_41)) + { + net_send_error_323(&net, ER_NOT_SUPPORTED_AUTH_MODE); + return 1; + } + client_capabilities|= ((ulong) uint2korr(net.read_pos + 2)) << 16; + + pos= (char *) net.read_pos + 32; + + /* At least one byte for username and one byte for password */ + if (pos >= (char *) net.read_pos + pkt_len + 2) + { + /*TODO add user and password handling in error messages*/ + net_send_error(&net, ER_HANDSHAKE_ERROR); + return 1; + } + + const char *user= pos; + const char *password= strend(user)+1; + ulong password_len= *password++; + if (password_len != SCRAMBLE_LENGTH) + { + net_send_error(&net, ER_ACCESS_DENIED_ERROR); + return 1; + } + if (user_map.authenticate(user, password-user-2, password, scramble)) + { + net_send_error(&net, ER_ACCESS_DENIED_ERROR); + return 1; + } + net_send_ok(&net, connection_id); + return 0; +} + + +int Mysql_connection_thread::check_user(const char *user, const char *password) +{ + return 0; +} + + +int Mysql_connection_thread::do_command() +{ + char *packet; + uint old_timeout; + ulong packet_length; + + /* We start to count packets from 0 for each new command */ + net.pkt_nr= 0; + + if ((packet_length=my_net_read(&net)) == packet_error) + { + /* Check if we can continue without closing the connection */ + if (net.error != 3) // what is 3 - find out + { + return 1; + } + if (thread_registry.is_shutdown()) + return 1; + net_send_error(&net, net.last_errno); + net.error= 0; + return 0; + } + else + { + if (thread_registry.is_shutdown()) + return 1; + packet= (char*) net.read_pos; + enum enum_server_command command= (enum enum_server_command) + (uchar) *packet; + log_info("connection %d: packet_length=%d, command=%d", + connection_id, packet_length, command); + return dispatch_command(command, packet + 1, packet_length - 1); + } +} + +int Mysql_connection_thread::dispatch_command(enum enum_server_command command, + const char *packet, uint len) +{ + switch (command) { + case COM_QUIT: // client exit + log_info("query for connection %d received quit command",connection_id); + return 1; + case COM_PING: + log_info("query for connection %d received ping command",connection_id); + net_send_ok(&net, connection_id); + break; + case COM_QUERY: + { + log_info("query for connection %d : ----\n%s\n-------------------------", + connection_id,packet); + Command_factory commands_factory(instance_map); + if (Command *command= parse_command(&commands_factory, packet)) + { + int res= 0; + log_info("query for connection %d successefully parsed",connection_id); + res= command->execute(&net, connection_id); + delete command; + if (!res) + { + log_info("query for connection %d executed ok",connection_id); + } + else + { + log_info("query for connection %d executed err=%d",connection_id,res); + net_send_error(&net, res); + return 0; + } + } + else + { + net_send_error(&net,ER_OUT_OF_RESOURCES); + return 0; + } + break; + } + default: + log_info("query for connection %d received unknown command",connection_id); + net_send_error(&net, ER_UNKNOWN_COM_ERROR); + break; + } + return 0; +} + + +C_MODE_START + +pthread_handler_decl(mysql_connection, arg) +{ + Mysql_connection_thread_args *args= (Mysql_connection_thread_args *) arg; + Mysql_connection_thread mysql_connection_thread(*args); + delete args; + if (mysql_connection_thread.init()) + log_info("mysql_connection(): error initializing thread"); + else + { + mysql_connection_thread.run(); + mysql_connection_thread.cleanup(); + } + return 0; +} + +C_MODE_END + + +/* + vim: fdm=marker +*/ diff --git a/server-tools/instance-manager/mysql_connection.h b/server-tools/instance-manager/mysql_connection.h new file mode 100644 index 00000000000..225f4a352ce --- /dev/null +++ b/server-tools/instance-manager/mysql_connection.h @@ -0,0 +1,60 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_CONNECTION_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_CONNECTION_H +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include +#include + + +C_MODE_START + +pthread_handler_decl(mysql_connection, arg); + +C_MODE_END + + +class Thread_registry; +class User_map; +class Instance_map; +struct st_vio; + +struct Mysql_connection_thread_args +{ + struct st_vio *vio; + Thread_registry &thread_registry; + const User_map &user_map; + ulong connection_id; + Instance_map &instance_map; + + Mysql_connection_thread_args(struct st_vio *vio_arg, + Thread_registry &thread_registry_arg, + const User_map &user_map_arg, + ulong connection_id_arg, + Instance_map &instance_map_arg) : + vio(vio_arg) + ,thread_registry(thread_registry_arg) + ,user_map(user_map_arg) + ,connection_id(connection_id_arg) + ,instance_map(instance_map_arg) + {} +}; + +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_CONNECTION_H diff --git a/server-tools/instance-manager/mysql_manager_error.h b/server-tools/instance-manager/mysql_manager_error.h new file mode 100644 index 00000000000..2862e01649d --- /dev/null +++ b/server-tools/instance-manager/mysql_manager_error.h @@ -0,0 +1,27 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +/* Definefile for instance manager error messagenumbers */ + +#define ER_BAD_INSTANCE_NAME 3000 +#define ER_INSTANCE_IS_NOT_STARTED 3001 +#define ER_INSTANCE_ALREADY_STARTED 3002 +#define ER_CANNOT_START_INSTANCE 3003 +#define ER_STOP_INSTANCE 3004 + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H */ diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index 0ec7e043ed5..bd8f3c6b870 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 @@ -14,20 +14,21 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "manager.h" +#include "options.h" +#include "log.h" + #include #include #include #include #include -#include #include +#include -#include "manager.h" -#include "options.h" -#include "log.h" /* - Few notes about the Instance Manager architecture: + Few notes about Instance Manager architecture: Instance Manager consisits of two processes: the angel process, and the instance manager process. Responsibilities of the angel process is to monitor the instance manager process, and restart it in case of @@ -35,19 +36,19 @@ '--run-as-service' is provided. The Instance Manager process consists of several subsystems (thread sets): - - the signal handling thread: it's responsibilities are to handle + - the signal handling thread: it's responsibilities are to handle user signals and propogate them to the other threads. All other threads - are accounted in the signal handler thread Thread Repository. - - the listener: listens all sockets. There is a listening + are accounted in the signal handler thread Thread Registry. + - the listener: listens all sockets. There is a listening socket for each (mysql, http, snmp, rendezvous (?)) subsystem. - mysql subsystem: Instance Manager acts like an ordinary MySQL Server, - but with very restricted command set. Each MySQL client connection is + but with very restricted command set. Each MySQL client connection is handled in a separate thread. All MySQL client connections threads constitute mysql subsystem. - - http subsystem: it is also possible to talk with Instance Manager via + - http subsystem: it is also possible to talk with Instance Manager via http. One thread per http connection is used. Threads are pooled. - 'snmp' connections (FIXME: I know nothing about it yet) - - rendezvous threads + - rendezvous threads */ static void init_environment(char *progname); @@ -70,11 +71,12 @@ int main(int argc, char *argv[]) options.load(argc, argv); if (options.run_as_service) { + /* forks, and returns only in child */ daemonize(options.log_file_name); + /* forks again, and returns only in child: parent becomes angel */ angel(options); } - else - manager(options.log_file_name); + manager(options); return 0; } @@ -90,6 +92,7 @@ static void init_environment(char *progname) MY_INIT(progname); log_init(); umask(0117); + srand(time(0)); } @@ -109,8 +112,8 @@ static void daemonize(const char *log_file_name) int fd; /* Become a session leader: setsid must succeed because child is - guaranteed not to be a process group leader (it belongs to the - process group of the parent.) + guaranteed not to be a process group leader (it belongs to the + process group of the parent.) The goal is not to have a controlling terminal. */ setsid(); @@ -121,7 +124,7 @@ static void daemonize(const char *log_file_name) close(STDIN_FILENO); - fd= open(log_file_name, O_WRONLY | O_CREAT | O_APPEND | O_NOCTTY, + fd= open(log_file_name, O_WRONLY | O_CREAT | O_APPEND | O_NOCTTY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (fd < 0) die("daemonize(): failed to open log file %s, %s", log_file_name, @@ -133,7 +136,7 @@ static void daemonize(const char *log_file_name) /* TODO: chroot() and/or chdir() here */ break; - default: + default: /* successfully exit from parent */ exit(0); } @@ -144,13 +147,13 @@ enum { CHILD_OK= 0, CHILD_NEED_RESPAWN, CHILD_EXIT_ANGEL }; static volatile sig_atomic_t child_status= CHILD_OK; -/* +/* Signal handler for SIGCHLD: reap child, analyze child exit status, and set child_status appropriately. */ void reap_child(int __attribute__((unused)) signo) -{ +{ int child_exit_status; /* As we have only one child, no need to cycle waitpid */ if (waitpid(0, &child_exit_status, WNOHANG) > 0) @@ -159,16 +162,14 @@ void reap_child(int __attribute__((unused)) signo) child_status= CHILD_NEED_RESPAWN; else /* - As we reap_child is not called for SIGSTOP, we should be here only + As reap_child is not called for SIGSTOP, we should be here only if the child exited normally. */ child_status= CHILD_EXIT_ANGEL; } } -/* Not static to reuse it in childs */ - -volatile sig_atomic_t is_terminated= 0; +static volatile sig_atomic_t is_terminated= 0; /* Signal handler for terminate signals - SIGTERM, SIGHUP, SIGINT. @@ -215,18 +216,18 @@ spawn: pid_t pid= fork(); switch (pid) { case -1: - die("angel(): fork failed, %s", strerror(errno)); + die("angel(): fork failed, %s", strerror(errno)); case 0: // child, success /* restore default actions for signals to let the manager work with signals as he wishes - */ + */ sigaction(SIGCHLD, &sa_chld_out, 0); sigaction(SIGTERM, &sa_term_out, 0); sigaction(SIGINT, &sa_int_out, 0); sigaction(SIGHUP, &sa_hup_out, 0); - - manager(options.socket_file_name); + /* Here we return to main, and fall into manager */ + break; default: // parent, success while (child_status == CHILD_OK && is_terminated == 0) sigsuspend(&zeromask); @@ -235,12 +236,18 @@ spawn: log_info("angel got signal %d (%s), exiting", is_terminated, sys_siglist[is_terminated]); else if (child_status == CHILD_NEED_RESPAWN) - { + { child_status= CHILD_OK; log_error("angel(): mysqlmanager exited abnormally: respawning..."); sleep(1); /* don't respawn too fast */ goto spawn; } - /* mysqlmanager successfully exited, let's silently evaporate */ + /* + mysqlmanager successfully exited, let's silently evaporate + If we return to main we fall into the manager() function, so let's + simply exit(). + */ + exit(0); } } + diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 5bb4b180030..01d83e2d994 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 @@ -15,7 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef __GNUC__ -#pragma implementation +#pragma implementation #endif #include "options.h" @@ -24,14 +24,22 @@ #include #include +#include "priv.h" #define QUOTE2(x) #x #define QUOTE(x) QUOTE2(x) - + char Options::run_as_service; const char *Options::log_file_name= QUOTE(DEFAULT_LOG_FILE_NAME); const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME); const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME); +const char *Options::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME); +const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH); +const char *Options::default_admin_user= QUOTE(DEFAULT_USER); +const char *Options::default_admin_password= QUOTE(DEFAULT_PASSWORD); +const char *Options::bind_address= 0; /* No default value */ +uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL; +uint Options::port_number= DEFAULT_PORT; /* List of options, accepted by the instance manager. @@ -42,9 +50,18 @@ enum options { OPT_LOG= 256, OPT_PID_FILE, OPT_SOCKET, - OPT_RUN_AS_SERVICE + OPT_PASSWORD_FILE, + OPT_MYSQLD_PATH, + OPT_RUN_AS_SERVICE, + OPT_USER, + OPT_PASSWORD, + OPT_DEFAULT_ADMIN_USER, + OPT_DEFAULT_ADMIN_PASSWORD, + OPT_MONITORING_INTERVAL, + OPT_PORT, + OPT_BIND_ADDRESS }; - + static struct my_option my_long_options[] = { { "help", '?', "Display this help and exit.", @@ -57,11 +74,48 @@ static struct my_option my_long_options[] = { "pid-file", OPT_PID_FILE, "Pid file to use.", (gptr *) &Options::pid_file_name, (gptr *) &Options::pid_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - + { "socket", OPT_SOCKET, "Socket file to use for connection.", (gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "bind_address", OPT_BIND_ADDRESS, "Bind address to use for connection.", + (gptr *) &Options::bind_address, (gptr *) &Options::bind_address, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "port", OPT_PORT, "Port number to use for connections", + (gptr *) &Options::port_number, (gptr *) &Options::port_number, + 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "password-file", OPT_PASSWORD_FILE, "Look for Instane Manager users" + " and passwords here.", + (gptr *) &Options::password_file_name, + (gptr *) &Options::password_file_name, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "default_mysqld_path", OPT_MYSQLD_PATH, "Where to look for MySQL" + " Server binary.", + (gptr *) &Options::default_mysqld_path, (gptr *) &Options::default_mysqld_path, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "default_admin_user", OPT_DEFAULT_ADMIN_USER, "Username to shutdown MySQL" + " instances.", + (gptr *) &Options::default_admin_user, + (gptr *) &Options::default_admin_user, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "default_admin_password", OPT_DEFAULT_ADMIN_PASSWORD, "Password to" + "shutdown MySQL instances.", + (gptr *) &Options::default_admin_password, + (gptr *) &Options::default_admin_password, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "monitoring_interval", OPT_MONITORING_INTERVAL, "Interval to monitor instances" + " in seconds.", + (gptr *) &Options::monitoring_interval, + (gptr *) &Options::monitoring_interval, + 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "run-as-service", OPT_RUN_AS_SERVICE, "Daemonize and start angel process.", (gptr *) &Options::run_as_service, 0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 }, @@ -74,15 +128,26 @@ static struct my_option my_long_options[] = static void version() { - static const char mysqlmanager_version[] = "0.1-alpha"; - printf("%s Ver %s for %s on %s\n", my_progname, mysqlmanager_version, + printf("%s Ver %s for %s on %s\n", my_progname, mysqlmanager_version, SYSTEM_TYPE, MACHINE_TYPE); } + +static const char *default_groups[]= { "mysql", "manager", 0 }; + + static void usage() { version(); + + printf("Copyright (C) 2003, 2004 MySQL AB\n" + "This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n" + "and you are welcome to modify and redistribute it under the GPL license\n"); + printf("Usage: %s [OPTIONS] \n", my_progname); + my_print_help(my_long_options); + print_defaults("my", default_groups); + my_print_variables(my_long_options); } C_MODE_START @@ -107,9 +172,9 @@ get_one_option(int optid, C_MODE_END -/* +/* - call load_defaults to load configuration file section - - call handle_options to assign defaults and command-line arguments + - call handle_options to assign defaults and command-line arguments to the class members if either of these function fail, exit the program May not return. @@ -117,6 +182,9 @@ C_MODE_END void Options::load(int argc, char **argv) { + /* config-file options are prepended to command-line ones */ + load_defaults("my", default_groups, &argc, &argv); + if (int rc= handle_options(&argc, &argv, my_long_options, get_one_option)) exit(rc); } diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index b2eacf220d7..fc2b44c8b53 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -1,6 +1,6 @@ #ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H #define INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 @@ -24,13 +24,21 @@ Options - all possible options for the instance manager grouped in one struct. */ +#include -struct Options +struct Options { static char run_as_service; /* handle_options doesn't support bool */ 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; + static const char *default_admin_user; + static const char *default_admin_password; + static uint monitoring_interval; + static uint port_number; + static const char *bind_address; static void load(int argc, char **argv); }; diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc new file mode 100644 index 00000000000..09a60062946 --- /dev/null +++ b/server-tools/instance-manager/parse.cc @@ -0,0 +1,200 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#include "parse.h" +#include + +enum Token +{ + TOK_FLUSH = 0, + TOK_INSTANCE, + TOK_INSTANCES, + TOK_OPTIONS, + TOK_START, + TOK_STATUS, + TOK_STOP, + TOK_SHOW, + TOK_NOT_FOUND, // must be after all tokens + TOK_END +}; + +static const char *tokens[]= { + "FLUSH", + "INSTANCE", + "INSTANCES", + "OPTIONS", + "START", + "STATUS", + "STOP", + "SHOW", +}; + + +/* + tries to find next word in the text + if found, returns the beginning and puts word length to word_len argument. + if not found returns pointer to first non-space or to '\0', word_len == 0 +*/ + +inline void get_word(const char **text, uint *word_len) +{ + const char *word_end; + + /* skip space */ + while (my_isspace(default_charset_info, **text)) + ++(*text); + + word_end= *text; + + while (my_isalnum(default_charset_info, *word_end)) + ++word_end; + + *word_len= word_end - *text; +} + + +/* + Returns token no if word corresponds to some token, otherwise returns + TOK_NOT_FOUND +*/ + +inline Token find_token(const char *word, uint word_len) +{ + int i= 0; + do + { + if (strncasecmp(tokens[i], word, word_len) == 0) + break; + } + while (++i < TOK_NOT_FOUND); + return (Token) i; +} + + +Token get_token(const char **text, uint *word_len) +{ + get_word(text, word_len); + if (*word_len) + return find_token(*text, *word_len); + return TOK_END; +} + + +Token shift_token(const char **text, uint *word_len) +{ + Token save= get_token(text, word_len); + (*text)+= *word_len; + return save; +} + + +void print_token(const char *token, uint tok_len) +{ + for (uint i= 0; i < tok_len; ++i) + printf("%c", token[i]); +} + + +int get_text_id(const char **text, uint *word_len, const char **id) +{ + get_word(text, word_len); + if (word_len == 0) + return 1; + *id= *text; + return 0; +} + + +Command *parse_command(Command_factory *factory, const char *text) +{ + uint word_len; + const char *instance_name; + uint instance_name_len; + Command *command; + const char *saved_text= text; + + Token tok1= shift_token(&text, &word_len); + + switch (tok1) { + case TOK_START: // fallthrough + case TOK_STOP: + if (shift_token(&text, &word_len) != TOK_INSTANCE) + goto syntax_error; + get_word(&text, &word_len); + if (word_len == 0) + goto syntax_error; + instance_name= text; + instance_name_len= word_len; + text+= word_len; + /* it should be the end of command */ + get_word(&text, &word_len); + if (word_len) + goto syntax_error; + + command= (tok1 == TOK_START) ? (Command *) + factory->new_Start_instance(instance_name, instance_name_len): + (Command *) + factory->new_Stop_instance(instance_name, instance_name_len); + break; + case TOK_FLUSH: + if (shift_token(&text, &word_len) != TOK_INSTANCES) + goto syntax_error; + + get_word(&text, &word_len); + if (word_len) + goto syntax_error; + + command= factory->new_Flush_instances(); + break; + case TOK_SHOW: + switch (shift_token(&text, &word_len)) { + case TOK_INSTANCES: + get_word(&text, &word_len); + if (word_len) + goto syntax_error; + command= factory->new_Show_instances(); + break; + case TOK_INSTANCE: + switch (Token tok2= shift_token(&text, &word_len)) { + case TOK_OPTIONS: + case TOK_STATUS: + get_text_id(&text, &instance_name_len, &instance_name); + text+= instance_name_len; + get_word(&text, &word_len); + if (word_len) + goto syntax_error; + command= (tok2 == TOK_STATUS) ? (Command *) + factory->new_Show_instance_status(instance_name, + instance_name_len): + (Command *) + factory->new_Show_instance_options(instance_name, + instance_name_len); + break; + default: + goto syntax_error; + } + break; + default: + goto syntax_error; + } + break; + default: +syntax_error: + command= factory->new_Syntax_error(); + } + return command; +} + diff --git a/server-tools/instance-manager/parse.h b/server-tools/instance-manager/parse.h new file mode 100644 index 00000000000..236a9bee53a --- /dev/null +++ b/server-tools/instance-manager/parse.h @@ -0,0 +1,23 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 */ + +#include "factory.h" + +Command *parse_command(Command_factory *factory, const char *text); + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H */ diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc new file mode 100644 index 00000000000..e449df9f540 --- /dev/null +++ b/server-tools/instance-manager/priv.cc @@ -0,0 +1,34 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +#include "priv.h" + +const char mysqlmanager_version[] = "0.2-alpha"; + +const int mysqlmanager_version_length= sizeof(mysqlmanager_version) - 1; + +const unsigned char protocol_version= PROTOCOL_VERSION; + +unsigned long net_buffer_length= 16384; + +unsigned long max_allowed_packet= 16384; + +unsigned long net_read_timeout= 30; // same as in mysqld + +unsigned long net_write_timeout= 60; // same as in mysqld + +unsigned long net_retry_count= 10; // same as in mysqld + diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h new file mode 100644 index 00000000000..73ee87552c8 --- /dev/null +++ b/server-tools/instance-manager/priv.h @@ -0,0 +1,60 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +extern const char mysqlmanager_version[]; +extern const int mysqlmanager_version_length; + +/* MySQL client-server protocol version: substituted from configure */ +extern const unsigned char protocol_version; + +/* + These variables are used in MySQL subsystem to work with mysql clients + To be moved to a config file/options one day. +*/ + + +/* Buffer length for TCP/IP and socket communication */ +extern unsigned long net_buffer_length; + + +/* Maximum allowed incoming/ougoung packet length */ +extern unsigned long max_allowed_packet; + + +/* + Number of seconds to wait for more data from a connection before aborting + the read +*/ +extern unsigned long net_read_timeout; + + +/* + Number of seconds to wait for a block to be written to a connection + before aborting the write. +*/ +extern unsigned long net_write_timeout; + + +/* + If a read on a communication port is interrupted, retry this many times + before giving up. +*/ +extern unsigned long net_retry_count; + + +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc new file mode 100644 index 00000000000..6bb6c6d2fa0 --- /dev/null +++ b/server-tools/instance-manager/protocol.cc @@ -0,0 +1,171 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +#include +#include +#include +#include + +#include "messages.h" +#include "protocol.h" + +static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */ + +int net_send_ok(struct st_net *net, unsigned long connection_id) +{ + char buff[1 + // packet type code + 9 + // affected rows count + 9 + // connection id + 2 + // thread return status + 2]; // warning count + + char *pos= buff; + enum { OK_PACKET_CODE= 0 }; + *pos++= OK_PACKET_CODE; + pos= net_store_length(pos, (ulonglong) 0); + pos= net_store_length(pos, (ulonglong) connection_id); + int2store(pos, *net->return_status); + pos+= 2; + /* We don't support warnings, so store 0 for total warning count */ + int2store(pos, 0); + pos+= 2; + + return my_net_write(net, buff, pos - buff) || net_flush(net); +} + + +int net_send_error(struct st_net *net, uint sql_errno) +{ + const char *err= message(sql_errno); + char buff[1 + // packet type code + 2 + // sql error number + 1 + SQLSTATE_LENGTH + // sql state + MYSQL_ERRMSG_SIZE]; // message + char *pos= buff; + + enum { ERROR_PACKET_CODE= 255 }; + *pos++= ERROR_PACKET_CODE; + int2store(pos, sql_errno); + pos+= 2; + /* The first # is to make the protocol backward compatible */ + *pos++= '#'; + memcpy(pos, errno_to_sqlstate(sql_errno), SQLSTATE_LENGTH); + pos+= SQLSTATE_LENGTH; + pos= strmake(pos, err, MYSQL_ERRMSG_SIZE - 1) + 1; + return my_net_write(net, buff, pos - buff) || net_flush(net); +} + + +int net_send_error_323(struct st_net *net, uint sql_errno) +{ + const char *err= message(sql_errno); + char buff[1 + // packet type code + 2 + // sql error number + MYSQL_ERRMSG_SIZE]; // message + char *pos= buff; + + enum { ERROR_PACKET_CODE= 255 }; + *pos++= ERROR_PACKET_CODE; + int2store(pos, sql_errno); + pos+= 2; + pos= strmake(pos, err, MYSQL_ERRMSG_SIZE - 1) + 1; + return my_net_write(net, buff, pos - buff) || net_flush(net); +} + +char *net_store_length(char *pkg, uint length) +{ + uchar *packet=(uchar*) pkg; + if (length < 251) + { + *packet=(uchar) length; + return (char*) packet+1; + } + *packet++=252; + int2store(packet,(uint) length); + return (char*) packet+2; +} + + +void store_to_string(Buffer *buf, const char *string, uint *position) +{ + char* currpos; + uint string_len; + + string_len= strlen(string); + buf->check_and_add(*position, 2); + currpos= net_store_length(buf->buffer + *position, string_len); + buf->put_to_buffer(currpos, string, string_len); + *position= *position + string_len + (currpos - buf->buffer - *position); +} + + +int send_eof(struct st_net *net) +{ + char buff[1 + /* eof packet code */ + 2 + /* warning count */ + 2]; /* server status */ + + buff[0]=254; + int2store(buff+1, 0); + int2store(buff+3, 0); + return my_net_write(net, buff, sizeof buff); +} + +int send_fields(struct st_net *net, LIST *fields) +{ + LIST *tmp= fields; + Buffer send_buff; + char small_buff[4]; + uint position= 0; + NAME_WITH_LENGTH *field; + + /* send the number of fileds */ + net_store_length(small_buff, (uint) list_length(fields)); + my_net_write(net, small_buff, (uint) 1); + + while (tmp) + { + position= 0; + field= (NAME_WITH_LENGTH *) tmp->data; + + store_to_string(&send_buff, (char *) "", &position); /* catalog name */ + store_to_string(&send_buff, (char *) "", &position); /* db name */ + store_to_string(&send_buff, (char *) "", &position); /* table name */ + store_to_string(&send_buff, (char *) "", &position); /* table name alias */ + store_to_string(&send_buff, field->name, &position); /* column name */ + store_to_string(&send_buff, field->name, &position); /* column name alias */ + send_buff.check_and_add(position, 12); + send_buff.buffer[position++]= 12; + int2store(send_buff.buffer + position, 1); /* charsetnr */ + int4store(send_buff.buffer + position + 2, field->length); /* field length */ + send_buff.buffer[position+6]= FIELD_TYPE_STRING; /* type */ + int2store(send_buff.buffer + position + 7, 0); /* flags */ + send_buff.buffer[position + 9]= (char) 0; /* decimals */ + send_buff.buffer[position + 10]= 0; + send_buff.buffer[position + 11]= 0; + position+= 12; + if (my_net_write(net, send_buff.buffer, (uint) position+1)) + goto err; + tmp= rest(tmp); + } + + if ( my_net_write(net, eof_buff, 1)) + goto err; + return 0; + +err: + return 1; +} diff --git a/server-tools/instance-manager/protocol.h b/server-tools/instance-manager/protocol.h new file mode 100644 index 00000000000..7bce0e35b5b --- /dev/null +++ b/server-tools/instance-manager/protocol.h @@ -0,0 +1,43 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PROTOCOL_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_PROTOCOL_H +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +#include "buffer.h" +#include + +typedef struct field { + char *name; + uint length; +} NAME_WITH_LENGTH; + +struct st_net; + +int net_send_ok(struct st_net *net, unsigned long connection_id); + +int net_send_error(struct st_net *net, unsigned sql_errno); + +int net_send_error_323(struct st_net *net, unsigned sql_errno); + +int send_fields(struct st_net *net, LIST *fields); + +char *net_store_length(char *pkg, uint length); + +void store_to_string(Buffer *buf, const char *string, uint *position); + +int send_eof(struct st_net *net); + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PROTOCOL_H */ diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc new file mode 100644 index 00000000000..4037da71880 --- /dev/null +++ b/server-tools/instance-manager/thread_registry.cc @@ -0,0 +1,206 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "thread_registry.h" + +#include +#include +#include +#include "log.h" + + +/* Kick-off signal handler */ + +enum { THREAD_KICK_OFF_SIGNAL= SIGUSR2 }; + +static void handle_signal(int __attribute__((unused)) sig_no) +{ +} + + +/* + TODO: think about moving signal information (now it's shutdown_in_progress) + to Thread_info. It will reduce contention and allow signal deliverence to + a particular thread, not to the whole worker crew +*/ + +Thread_registry::Thread_registry() : + shutdown_in_progress(false) + ,sigwait_thread_pid(pthread_self()) +{ + pthread_mutex_init(&LOCK_thread_registry, 0); + pthread_cond_init(&COND_thread_registry_is_empty, 0); + + /* head is used by-value to simplify nodes inserting */ + head.next= head.prev= &head; +} + + +Thread_registry::~Thread_registry() +{ + /* Check that no one uses the repository. */ + pthread_mutex_lock(&LOCK_thread_registry); + + /* All threads must unregister */ + DBUG_ASSERT(head.next == &head); + + pthread_mutex_unlock(&LOCK_thread_registry); + pthread_cond_destroy(&COND_thread_registry_is_empty); + pthread_mutex_destroy(&LOCK_thread_registry); +} + + +/* + Set signal handler for kick-off thread, and insert a thread info to the + repository. New node is appended to the end of the list; head.prev always + points to the last node. +*/ + +void Thread_registry::register_thread(Thread_info *info) +{ + struct sigaction sa; + sa.sa_handler= handle_signal; + sa.sa_flags= 0; + sigemptyset(&sa.sa_mask); + sigaction(THREAD_KICK_OFF_SIGNAL, &sa, 0); + + info->current_cond= 0; + + pthread_mutex_lock(&LOCK_thread_registry); + info->next= &head; + info->prev= head.prev; + head.prev->next= info; + head.prev= info; + pthread_mutex_unlock(&LOCK_thread_registry); +} + + +/* + Unregister a thread from the repository and free Thread_info structure. + Every registered thread must unregister. Unregistering should be the last + thing a thread is doing, otherwise it could have no time to finalize. +*/ + +void Thread_registry::unregister_thread(Thread_info *info) +{ + pthread_mutex_lock(&LOCK_thread_registry); + info->prev->next= info->next; + info->next->prev= info->prev; + if (head.next == &head) + pthread_cond_signal(&COND_thread_registry_is_empty); + pthread_mutex_unlock(&LOCK_thread_registry); +} + + +/* + Check whether shutdown is in progress, and if yes, return immediately. + Else set info->current_cond and call pthread_cond_wait. When + pthread_cond_wait returns, unregister current cond and check the shutdown + status again. + RETURN VALUE + return value from pthread_cond_wait +*/ + +int Thread_registry::cond_wait(Thread_info *info, pthread_cond_t *cond, + pthread_mutex_t *mutex, bool *is_shutdown) +{ + pthread_mutex_lock(&LOCK_thread_registry); + *is_shutdown= shutdown_in_progress; + if (*is_shutdown) + { + pthread_mutex_unlock(&LOCK_thread_registry); + return 0; + } + info->current_cond= cond; + pthread_mutex_unlock(&LOCK_thread_registry); + /* sic: race condition here, cond can be signaled in deliver_shutdown */ + int rc= pthread_cond_wait(cond, mutex); + pthread_mutex_lock(&LOCK_thread_registry); + info->current_cond= 0; + *is_shutdown= shutdown_in_progress; + pthread_mutex_unlock(&LOCK_thread_registry); + return rc; +} + + +/* + Deliver shutdown message to the workers crew. + As it's impossible to avoid all race conditions, signal latecomers + again. +*/ + +void Thread_registry::deliver_shutdown() +{ + struct timespec shutdown_time; + set_timespec(shutdown_time, 1); + + pthread_mutex_lock(&LOCK_thread_registry); + shutdown_in_progress= true; + + /* to stop reading from the network we need to flush alarm queue */ + end_thr_alarm(0); + /* + We have to deliver final alarms this way, as the main thread has already + stopped alarm processing. + */ + process_alarm(THR_SERVER_ALARM); + for (Thread_info *info= head.next; info != &head; info= info->next) + { + pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL); + /* + sic: race condition here, the thread may not yet fall into + pthread_cond_wait. + */ + if (info->current_cond) + pthread_cond_signal(info->current_cond); + } + /* + The common practice is to test predicate before pthread_cond_wait. + I don't do that here because the predicate is practically always false + before wait - is_shutdown's been just set, and the lock's still not + released - the only case when the predicate is false is when no other + threads exist. + */ + while (pthread_cond_timedwait(&COND_thread_registry_is_empty, + &LOCK_thread_registry, + &shutdown_time) != ETIMEDOUT && + head.next != &head) + ; + /* + If previous signals did not reach some threads, they must be sleeping + in pthread_cond_wait or in a blocking syscall. Wake them up: + every thread shall check signal variables after each syscall/cond_wait, + so this time everybody should be informed (presumably each worker can + get CPU during shutdown_time.) + */ + for (Thread_info *info= head.next; info != &head; info= info->next) + { + pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL); + if (info->current_cond) + pthread_cond_signal(info->current_cond); + } + pthread_mutex_unlock(&LOCK_thread_registry); +} + + +void Thread_registry::request_shutdown() +{ + pthread_kill(sigwait_thread_pid, SIGTERM); +} diff --git a/server-tools/instance-manager/thread_registry.h b/server-tools/instance-manager/thread_registry.h new file mode 100644 index 00000000000..0836f44345d --- /dev/null +++ b/server-tools/instance-manager/thread_registry.h @@ -0,0 +1,116 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REGISTRY_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REGISTRY_H +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +/* + A multi-threaded application shall nicely work with signals. + + This means it shall, first of all, shut down nicely on ``quit'' signals: + stop all running threads, cleanup and exit. + + Note, that a thread can't be shut down nicely if it doesn't want to be. + That's why to perform clean shutdown, all threads consituting a process + must observe certain rules. Here we use the rules, described in Butenhof + book 'Programming with POSIX threads', namely: + - all user signals are handled in 'signal thread' in synchronous manner + (by means of sigwait). To guarantee that the signal thread is the only who + can receive user signals, all threads block them, and signal thread is + the only who calls sigwait() with an apporpriate sigmask. + To propogate a signal to the workers the signal thread sets + a variable, corresponding to the signal. Additionally the signal thread + sends each worker an internal signal (by means of pthread_kill) to kick it + out from possible blocking syscall, and possibly pthread_cond_signal if + some thread is blocked in pthread_cond_[timed]wait. + - a worker handles only internal 'kick' signal (the handler does nothing). + In case when a syscall returns 'EINTR' the worker checks all + signal-related variables and behaves accordingly. + Also these variables shall be checked from time to time in long + CPU-bounded operations, and before/after pthread_cond_wait. (It's supposed + that a worker thread either waits in a syscall/conditional variable, or + computes something.) + - to guarantee signal deliverence, there should be some kind of feedback, + e. g. all workers shall account in the signal thread Thread Repository and + unregister from it on exit. + + Configuration reload (on SIGHUP) and thread timeouts/alarms can be handled + in manner, similar to ``quit'' signals. +*/ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include +#include + + +/* + Thread_info - repository entry for each worker thread + All entries comprise double-linked list like: + 0 -- entry -- entry -- entry - 0 + Double-linked list is used to unregister threads easy. +*/ + +class Thread_info +{ + pthread_cond_t *current_cond; + Thread_info *prev, *next; + pthread_t thread_id; + Thread_info() {} + friend class Thread_registry; +public: + Thread_info(pthread_t thread_id_arg) : thread_id(thread_id_arg) {} +}; + + +/* + Thread_registry - contains handles for each worker thread to deliver + signal information to workers. +*/ + +class Thread_registry +{ +public: + Thread_registry(); + ~Thread_registry(); + + void register_thread(Thread_info *info); + void unregister_thread(Thread_info *info); + void deliver_shutdown(); + void request_shutdown(); + inline bool is_shutdown(); + int cond_wait(Thread_info *info, pthread_cond_t *cond, + pthread_mutex_t *mutex, bool *is_shutdown); +private: + Thread_info head; + bool shutdown_in_progress; + pthread_mutex_t LOCK_thread_registry; + pthread_cond_t COND_thread_registry_is_empty; + pid_t sigwait_thread_pid; +}; + + +inline bool Thread_registry::is_shutdown() +{ + pthread_mutex_lock(&LOCK_thread_registry); + bool res= shutdown_in_progress; + pthread_mutex_unlock(&LOCK_thread_registry); + return res; +} + + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REGISTRY_H */ diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc new file mode 100644 index 00000000000..7fff324a521 --- /dev/null +++ b/server-tools/instance-manager/user_map.cc @@ -0,0 +1,172 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include "user_map.h" + +#include +#include + +#include "log.h" + +struct User +{ + char user[USERNAME_LENGTH + 1]; + uint8 user_length; + uint8 salt[SCRAMBLE_LENGTH]; + int init(const char *line); +}; + + +int User::init(const char *line) +{ + const char *name_begin, *name_end, *password; + + if (line[0] == '\'' || line[0] == '"') + { + name_begin= line + 1; + name_end= strchr(name_begin, line[0]); + if (name_end == 0 || name_end[1] != ':') + goto err; + password= name_end + 2; + } + else + { + name_begin= line; + name_end= strchr(name_begin, ':'); + if (name_end == 0) + goto err; + password= name_end + 1; + } + user_length= name_end - name_begin; + if (user_length > USERNAME_LENGTH) + goto err; + + /* assume that newline characater is present */ + if (strlen(password) != SCRAMBLED_PASSWORD_CHAR_LENGTH + 1) + goto err; + + memcpy(user, name_begin, user_length); + user[user_length]= 0; + get_salt_from_password(salt, password); + log_info("loaded user %s", user); + + return 0; +err: + log_error("error parsing user and password at line %d", line); + return 1; +} + + +C_MODE_START + +static byte* get_user_key(const byte* u, uint* len, + my_bool __attribute__((unused)) t) +{ + const User *user= (const User *) u; + *len= user->user_length; + return (byte *) user->user; +} + +static void delete_user(void *u) +{ + User *user= (User *) u; + delete user; +} + +C_MODE_END + + +User_map::User_map() +{ + enum { START_HASH_SIZE = 16 }; + hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, + get_user_key, delete_user, 0); +} + +User_map::~User_map() +{ + hash_free(&hash); +} + + +/* + Load all users from the password file. Must be called once right after + construction. + In case of failure, puts error message to the log file and returns 1 +*/ + +int User_map::load(const char *password_file_name) +{ + FILE *file; + char line[USERNAME_LENGTH + SCRAMBLED_PASSWORD_CHAR_LENGTH + + 2 + /* for possible quotes */ + 1 + /* for ':' */ + 1 + /* for newline */ + 1]; /* for trailing zero */ + uint line_length; + User *user; + int rc= 1; + + if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0) + { + log_error("can't open password file %s: errno=%d, %s", password_file_name, + errno, strerror(errno)); + return 1; + } + + while (fgets(line, sizeof(line), file)) + { + /* skip comments and empty lines */ + if (line[0] == '#' || line[0] == '\n' && line[1] == '\0') + continue; + if ((user= new User) == 0) + goto done; + if (user->init(line) || my_hash_insert(&hash, (byte *) user)) + goto err_init_user; + } + if (feof(file)) + rc= 0; + goto done; +err_init_user: + delete user; +done: + my_fclose(file, MYF(0)); + return rc; +} + + +/* + Check if user exists and password is correct + RETURN VALUE + 0 - user found and password OK + 1 - password mismatch + 2 - user not found +*/ + +int User_map::authenticate(const char *user_name, uint length, + const char *scrambled_password, + const char *scramble) const +{ + const User *user= (const User *) hash_search((HASH *) &hash, + (byte *) user_name, length); + if (user) + return check_scramble(scrambled_password, scramble, user->salt); + return 2; +} diff --git a/server-tools/instance-manager/user_map.h b/server-tools/instance-manager/user_map.h new file mode 100644 index 00000000000..acee0b3c02b --- /dev/null +++ b/server-tools/instance-manager/user_map.h @@ -0,0 +1,45 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include +#include +#include + +/* + User_map -- all users and passwords +*/ + +class User_map +{ +public: + User_map(); + ~User_map(); + + int load(const char *password_file_name); + int authenticate(const char *user_name, uint length, + const char *scrambled_password, + const char *scramble) const; +private: + HASH hash; +}; + +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 82a3b1bd520..78d254fccb1 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -78,11 +78,11 @@ my_bool net_flush(NET *net); can't normally do this the client should have a bigger max_allowed_packet. */ -#if defined(__WIN__) || !defined(MYSQL_SERVER) +#if (defined(__WIN__) || (!defined(MYSQL_SERVER) && !defined(MYSQL_INSTANCE_MANAGER))) /* The following is because alarms doesn't work on windows. */ #define NO_ALARM #endif - + #ifndef NO_ALARM #include "my_pthread.h" void sql_print_error(const char *format,...); @@ -665,6 +665,13 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed, } #endif /* NO_ALARM */ +/* + If we are inside of the instance manageer, we need to simulate mysql + server for the following function. +*/ +#ifdef MYSQL_INSTANCE_MANAGER +#define MYSQL_SERVER +#endif /* Reads one packet to net->buff + net->where_b @@ -854,6 +861,9 @@ end: return(len); } +#ifdef MYSQL_INSTANCE_MANAGER +#undef MYSQL_SERVER +#endif /* Read a packet from the client/server and return it without the internal From a3d9a1eb066d7cca01bef104d065864f2a7c65ec Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 25 Oct 2004 14:23:31 +0400 Subject: [PATCH 02/11] minor post review fixes server-tools/instance-manager/buffer.cc: function renames server-tools/instance-manager/buffer.h: function renames server-tools/instance-manager/command.cc: unecessary headers removed server-tools/instance-manager/command.h: cleanup server-tools/instance-manager/commands.cc: cleanup server-tools/instance-manager/commands.h: cleanup server-tools/instance-manager/guardian.cc: cleanup server-tools/instance-manager/instance.cc: cleanup server-tools/instance-manager/instance_options.cc: cleanup server-tools/instance-manager/instance_options.h: cleanup server-tools/instance-manager/listener.cc: cleanup server-tools/instance-manager/manager.cc: cleanup server-tools/instance-manager/protocol.cc: cleanup --- server-tools/instance-manager/buffer.cc | 20 +++++----- server-tools/instance-manager/buffer.h | 4 +- server-tools/instance-manager/command.cc | 10 ----- server-tools/instance-manager/command.h | 4 +- server-tools/instance-manager/commands.cc | 12 +++--- server-tools/instance-manager/commands.h | 3 -- server-tools/instance-manager/guardian.cc | 39 ++++++++----------- server-tools/instance-manager/instance.cc | 4 +- .../instance-manager/instance_options.cc | 2 +- .../instance-manager/instance_options.h | 9 +++-- server-tools/instance-manager/listener.cc | 11 +++--- server-tools/instance-manager/manager.cc | 37 +++++++++++------- server-tools/instance-manager/protocol.cc | 6 +-- 13 files changed, 77 insertions(+), 84 deletions(-) diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index 39e255300cf..66267e4af18 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -26,15 +26,15 @@ Puts the given string to the buffer. SYNOPSYS - put_to_buffer() - start_pos start position in the buffer + append() + position start position in the buffer string string to be put in the buffer len_arg the length of the string. This way we can avoid some strlens. DESCRIPTION - The method puts a string into the buffer, starting from start_pos . + The method puts a string into the buffer, starting from position . In the case when the buffer is too small it reallocs the buffer. The total size of the buffer is restricted with 16. @@ -43,12 +43,12 @@ 1 - The buffer came to 16Mb barrier */ -int Buffer::put_to_buffer(char *start_pos, const char *string, uint len_arg) +int Buffer::append(char *position, const char *string, uint len_arg) { - if (check_and_add(start_pos - buffer, len_arg)) + if (reserve(position - buffer, len_arg)) return 1; - strnmov(start_pos, string, len_arg); + strnmov(position, string, len_arg); return 0; } @@ -58,7 +58,7 @@ int Buffer::put_to_buffer(char *start_pos, const char *string, uint len_arg) "len_arg" starting from "position" and reallocs it if no. SYNOPSYS - check_and_add() + reserve() position the number starting byte on the buffer to store a buffer len_arg the length of the string. @@ -74,7 +74,7 @@ int Buffer::put_to_buffer(char *start_pos, const char *string, uint len_arg) 1 - The buffer came to 16Mb barrier */ -int Buffer::check_and_add(uint position, uint len_arg) +int Buffer::reserve(uint position, uint len_arg) { if (position + len_arg >= MAX_BUFFER_SIZE) return 1; @@ -83,9 +83,9 @@ int Buffer::check_and_add(uint position, uint len_arg) { buffer= (char *) realloc(buffer, min(MAX_BUFFER_SIZE, - max((uint) buffer_size*1.5, + max((uint) (buffer_size*1.5), position + len_arg))); - buffer_size= (uint) buffer_size*1.5; + buffer_size= (uint) (buffer_size*1.5); } return 0; } diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h index 8781abebd71..dbf72e34bf0 100644 --- a/server-tools/instance-manager/buffer.h +++ b/server-tools/instance-manager/buffer.h @@ -50,8 +50,8 @@ public: public: char *buffer; - int put_to_buffer(char *start_pos, const char *string, uint len_arg); - int check_and_add(uint position, uint len_arg); + int append(char *start_pos, const char *string, uint len_arg); + int reserve(uint position, uint len_arg); }; #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H */ diff --git a/server-tools/instance-manager/command.cc b/server-tools/instance-manager/command.cc index 71415a038f0..87260f9e17b 100644 --- a/server-tools/instance-manager/command.cc +++ b/server-tools/instance-manager/command.cc @@ -20,16 +20,6 @@ #include "command.h" -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "protocol.h" -#include "instance_map.h" Command::Command(Command_factory *factory_arg) :factory(factory_arg) diff --git a/server-tools/instance-manager/command.h b/server-tools/instance-manager/command.h index 25b418ca8fe..9b981ecd163 100644 --- a/server-tools/instance-manager/command.h +++ b/server-tools/instance-manager/command.h @@ -22,7 +22,7 @@ #include -/* Class responsible for allocation and deallocation of im classes. */ +/* Class responsible for allocation of im commands. */ class Command_factory; @@ -44,6 +44,4 @@ protected: Command_factory *factory; }; -#define CONST_STR(a) String(a,sizeof(a),&my_charset_latin1) - #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H */ diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index e990f04216d..30d8f0794a0 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -54,11 +54,12 @@ Show_instance_status::Show_instance_status(Command_factory *factory, Instance *instance; /* we make a search here, since we don't want t store the name */ - if (instance= (factory->instance_map).find(name, len)) + if (instance= factory->instance_map.find(name, len)) { instance_name= instance->options.instance_name; } - else instance_name= NULL; + else + instance_name= NULL; } @@ -90,7 +91,8 @@ Show_instance_options::Show_instance_options(Command_factory *factory, { instance_name= instance->options.instance_name; } - else instance_name= NULL; + else + instance_name= NULL; } @@ -116,7 +118,7 @@ Start_instance::Start_instance(Command_factory *factory, :Command(factory) { /* we make a search here, since we don't want t store the name */ - if (instance= (factory->instance_map).find(name, len)) + if (instance= factory->instance_map.find(name, len)) instance_name= instance->options.instance_name; } @@ -150,7 +152,7 @@ Stop_instance::Stop_instance(Command_factory *factory, :Command(factory) { /* we make a search here, since we don't want t store the name */ - if (instance= (factory->instance_map).find(name, len)) + if (instance= factory->instance_map.find(name, len)) instance_name= instance->options.instance_name; } diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h index bab61f04b17..09df4fc9260 100644 --- a/server-tools/instance-manager/commands.h +++ b/server-tools/instance-manager/commands.h @@ -120,9 +120,6 @@ public: class Syntax_error : public Command { public: - Syntax_error() - {} - int execute(struct st_net *net, ulong connection_id); }; diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index ebc59d67906..f13b98cbf20 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -70,16 +70,13 @@ Guardian_thread::~Guardian_thread() Check for all guarded instances and restart them if needed. If everything is fine go and sleep for some time. - - RETURN - The function return no value */ void Guardian_thread::run() { Instance *instance; LIST *loop; - int i=0; + int i= 0; my_thread_init(); @@ -90,11 +87,8 @@ void Guardian_thread::run() while (loop != NULL) { instance= (Instance *) loop->data; - if (instance != NULL) - { - if (!instance->is_running()) - instance->start(); - } + /* instance-> start already checks whether instance is running */ + instance->start(); loop= loop->next; } pthread_mutex_unlock(&LOCK_guardian); @@ -124,17 +118,18 @@ void Guardian_thread::run() int Guardian_thread::guard(const char *instance_name, uint name_len) { - LIST *lst; + LIST *node; Instance *instance; - lst= (LIST *) alloc_root(&alloc, sizeof(LIST)); - if (lst == NULL) return 1; + node= (LIST *) alloc_root(&alloc, sizeof(LIST)); + if (node == NULL) + return 1; instance= instance_map->find(instance_name, name_len); /* we store the pointers to instances from the instance_map's MEM_ROOT */ - lst->data= (void *) instance; + node->data= (void *) instance; pthread_mutex_lock(&LOCK_guardian); - guarded_instances= list_add(guarded_instances, lst); + guarded_instances= list_add(guarded_instances, node); pthread_mutex_unlock(&LOCK_guardian); return 0; @@ -150,28 +145,28 @@ int Guardian_thread::guard(const char *instance_name, uint name_len) int Guardian_thread::stop_guard(const char *instance_name, uint name_len) { - LIST *lst; + LIST *node; Instance *instance; instance= instance_map->find(instance_name, name_len); - lst= guarded_instances; - if (lst == NULL) return 1; - pthread_mutex_lock(&LOCK_guardian); - while (lst != NULL) + node= guarded_instances; + + while (node != NULL) { /* We compare only pointers, as we always use pointers from the instance_map's MEM_ROOT. */ - if ((Instance *) lst->data == instance) + if ((Instance *) node->data == instance) { - guarded_instances= list_delete(guarded_instances, lst); + guarded_instances= list_delete(guarded_instances, node); pthread_mutex_unlock(&LOCK_guardian); return 0; } - else lst= lst->next; + else + node= node->next; } pthread_mutex_unlock(&LOCK_guardian); /* if there is nothing to delete it is also fine */ diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 32e77429572..689cea2d786 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -49,8 +49,8 @@ int Instance::start() exit(0); /* parent goes bye-bye */ else { - execv(options.mysqld_path, options.argv); - exit(1); + execv(options.mysqld_path, options.argv); + exit(1); } case -1: return ER_CANNOT_START_INSTANCE; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 7586566de9d..8311e4f7bc0 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -172,7 +172,7 @@ err: int Instance_options::add_to_argv(const char* option) { - DBUG_ASSERT(filled_default_options < (MAX_NUMBER_OF_DEFAULT_OPTIONS + 1)); + DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS); if (option != NULL) argv[filled_default_options++]= (char *) option; diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 5034e775cd0..5bc46497d2a 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -36,9 +36,10 @@ class Instance_options { public: - Instance_options() : mysqld_socket(0), mysqld_datadir(0), - mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), - mysqld_user(0), mysqld_password(0), is_guarded(0), filled_default_options(0) + Instance_options() : + mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0), + mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), mysqld_user(0), + mysqld_password(0), is_guarded(0), filled_default_options(0) {} ~Instance_options(); /* fills in argv */ @@ -50,7 +51,7 @@ public: int init(const char *instance_name_arg); public: - enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 3 }; + enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 1 }; enum { MEM_ROOT_BLOCK_SIZE= 512 }; char **argv; /* We need the some options, so we store them as a separate pointers */ diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 749a31ea525..ddd03726917 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -78,9 +78,9 @@ Listener_thread::~Listener_thread() void Listener_thread::run() { - enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size + enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size int flags; - int arg= 1; /* value to be set by setsockopt */ + int arg= 1; /* value to be set by setsockopt */ /* I. prepare 'listen' sockets */ int ip_socket= socket(AF_INET, SOCK_STREAM, 0); @@ -93,7 +93,7 @@ void Listener_thread::run() } struct sockaddr_in ip_socket_address; - memset(&ip_socket_address, 0, sizeof(ip_socket_address)); + bzero(&ip_socket_address, sizeof(ip_socket_address)); ulong im_bind_addr; if (options.bind_address != 0) @@ -101,7 +101,8 @@ void Listener_thread::run() if ((im_bind_addr= (ulong) inet_addr(options.bind_address)) == INADDR_NONE) im_bind_addr= htonl(INADDR_ANY); } - else im_bind_addr= htonl(INADDR_ANY); + else + im_bind_addr= htonl(INADDR_ANY); uint im_port= options.port_number; ip_socket_address.sin_family= AF_INET; @@ -144,7 +145,7 @@ void Listener_thread::run() } struct sockaddr_un unix_socket_address; - memset(&unix_socket_address, 0, sizeof(unix_socket_address)); + bzero(&unix_socket_address, sizeof(unix_socket_address)); unix_socket_address.sun_family= AF_UNIX; strmake(unix_socket_address.sun_path, options.socket_file_name, diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index cb51197d52a..11a3289adbf 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -30,6 +30,23 @@ #include "log.h" #include "guardian.h" +static int create_pid_file(const char *pid_file_name) +{ + if (FILE *pid_file= my_fopen(pid_file_name, + O_WRONLY | O_CREAT | O_BINARY, MYF(0))) + { + fprintf(pid_file, "%d\n", (int) getpid()); + my_fclose(pid_file, MYF(0)); + } + else + { + log_error("can't create pid file %s: errno=%d, %s", + pid_file_name, errno, strerror(errno)); + return 1; + } + return 0; +} + /* manager - entry point to the main instance manager process: start @@ -53,32 +70,24 @@ void manager(const Options &options) &instance_map, options.monitoring_interval); + Listener_thread_args listener_args(thread_registry, options, user_map, + instance_map); + instance_map.mysqld_path= options.default_mysqld_path; instance_map.user= options.default_admin_user; instance_map.password= options.default_admin_password; instance_map.guardian= &guardian_thread; - instance_map.load(); - Listener_thread_args listener_args(thread_registry, options, user_map, - instance_map); + if (instance_map.load()) + return; if (user_map.load(options.password_file_name)) return; /* write pid file */ - if (FILE *pid_file= my_fopen(options.pid_file_name, - O_WRONLY | O_CREAT | O_BINARY, MYF(0))) - { - fprintf(pid_file, "%d\n", (int) getpid()); - my_fclose(pid_file, MYF(0)); - } - else - { - log_error("can't create pid file %s: errno=%d, %s", - options.pid_file_name, errno, strerror(errno)); + if (create_pid_file(options.pid_file_name)) return; - } /* block signals */ sigset_t mask; diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc index 6bb6c6d2fa0..f32d8558fbf 100644 --- a/server-tools/instance-manager/protocol.cc +++ b/server-tools/instance-manager/protocol.cc @@ -105,9 +105,9 @@ void store_to_string(Buffer *buf, const char *string, uint *position) uint string_len; string_len= strlen(string); - buf->check_and_add(*position, 2); + buf->reserve(*position, 2); currpos= net_store_length(buf->buffer + *position, string_len); - buf->put_to_buffer(currpos, string, string_len); + buf->append(currpos, string, string_len); *position= *position + string_len + (currpos - buf->buffer - *position); } @@ -147,7 +147,7 @@ int send_fields(struct st_net *net, LIST *fields) store_to_string(&send_buff, (char *) "", &position); /* table name alias */ store_to_string(&send_buff, field->name, &position); /* column name */ store_to_string(&send_buff, field->name, &position); /* column name alias */ - send_buff.check_and_add(position, 12); + send_buff.reserve(position, 12); send_buff.buffer[position++]= 12; int2store(send_buff.buffer + position, 1); /* charsetnr */ int4store(send_buff.buffer + position + 2, field->length); /* field length */ From 234ca309b9e1e49b6425fbf0dfd662e7d0f7b383 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 26 Oct 2004 23:22:12 +0400 Subject: [PATCH 03/11] Various post-review fixes server-tools/instance-manager/buffer.cc: simplified buffer interface server-tools/instance-manager/buffer.h: simplified buffer interface server-tools/instance-manager/command.cc: Command class now uses instance_map directly server-tools/instance-manager/command.h: Made Command to use instance_map directly (not through the factory, which is not needed here in fact) server-tools/instance-manager/commands.cc: Moved mysql client/server protocol-specific functions to the commands server-tools/instance-manager/commands.h: Added a comment for Syntax_error command, fixed classes to use instance map instead of the factory server-tools/instance-manager/factory.cc: Fixed factory to give appropriate class to the commands server-tools/instance-manager/guardian.cc: Fixed guardian to delay start of new instances monitoring. Moved guardian initialization to the class from Instance map. server-tools/instance-manager/guardian.h: interface fixed server-tools/instance-manager/instance.cc: added some loging server-tools/instance-manager/instance_map.cc: All non-instance map specific functions moved from the class. Added iterator for instance_map server-tools/instance-manager/instance_map.h: All non-instance map related functions moved from the class. Added iterator for instance_map. server-tools/instance-manager/listener.cc: Added FD_CLOEXEC flag to sockets, as we don't want instances to inherit them after exec. server-tools/instance-manager/manager.cc: use guardian method moved from the instance map server-tools/instance-manager/mysql_connection.cc: cleanup server-tools/instance-manager/protocol.cc: fix according to the changes in the Buffer class --- server-tools/instance-manager/buffer.cc | 9 +- server-tools/instance-manager/buffer.h | 4 +- server-tools/instance-manager/command.cc | 4 +- server-tools/instance-manager/command.h | 6 +- server-tools/instance-manager/commands.cc | 261 +++++++++++++++-- server-tools/instance-manager/commands.h | 23 +- server-tools/instance-manager/factory.cc | 12 +- server-tools/instance-manager/guardian.cc | 58 +++- server-tools/instance-manager/guardian.h | 10 +- server-tools/instance-manager/instance.cc | 2 + server-tools/instance-manager/instance_map.cc | 273 +++--------------- server-tools/instance-manager/instance_map.h | 25 +- server-tools/instance-manager/listener.cc | 7 + server-tools/instance-manager/manager.cc | 2 +- .../instance-manager/mysql_connection.cc | 4 +- server-tools/instance-manager/protocol.cc | 6 +- 16 files changed, 401 insertions(+), 305 deletions(-) diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index 66267e4af18..212260bf9e0 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -27,7 +27,7 @@ SYNOPSYS append() - position start position in the buffer + position start position in the buffer string string to be put in the buffer len_arg the length of the string. This way we can avoid some strlens. @@ -43,12 +43,12 @@ 1 - The buffer came to 16Mb barrier */ -int Buffer::append(char *position, const char *string, uint len_arg) +int Buffer::append(uint position, const char *string, uint len_arg) { - if (reserve(position - buffer, len_arg)) + if (reserve(position, len_arg)) return 1; - strnmov(position, string, len_arg); + strnmov(buffer + position, string, len_arg); return 0; } @@ -89,3 +89,4 @@ int Buffer::reserve(uint position, uint len_arg) } return 0; } + diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h index dbf72e34bf0..66860bd67b5 100644 --- a/server-tools/instance-manager/buffer.h +++ b/server-tools/instance-manager/buffer.h @@ -35,7 +35,7 @@ private: enum { BUFFER_INITIAL_SIZE= 4096 }; /* maximum buffer size is 16Mb */ enum { MAX_BUFFER_SIZE= 16777216 }; - uint buffer_size; + size_t buffer_size; public: Buffer() { @@ -50,7 +50,7 @@ public: public: char *buffer; - int append(char *start_pos, const char *string, uint len_arg); + int append(uint position, const char *string, uint len_arg); int reserve(uint position, uint len_arg); }; diff --git a/server-tools/instance-manager/command.cc b/server-tools/instance-manager/command.cc index 87260f9e17b..9f7d08d9fda 100644 --- a/server-tools/instance-manager/command.cc +++ b/server-tools/instance-manager/command.cc @@ -21,8 +21,8 @@ #include "command.h" -Command::Command(Command_factory *factory_arg) - :factory(factory_arg) +Command::Command(Instance_map *imap_arg) + :instance_map(imap_arg) {} Command::~Command() diff --git a/server-tools/instance-manager/command.h b/server-tools/instance-manager/command.h index 9b981ecd163..8ae4e33b92f 100644 --- a/server-tools/instance-manager/command.h +++ b/server-tools/instance-manager/command.h @@ -24,7 +24,7 @@ /* Class responsible for allocation of im commands. */ -class Command_factory; +class Instance_map; /* Command - entry point for any command. @@ -34,14 +34,14 @@ class Command_factory; class Command { public: - Command(Command_factory *factory_arg= 0); + Command(Instance_map *instance_map_arg= 0); virtual ~Command(); /* method of executing: */ virtual int execute(struct st_net *net, ulong connection_id) = 0; protected: - Command_factory *factory; + Instance_map *instance_map; }; #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H */ diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 30d8f0794a0..357b9a47d4d 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -15,18 +15,81 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "command.h" -#include "factory.h" #include "commands.h" #include "instance.h" #include "instance_map.h" #include "messages.h" +#include "protocol.h" +#include "buffer.h" +#include /* implementation for Show_instances: */ + +/* + The method sends a list of instances in the instance map to the client. + + SYNOPSYS + Show_instances::do_command() + net The network connection to the client. + + RETURN + 0 - ok + 1 - error occured +*/ + +int Show_instances::do_command(struct st_net *net) +{ + Buffer send_buff; /* buffer for packets */ + LIST name, status; + NAME_WITH_LENGTH name_field, status_field; + LIST *field_list; + uint position=0; + + name_field.name= (char *) "instance_name"; + name_field.length= 20; + name.data= &name_field; + status_field.name= (char *) "status"; + status_field.length= 20; + status.data= &status_field; + field_list= list_add(NULL, &status); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + Imap_iterator iterator(instance_map); + + instance_map->lock(); + while (instance= iterator.next()) + { + position= 0; + store_to_string(&send_buff, instance->options.instance_name, &position); + if (instance->is_running()) + store_to_string(&send_buff, (char *) "online", &position); + else + store_to_string(&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; +err: + return 1; +} + + int Show_instances::execute(struct st_net *net, ulong connection_id) { - if (factory->instance_map.show_instances(net)) + if (do_command(net)) return ER_OUT_OF_RESOURCES; return 0; @@ -37,7 +100,7 @@ int Show_instances::execute(struct st_net *net, ulong connection_id) int Flush_instances::execute(struct st_net *net, ulong connection_id) { - if (factory->instance_map.flush_instances()) + if (instance_map->flush_instances()) return ER_OUT_OF_RESOURCES; net_send_ok(net, connection_id); @@ -47,14 +110,14 @@ int Flush_instances::execute(struct st_net *net, ulong connection_id) /* implementation for Show_instance_status: */ -Show_instance_status::Show_instance_status(Command_factory *factory, +Show_instance_status::Show_instance_status(Instance_map *imap_arg, const char *name, uint len) - :Command(factory) + :Command(imap_arg) { Instance *instance; /* we make a search here, since we don't want t store the name */ - if (instance= factory->instance_map.find(name, len)) + if (instance= instance_map->find(name, len)) { instance_name= instance->options.instance_name; } @@ -63,11 +126,80 @@ Show_instance_status::Show_instance_status(Command_factory *factory, } +/* + The method sends a table with a status of requested instance to the client. + + SYNOPSYS + Show_instance_status::do_command() + net The network connection to the client. + instance_name The name of the instance. + + RETURN + 0 - ok + 1 - error occured +*/ + + +int Show_instance_status::do_command(struct st_net *net, + const char *instance_name) +{ + enum { MAX_VERSION_LENGTH= 40 }; + Buffer send_buff; /* buffer for packets */ + LIST name, status, version; + LIST *field_list; + NAME_WITH_LENGTH name_field, status_field, version_field; + uint position=0; + + /* create list of the fileds to be passed to send_fields */ + name_field.name= (char *) "instance_name"; + name_field.length= 20; + name.data= &name_field; + status_field.name= (char *) "status"; + status_field.length= 20; + status.data= &status_field; + version_field.name= (char *) "version"; + version_field.length= MAX_VERSION_LENGTH; + version.data= &version_field; + field_list= list_add(NULL, &version); + field_list= list_add(field_list, &status); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + + store_to_string(&send_buff, (char *) instance_name, &position); + if ((instance= instance_map->find(instance_name, strlen(instance_name))) == NULL) + goto err; + if (instance->is_running()) + { + store_to_string(&send_buff, (char *) "online", &position); + store_to_string(&send_buff, mysql_get_server_info(&(instance->mysql)), &position); + } + else + { + store_to_string(&send_buff, (char *) "offline", &position); + store_to_string(&send_buff, (char *) "unknown", &position); + } + + + my_net_write(net, send_buff.buffer, (uint) position); + } + + send_eof(net); + net_flush(net); + +err: + return 0; +} + + int Show_instance_status::execute(struct st_net *net, ulong connection_id) { if (instance_name != NULL) { - if (factory->instance_map.show_instance_status(net, instance_name)) + if (do_command(net, instance_name)) return ER_OUT_OF_RESOURCES; return 0; } @@ -80,14 +212,14 @@ int Show_instance_status::execute(struct st_net *net, ulong connection_id) /* Implementation for Show_instance_options */ -Show_instance_options::Show_instance_options(Command_factory *factory, +Show_instance_options::Show_instance_options(Instance_map *imap_arg, const char *name, uint len): - Command(factory) + Command(imap_arg) { Instance *instance; /* we make a search here, since we don't want t store the name */ - if (instance= (factory->instance_map).find(name, len)) + if (instance= instance_map->find(name, len)) { instance_name= instance->options.instance_name; } @@ -96,11 +228,99 @@ Show_instance_options::Show_instance_options(Command_factory *factory, } +int Show_instance_options::do_command(struct st_net *net, + const char *instance_name) +{ + enum { MAX_VERSION_LENGTH= 40 }; + Buffer send_buff; /* buffer for packets */ + LIST name, option; + LIST *field_list; + NAME_WITH_LENGTH name_field, option_field; + uint position=0; + + /* create list of the fileds to be passed to send_fields */ + name_field.name= (char *) "option_name"; + name_field.length= 20; + name.data= &name_field; + option_field.name= (char *) "value"; + option_field.length= 20; + option.data= &option_field; + field_list= list_add(NULL, &option); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + + if ((instance= instance_map-> + find(instance_name, strlen(instance_name))) == NULL) + goto err; + store_to_string(&send_buff, (char *) "instance_name", &position); + store_to_string(&send_buff, (char *) instance_name, &position); + my_net_write(net, send_buff.buffer, (uint) position); + if (instance->options.mysqld_path != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "mysqld_path", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_path, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + if (instance->options.mysqld_user != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "admin_user", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_user, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + if (instance->options.mysqld_password != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "admin_password", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_password, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + /* loop through the options stored in DYNAMIC_ARRAY */ + for (int i= 0; i < instance->options.options_array.elements; i++) + { + char *tmp_option, *option_value; + get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i); + option_value= strchr(tmp_option, '='); + /* split the option string into two parts */ + *option_value= 0; + position= 0; + store_to_string(&send_buff, tmp_option + 2, &position); + store_to_string(&send_buff, option_value + 1, &position); + /* join name and the value into the same option again */ + *option_value= '='; + my_net_write(net, send_buff.buffer, (uint) position); + } + } + + send_eof(net); + net_flush(net); + + return 0; + +err: + return 1; +} + + int Show_instance_options::execute(struct st_net *net, ulong connection_id) { if (instance_name != NULL) { - if (factory->instance_map.show_instance_options(net, instance_name)) + if (do_command(net, instance_name)) return ER_OUT_OF_RESOURCES; return 0; } @@ -113,12 +333,12 @@ int Show_instance_options::execute(struct st_net *net, ulong connection_id) /* Implementation for Start_instance */ -Start_instance::Start_instance(Command_factory *factory, +Start_instance::Start_instance(Instance_map *imap_arg, const char *name, uint len) - :Command(factory) + :Command(imap_arg) { /* we make a search here, since we don't want t store the name */ - if (instance= factory->instance_map.find(name, len)) + if (instance= instance_map->find(name, len)) instance_name= instance->options.instance_name; } @@ -136,8 +356,7 @@ int Start_instance::execute(struct st_net *net, ulong connection_id) return err_code; if (instance->options.is_guarded != NULL) - factory->instance_map.guardian->guard(instance->options.instance_name, - instance->options.instance_name_len); + instance_map->guardian->guard(instance); net_send_ok(net, connection_id); return 0; @@ -147,12 +366,12 @@ int Start_instance::execute(struct st_net *net, ulong connection_id) /* Implementation for Stop_instance: */ -Stop_instance::Stop_instance(Command_factory *factory, +Stop_instance::Stop_instance(Instance_map *imap_arg, const char *name, uint len) - :Command(factory) + :Command(imap_arg) { /* we make a search here, since we don't want t store the name */ - if (instance= factory->instance_map.find(name, len)) + if (instance= instance_map->find(name, len)) instance_name= instance->options.instance_name; } @@ -168,8 +387,8 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id) else { if (instance->options.is_guarded != NULL) - factory->instance_map.guardian-> - stop_guard(instance_name, instance->options.instance_name_len); + instance_map->guardian-> + stop_guard(instance); if (err_code= instance->stop()) return err_code; diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h index 09df4fc9260..8b53b21bbac 100644 --- a/server-tools/instance-manager/commands.h +++ b/server-tools/instance-manager/commands.h @@ -27,9 +27,10 @@ class Show_instances : public Command { public: - Show_instances(Command_factory *factory): Command(factory) + Show_instances(Instance_map *imap_arg): Command(imap_arg) {} + int do_command(struct st_net *net); int execute(struct st_net *net, ulong connection_id); }; @@ -42,7 +43,7 @@ public: class Flush_instances : public Command { public: - Flush_instances(Command_factory *factory): Command(factory) + Flush_instances(Instance_map *imap_arg): Command(imap_arg) {} int execute(struct st_net *net, ulong connection_id); @@ -58,8 +59,8 @@ class Show_instance_status : public Command { public: - Show_instance_status(Command_factory *factory, const char *name, uint len); - + Show_instance_status(Instance_map *imap_arg, const char *name, uint len); + int do_command(struct st_net *net, const char *instance_name); int execute(struct st_net *net, ulong connection_id); const char *instance_name; }; @@ -74,9 +75,10 @@ class Show_instance_options : public Command { public: - Show_instance_options(Command_factory *factory, const char *name, uint len); + Show_instance_options(Instance_map *imap_arg, const char *name, uint len); int execute(struct st_net *net, ulong connection_id); + int do_command(struct st_net *net, const char *instance_name); const char *instance_name; }; @@ -89,11 +91,11 @@ public: class Start_instance : public Command { public: - Start_instance(Command_factory *factory, const char *name, uint len); + Start_instance(Instance_map *imap_arg, const char *name, uint len); - Instance *instance; int execute(struct st_net *net, ulong connection_id); const char *instance_name; + Instance *instance; }; @@ -105,7 +107,7 @@ public: class Stop_instance : public Command { public: - Stop_instance(Command_factory *factory, const char *name, uint len); + Stop_instance(Instance_map *imap_arg, const char *name, uint len); Instance *instance; int execute(struct st_net *net, ulong connection_id); @@ -114,7 +116,10 @@ public: /* - Syntax error command. + Syntax error command. This command is issued if parser reported a syntax error. + We need it to distinguish the parse error and the situation when parser internal + error occured. E.g. parsing failed because we hadn't had enought memory. In the + latter case parse_command() should return an error. */ class Syntax_error : public Command diff --git a/server-tools/instance-manager/factory.cc b/server-tools/instance-manager/factory.cc index 691aca0c7ea..cde5d0564aa 100644 --- a/server-tools/instance-manager/factory.cc +++ b/server-tools/instance-manager/factory.cc @@ -23,35 +23,35 @@ Show_instances *Command_factory::new_Show_instances() { - return new Show_instances(this); + return new Show_instances(&instance_map); } Flush_instances *Command_factory::new_Flush_instances() { - return new Flush_instances(this); + return new Flush_instances(&instance_map); } Show_instance_status *Command_factory:: new_Show_instance_status(const char *name, uint len) { - return new Show_instance_status(this, name, len); + return new Show_instance_status(&instance_map, name, len); } Show_instance_options *Command_factory:: new_Show_instance_options(const char *name, uint len) { - return new Show_instance_options(this, name, len); + return new Show_instance_options(&instance_map, name, len); } Start_instance *Command_factory:: new_Start_instance(const char *name, uint len) { - return new Start_instance(this, name, len); + return new Start_instance(&instance_map, name, len); } Stop_instance *Command_factory::new_Stop_instance(const char *name, uint len) { - return new Stop_instance(this, name, len); + return new Stop_instance(&instance_map, name, len); } Syntax_error *Command_factory::new_Syntax_error() diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index f13b98cbf20..5a4d0bade74 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -21,6 +21,8 @@ #include "guardian.h" #include "instance_map.h" +#include "mysql_manager_error.h" +#include "log.h" #include C_MODE_START @@ -46,6 +48,7 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, thread_registry.register_thread(&thread_info); init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); guarded_instances= NULL; + starting_instances= NULL; } @@ -76,7 +79,6 @@ void Guardian_thread::run() { Instance *instance; LIST *loop; - int i= 0; my_thread_init(); @@ -88,9 +90,12 @@ void Guardian_thread::run() { instance= (Instance *) loop->data; /* instance-> start already checks whether instance is running */ - instance->start(); + if (instance->start() != ER_INSTANCE_ALREADY_STARTED) + log_info("guardian attempted to restart instance %s", + instance->options.instance_name); loop= loop->next; } + move_to_list(&starting_instances, &guarded_instances); pthread_mutex_unlock(&LOCK_guardian); sleep(monitoring_interval); } @@ -99,6 +104,24 @@ void Guardian_thread::run() } +int Guardian_thread::start() +{ + Instance *instance; + Imap_iterator iterator(instance_map); + + instance_map->lock(); + while (instance= iterator.next()) + { + if ((instance->options.is_guarded != NULL) && (instance->is_running())) + if (guard(instance)) + return 1; + } + instance_map->unlock(); + + return 0; +} + + /* Start instance guarding @@ -116,20 +139,38 @@ void Guardian_thread::run() 1 - error occured */ -int Guardian_thread::guard(const char *instance_name, uint name_len) + +int Guardian_thread::guard(Instance *instance) +{ + return add_instance_to_list(instance, &starting_instances); +} + + +void Guardian_thread::move_to_list(LIST **from, LIST **to) +{ + LIST *tmp; + + while (*from) + { + tmp= rest(*from); + *to= list_add(*to, *from); + *from= tmp; + } +} + + +int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list) { LIST *node; - Instance *instance; node= (LIST *) alloc_root(&alloc, sizeof(LIST)); if (node == NULL) return 1; - instance= instance_map->find(instance_name, name_len); /* we store the pointers to instances from the instance_map's MEM_ROOT */ node->data= (void *) instance; pthread_mutex_lock(&LOCK_guardian); - guarded_instances= list_add(guarded_instances, node); + *list= list_add(*list, node); pthread_mutex_unlock(&LOCK_guardian); return 0; @@ -143,12 +184,9 @@ int Guardian_thread::guard(const char *instance_name, uint name_len) a piece of the MEM_ROOT). */ -int Guardian_thread::stop_guard(const char *instance_name, uint name_len) +int Guardian_thread::stop_guard(Instance *instance) { LIST *node; - Instance *instance; - - instance= instance_map->find(instance_name, name_len); pthread_mutex_lock(&LOCK_guardian); node= guarded_instances; diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index 5d0cdcd7c92..0ae2161f1dc 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -66,13 +66,19 @@ public: ~Guardian_thread(); void run(); int init(); - int guard(const char *instance_name, uint name_len); - int stop_guard(const char *instance_name, uint name_len); + int start(); + int guard(Instance *instance); + int stop_guard(Instance *instance); + +private: + int add_instance_to_list(Instance *instance, LIST **list); + void move_to_list(LIST **from, LIST **to); private: pthread_mutex_t LOCK_guardian; Thread_info thread_info; LIST *guarded_instances; + LIST *starting_instances; MEM_ROOT alloc; enum { MEM_ROOT_BLOCK_SIZE= 512 }; }; diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 689cea2d786..1b9ce09d782 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -20,6 +20,7 @@ #include "instance.h" #include "mysql_manager_error.h" +#include "log.h" #include #include #include @@ -43,6 +44,7 @@ int Instance::start() if (!is_running()) { + log_info("trying to start instance %s", options.instance_name); switch (fork()) { case 0: if (fork()) /* zombie protection */ diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 89731eb27b9..aa81194b1d4 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -127,6 +127,18 @@ Instance_map::~Instance_map() } +int Instance_map::lock() +{ + pthread_mutex_lock(&LOCK_instance_map); +} + + +int Instance_map::unlock() +{ + pthread_mutex_unlock(&LOCK_instance_map); +} + + int Instance_map::flush_instances() { int rc; @@ -141,242 +153,6 @@ int Instance_map::flush_instances() } -int Instance_map::show_instance_options(struct st_net *net, - const char *instance_name) -{ - enum { MAX_VERSION_LENGTH= 40 }; - Buffer send_buff; /* buffer for packets */ - LIST name, option; - LIST *field_list; - NAME_WITH_LENGTH name_field, option_field; - uint position=0; - - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char *) "option_name"; - name_field.length= 20; - name.data= &name_field; - option_field.name= (char *) "value"; - option_field.length= 20; - option.data= &option_field; - field_list= list_add(NULL, &option); - field_list= list_add(field_list, &name); - - send_fields(net, field_list); - - { - Instance *instance; - - if ((instance= find(instance_name, strlen(instance_name))) == NULL) - goto err; - store_to_string(&send_buff, (char *) "instance_name", &position); - store_to_string(&send_buff, (char *) instance_name, &position); - my_net_write(net, send_buff.buffer, (uint) position); - if (instance->options.mysqld_path != NULL) - { - position= 0; - store_to_string(&send_buff, (char *) "mysqld_path", &position); - store_to_string(&send_buff, - (char *) instance->options.mysqld_path, - &position); - my_net_write(net, send_buff.buffer, (uint) position); - } - - if (instance->options.mysqld_user != NULL) - { - position= 0; - store_to_string(&send_buff, (char *) "admin_user", &position); - store_to_string(&send_buff, - (char *) instance->options.mysqld_user, - &position); - my_net_write(net, send_buff.buffer, (uint) position); - } - - if (instance->options.mysqld_password != NULL) - { - position= 0; - store_to_string(&send_buff, (char *) "admin_password", &position); - store_to_string(&send_buff, - (char *) instance->options.mysqld_password, - &position); - my_net_write(net, send_buff.buffer, (uint) position); - } - - /* loop through the options stored in DYNAMIC_ARRAY */ - for (int i= 0; i < instance->options.options_array.elements; i++) - { - char *tmp_option, *option_value; - get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i); - option_value= strchr(tmp_option, '='); - /* split the option string into two parts */ - *option_value= 0; - position= 0; - store_to_string(&send_buff, tmp_option + 2, &position); - store_to_string(&send_buff, option_value + 1, &position); - /* join name and the value into the same option again */ - *option_value= '='; - my_net_write(net, send_buff.buffer, (uint) position); - } - } - - send_eof(net); - net_flush(net); - - return 0; - -err: - return 1; -} - -/* return the list of running guarded instances */ -int Instance_map::init_guardian() -{ - Instance *instance; - uint i= 0; - - while (i < hash.records) - { - instance= (Instance *) hash_element(&hash, i); - if ((instance->options.is_guarded != NULL) && (instance->is_running())) - if (guardian->guard(instance->options.instance_name, - instance->options.instance_name_len)) - return 1; - i++; - } - - return 0; -} - - -/* - The method sends a list of instances in the instance map to the client. - - SYNOPSYS - show_instances() - net The network connection to the client. - - RETURN - 0 - ok - 1 - error occured -*/ - -int Instance_map::show_instances(struct st_net *net) -{ - Buffer send_buff; /* buffer for packets */ - LIST name, status; - NAME_WITH_LENGTH name_field, status_field; - LIST *field_list; - uint position=0; - - name_field.name= (char *) "instance_name"; - name_field.length= 20; - name.data= &name_field; - status_field.name= (char *) "status"; - status_field.length= 20; - status.data= &status_field; - field_list= list_add(NULL, &status); - field_list= list_add(field_list, &name); - - send_fields(net, field_list); - - { - Instance *instance; - uint i= 0; - - pthread_mutex_lock(&LOCK_instance_map); - while (i < hash.records) - { - position= 0; - instance= (Instance *) hash_element(&hash, i); - store_to_string(&send_buff, instance->options.instance_name, &position); - if (instance->is_running()) - store_to_string(&send_buff, (char *) "online", &position); - else - store_to_string(&send_buff, (char *) "offline", &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; - i++; - } - pthread_mutex_unlock(&LOCK_instance_map); - } - if (send_eof(net)) - goto err; - if (net_flush(net)) - goto err; - - return 0; -err: - return 1; -} - - -/* - The method sends a table with a status of requested instance to the client. - - SYNOPSYS - show_instance_status() - net The network connection to the client. - instance_name The name of the instance. - - RETURN - 0 - ok - 1 - error occured -*/ - -int Instance_map::show_instance_status(struct st_net *net, - const char *instance_name) -{ - enum { MAX_VERSION_LENGTH= 40 }; - Buffer send_buff; /* buffer for packets */ - LIST name, status, version; - LIST *field_list; - NAME_WITH_LENGTH name_field, status_field, version_field; - uint position=0; - - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char *) "instance_name"; - name_field.length= 20; - name.data= &name_field; - status_field.name= (char *) "status"; - status_field.length= 20; - status.data= &status_field; - version_field.name= (char *) "version"; - version_field.length= MAX_VERSION_LENGTH; - version.data= &version_field; - field_list= list_add(NULL, &version); - field_list= list_add(field_list, &status); - field_list= list_add(field_list, &name); - - send_fields(net, field_list); - - { - Instance *instance; - - store_to_string(&send_buff, (char *) instance_name, &position); - if ((instance= find(instance_name, strlen(instance_name))) == NULL) - goto err; - if (instance->is_running()) - { - store_to_string(&send_buff, (char *) "online", &position); - store_to_string(&send_buff, mysql_get_server_info(&(instance->mysql)), &position); - } - else - { - store_to_string(&send_buff, (char *) "offline", &position); - store_to_string(&send_buff, (char *) "unknown", &position); - } - - - my_net_write(net, send_buff.buffer, (uint) position); - } - - send_eof(net); - net_flush(net); - -err: - return 0; -} - - int Instance_map::add_instance(Instance *instance) { return my_hash_insert(&hash, (byte *) instance); @@ -448,3 +224,28 @@ int Instance_map::load() return error; } + + +Instance *Instance_map::get_instance(uint instance_number) +{ + if (instance_number < hash.records) + return (Instance *) hash_element(&hash, instance_number); + else + return NULL; +} + + +/*--- Implementaton of the Instance map iterator class (Imap_iterator) ---*/ + + +void Imap_iterator::go_to_first() +{ + current_instance=0; +} + + +Instance *Imap_iterator::next() +{ + return instance_map->get_instance(current_instance++); +} + diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 965261f2920..193376c6f23 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -43,12 +43,11 @@ public: Instance *find(const char *name, uint name_len); Instance *find(uint instance_number); - int show_instances(struct st_net *net); - int show_instance_status(struct st_net *net, const char *instance_name); - int show_instance_options(struct st_net *net, const char *instance_name); int flush_instances(); - int init_guardian(); int cleanup(); + int lock(); + int unlock(); + Instance *get_instance(uint instance_number); Instance_map(); ~Instance_map(); @@ -73,4 +72,22 @@ private: HASH hash; }; + +/* Instance_map iterator */ + +class Imap_iterator +{ +private: + uint current_instance; + Instance_map *instance_map; +public: + Imap_iterator(Instance_map *instance_map_arg) : + instance_map(instance_map_arg), current_instance(0) + {} + + void go_to_first(); + Instance *next(); +}; + + #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H */ diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index ddd03726917..4a13dc39ec7 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -129,8 +129,12 @@ void Listener_thread::run() thread_registry.request_shutdown(); return; } + /* set the socket nonblocking */ flags= fcntl(ip_socket, F_GETFL, 0); fcntl(ip_socket, F_SETFL, flags | O_NONBLOCK); + /* make shure that instances won't be listening our sockets */ + flags= fcntl(ip_socket, F_GETFD, 0); + fcntl(ip_socket, F_SETFD, flags | FD_CLOEXEC); log_info("accepting connections on ip socket"); @@ -180,6 +184,9 @@ void Listener_thread::run() /* set the socket nonblocking */ flags= fcntl(unix_socket, F_GETFL, 0); fcntl(unix_socket, F_SETFL, flags | O_NONBLOCK); + /* make shure that instances won't be listening our sockets */ + flags= fcntl(unix_socket, F_GETFD, 0); + fcntl(unix_socket, F_SETFD, flags | FD_CLOEXEC); } log_info("accepting connections on unix socket %s", unix_socket_address.sun_path); diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 11a3289adbf..1c23aa602d4 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -161,7 +161,7 @@ void manager(const Options &options) alarm structures initialization as we have to use net_* functions while making the list. And they in their turn need alarms for timeout suppport. */ - instance_map.init_guardian(); + guardian_thread.start(); while (!shutdown_complete) { diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc index 2a617675c26..0ebcb0eea8d 100644 --- a/server-tools/instance-manager/mysql_connection.cc +++ b/server-tools/instance-manager/mysql_connection.cc @@ -319,10 +319,10 @@ int Mysql_connection_thread::dispatch_command(enum enum_server_command command, { switch (command) { case COM_QUIT: // client exit - log_info("query for connection %d received quit command",connection_id); + log_info("query for connection %d received quit command", connection_id); return 1; case COM_PING: - log_info("query for connection %d received ping command",connection_id); + log_info("query for connection %d received ping command", connection_id); net_send_ok(&net, connection_id); break; case COM_QUERY: diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc index f32d8558fbf..2f1f95a5f05 100644 --- a/server-tools/instance-manager/protocol.cc +++ b/server-tools/instance-manager/protocol.cc @@ -101,14 +101,14 @@ char *net_store_length(char *pkg, uint length) void store_to_string(Buffer *buf, const char *string, uint *position) { - char* currpos; + uint currpos; uint string_len; string_len= strlen(string); buf->reserve(*position, 2); - currpos= net_store_length(buf->buffer + *position, string_len); + currpos= (net_store_length(buf->buffer + *position, string_len) - buf->buffer); buf->append(currpos, string, string_len); - *position= *position + string_len + (currpos - buf->buffer - *position); + *position= *position + string_len + (currpos - *position); } From d97e8686f1aa3c880eb5baa9319b1e821d6d2bab Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Oct 2004 10:21:48 +0400 Subject: [PATCH 04/11] more review fixes server-tools/instance-manager/command.cc: member rename server-tools/instance-manager/commands.cc: rename server-tools/instance-manager/commands.h: member rename server-tools/instance-manager/guardian.cc: guardian fixed to use Instance_map::Iterator intead of Imap server-tools/instance-manager/instance_map.cc: removed get_instance method from Instance_map as it is needed only by Iterator which is made a friend class server-tools/instance-manager/instance_map.h: use Instance_map::Iterator instead if Imap_iterator (no need to add more names to the global namespace) --- server-tools/instance-manager/command.cc | 4 +-- server-tools/instance-manager/commands.cc | 18 +++++----- server-tools/instance-manager/commands.h | 12 +++---- server-tools/instance-manager/guardian.cc | 2 +- server-tools/instance-manager/instance_map.cc | 20 ++++------- server-tools/instance-manager/instance_map.h | 35 +++++++++---------- 6 files changed, 41 insertions(+), 50 deletions(-) diff --git a/server-tools/instance-manager/command.cc b/server-tools/instance-manager/command.cc index 9f7d08d9fda..818c4b0cae2 100644 --- a/server-tools/instance-manager/command.cc +++ b/server-tools/instance-manager/command.cc @@ -21,8 +21,8 @@ #include "command.h" -Command::Command(Instance_map *imap_arg) - :instance_map(imap_arg) +Command::Command(Instance_map *instance_map_arg) + :instance_map(instance_map_arg) {} Command::~Command() diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 357b9a47d4d..102a3df00e5 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -60,7 +60,7 @@ int Show_instances::do_command(struct st_net *net) { Instance *instance; - Imap_iterator iterator(instance_map); + Instance_map::Iterator iterator(instance_map); instance_map->lock(); while (instance= iterator.next()) @@ -110,9 +110,9 @@ int Flush_instances::execute(struct st_net *net, ulong connection_id) /* implementation for Show_instance_status: */ -Show_instance_status::Show_instance_status(Instance_map *imap_arg, +Show_instance_status::Show_instance_status(Instance_map *instance_map_arg, const char *name, uint len) - :Command(imap_arg) + :Command(instance_map_arg) { Instance *instance; @@ -212,9 +212,9 @@ int Show_instance_status::execute(struct st_net *net, ulong connection_id) /* Implementation for Show_instance_options */ -Show_instance_options::Show_instance_options(Instance_map *imap_arg, +Show_instance_options::Show_instance_options(Instance_map *instance_map_arg, const char *name, uint len): - Command(imap_arg) + Command(instance_map_arg) { Instance *instance; @@ -333,9 +333,9 @@ int Show_instance_options::execute(struct st_net *net, ulong connection_id) /* Implementation for Start_instance */ -Start_instance::Start_instance(Instance_map *imap_arg, +Start_instance::Start_instance(Instance_map *instance_map_arg, const char *name, uint len) - :Command(imap_arg) + :Command(instance_map_arg) { /* we make a search here, since we don't want t store the name */ if (instance= instance_map->find(name, len)) @@ -366,9 +366,9 @@ int Start_instance::execute(struct st_net *net, ulong connection_id) /* Implementation for Stop_instance: */ -Stop_instance::Stop_instance(Instance_map *imap_arg, +Stop_instance::Stop_instance(Instance_map *instance_map_arg, const char *name, uint len) - :Command(imap_arg) + :Command(instance_map_arg) { /* we make a search here, since we don't want t store the name */ if (instance= instance_map->find(name, len)) diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h index 8b53b21bbac..cd13bf09549 100644 --- a/server-tools/instance-manager/commands.h +++ b/server-tools/instance-manager/commands.h @@ -27,7 +27,7 @@ class Show_instances : public Command { public: - Show_instances(Instance_map *imap_arg): Command(imap_arg) + Show_instances(Instance_map *instance_map_arg): Command(instance_map_arg) {} int do_command(struct st_net *net); @@ -43,7 +43,7 @@ public: class Flush_instances : public Command { public: - Flush_instances(Instance_map *imap_arg): Command(imap_arg) + Flush_instances(Instance_map *instance_map_arg): Command(instance_map_arg) {} int execute(struct st_net *net, ulong connection_id); @@ -59,7 +59,7 @@ class Show_instance_status : public Command { public: - Show_instance_status(Instance_map *imap_arg, const char *name, uint len); + Show_instance_status(Instance_map *instance_map_arg, const char *name, uint len); int do_command(struct st_net *net, const char *instance_name); int execute(struct st_net *net, ulong connection_id); const char *instance_name; @@ -75,7 +75,7 @@ class Show_instance_options : public Command { public: - Show_instance_options(Instance_map *imap_arg, const char *name, uint len); + Show_instance_options(Instance_map *instance_map_arg, const char *name, uint len); int execute(struct st_net *net, ulong connection_id); int do_command(struct st_net *net, const char *instance_name); @@ -91,7 +91,7 @@ public: class Start_instance : public Command { public: - Start_instance(Instance_map *imap_arg, const char *name, uint len); + Start_instance(Instance_map *instance_map_arg, const char *name, uint len); int execute(struct st_net *net, ulong connection_id); const char *instance_name; @@ -107,7 +107,7 @@ public: class Stop_instance : public Command { public: - Stop_instance(Instance_map *imap_arg, const char *name, uint len); + Stop_instance(Instance_map *instance_map_arg, const char *name, uint len); Instance *instance; int execute(struct st_net *net, ulong connection_id); diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index 5a4d0bade74..c7c0f1b0a77 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -107,7 +107,7 @@ void Guardian_thread::run() int Guardian_thread::start() { Instance *instance; - Imap_iterator iterator(instance_map); + Instance_map::Iterator iterator(instance_map); instance_map->lock(); while (instance= iterator.next()) diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index aa81194b1d4..b70f622fa73 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -226,26 +226,20 @@ int Instance_map::load() } -Instance *Instance_map::get_instance(uint instance_number) -{ - if (instance_number < hash.records) - return (Instance *) hash_element(&hash, instance_number); - else - return NULL; -} +/*--- Implementaton of the Instance map iterator class ---*/ -/*--- Implementaton of the Instance map iterator class (Imap_iterator) ---*/ - - -void Imap_iterator::go_to_first() +void Instance_map::Iterator::go_to_first() { current_instance=0; } -Instance *Imap_iterator::next() +Instance *Instance_map::Iterator::next() { - return instance_map->get_instance(current_instance++); + if (current_instance < instance_map->hash.records) + return (Instance *) hash_element(&instance_map->hash, current_instance++); + else + return NULL; } diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 193376c6f23..21d8c5caa9f 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -38,6 +38,22 @@ extern void free_groups(char **groups); class Instance_map { + friend class Iterator; +public: + /* Instance_map iterator */ + class Iterator + { + private: + uint current_instance; + Instance_map *instance_map; + public: + Iterator(Instance_map *instance_map_arg) : + instance_map(instance_map_arg), current_instance(0) + {} + + void go_to_first(); + Instance *next(); + }; public: /* returns a pointer to the instance or NULL, if there is no such instance */ Instance *find(const char *name, uint name_len); @@ -47,7 +63,6 @@ public: int cleanup(); int lock(); int unlock(); - Instance *get_instance(uint instance_number); Instance_map(); ~Instance_map(); @@ -72,22 +87,4 @@ private: HASH hash; }; - -/* Instance_map iterator */ - -class Imap_iterator -{ -private: - uint current_instance; - Instance_map *instance_map; -public: - Imap_iterator(Instance_map *instance_map_arg) : - instance_map(instance_map_arg), current_instance(0) - {} - - void go_to_first(); - Instance *next(); -}; - - #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H */ From 3691a8a426787e8f7a9b902d6cb6ab28ec20697a Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Oct 2004 11:01:38 +0400 Subject: [PATCH 05/11] comment fixed to reflect the current state of things server-tools/instance-manager/guardian.cc: comment fixed --- server-tools/instance-manager/guardian.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index c7c0f1b0a77..ac3e6298ea1 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -127,12 +127,13 @@ int Guardian_thread::start() SYNOPSYS guard() - instance_name the name of the instance to be guarded - name_len the length of the name + instance the instance to be guarded DESCRIPTION - The instance is added to the list of guarded instances. + The instance is added to the list of starting instances. Then after one guardian + loop it is moved to the guarded instances list. Usually guard() is called after we + start an instance, so we need to give some time to the instance to start. RETURN 0 - ok From 7a3a757fd5169cda75c2130cdd30457f7c8d2ee8 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 Nov 2004 10:11:03 +0300 Subject: [PATCH 06/11] post-review fixes server-tools/instance-manager/Makefile.am: Removed entry for deleted file server-tools/instance-manager/buffer.cc: cleanup server-tools/instance-manager/commands.cc: cleanup, added missing error handling server-tools/instance-manager/instance.cc: added waitpid in instance_start, added few checks server-tools/instance-manager/instance_map.cc: error handling for hash_init added server-tools/instance-manager/instance_map.h: Extended constructor server-tools/instance-manager/instance_options.cc: made add_option less bulky server-tools/instance-manager/instance_options.h: - server-tools/instance-manager/listener.cc: added missing close, fixed typo server-tools/instance-manager/manager.cc: moved some Instance_map initialization to costructor server-tools/instance-manager/protocol.cc: error handling added server-tools/instance-manager/protocol.h: store_to_string fixed to return a value server-tools/instance-manager/user_map.cc: error handling for hash_init added server-tools/instance-manager/user_map.h: added init() for User map (becouse of the hash_init check) --- server-tools/instance-manager/Makefile.am | 3 +- server-tools/instance-manager/buffer.cc | 7 +- server-tools/instance-manager/commands.cc | 31 +++-- server-tools/instance-manager/instance.cc | 21 +++- server-tools/instance-manager/instance_map.cc | 18 ++- server-tools/instance-manager/instance_map.h | 7 +- .../instance-manager/instance_options.cc | 115 +++++++----------- server-tools/instance-manager/listener.cc | 5 +- server-tools/instance-manager/manager.cc | 14 +-- server-tools/instance-manager/protocol.cc | 20 ++- server-tools/instance-manager/protocol.h | 2 +- server-tools/instance-manager/user_map.cc | 12 +- server-tools/instance-manager/user_map.h | 2 +- 13 files changed, 142 insertions(+), 115 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 522ca6166cc..71f2a5d66ce 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -80,8 +80,7 @@ mysqlmanager_SOURCES= mysqlmanager.cc manager.h manager.cc log.h log.cc \ instance_map.h instance_map.cc\ instance_options.h instance_options.cc \ buffer.h buffer.cc parse.cc parse.h \ - guardian.cc guardian.h common_structures.h \ - mysql_manager_error.h + guardian.cc guardian.h mysql_manager_error.h mysqlmanager_LDADD= liboptions.a \ libnet.a \ diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index 212260bf9e0..7e785f0450e 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -77,7 +77,7 @@ int Buffer::append(uint position, const char *string, uint len_arg) int Buffer::reserve(uint position, uint len_arg) { if (position + len_arg >= MAX_BUFFER_SIZE) - return 1; + goto err; if (position + len_arg>= buffer_size) { @@ -85,8 +85,13 @@ int Buffer::reserve(uint position, uint len_arg) min(MAX_BUFFER_SIZE, max((uint) (buffer_size*1.5), position + len_arg))); + if (buffer= NULL) + goto err; buffer_size= (uint) (buffer_size*1.5); } return 0; + +err: + return 1; } diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 102a3df00e5..50f7bea7daf 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -184,14 +184,17 @@ int Show_instance_status::do_command(struct st_net *net, } - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; } send_eof(net); net_flush(net); -err: return 0; + +err: + return 1; } @@ -258,7 +261,8 @@ int Show_instance_options::do_command(struct st_net *net, goto err; store_to_string(&send_buff, (char *) "instance_name", &position); store_to_string(&send_buff, (char *) instance_name, &position); - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; if (instance->options.mysqld_path != NULL) { position= 0; @@ -266,7 +270,17 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, (char *) instance->options.mysqld_path, &position); - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; + } + + if (instance->options.is_guarded != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "guarded", &position); + store_to_string(&send_buff, "", &position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; } if (instance->options.mysqld_user != NULL) @@ -276,7 +290,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, (char *) instance->options.mysqld_user, &position); - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; } if (instance->options.mysqld_password != NULL) @@ -286,7 +301,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, (char *) instance->options.mysqld_password, &position); - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; } /* loop through the options stored in DYNAMIC_ARRAY */ @@ -302,7 +318,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, option_value + 1, &position); /* join name and the value into the same option again */ *option_value= '='; - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; } } diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 1b9ce09d782..2c041e31119 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -24,7 +24,7 @@ #include #include #include - +#include /* The method starts an instance. @@ -41,11 +41,12 @@ int Instance::start() { + pid_t pid; if (!is_running()) { log_info("trying to start instance %s", options.instance_name); - switch (fork()) { + switch (pid= fork()) { case 0: if (fork()) /* zombie protection */ exit(0); /* parent goes bye-bye */ @@ -57,6 +58,7 @@ int Instance::start() case -1: return ER_CANNOT_START_INSTANCE; default: + waitpid(pid, NULL, 0); return 0; } } @@ -77,21 +79,32 @@ int Instance::cleanup() return 0; } + Instance::~Instance() { pthread_mutex_destroy(&LOCK_instance); } + bool Instance::is_running() { + uint port; + const char *socket; + + if (options.mysqld_port) + port= atoi(strchr(options.mysqld_port, '=') + 1); + + if (options.mysqld_socket) + socket= strchr(options.mysqld_socket, '=') + 1; + pthread_mutex_lock(&LOCK_instance); if (!is_connected) { mysql_init(&mysql); if (mysql_real_connect(&mysql, LOCAL_HOST, options.mysqld_user, options.mysqld_password, - NullS, atoi(strchr(options.mysqld_port, '=') + 1), - strchr(options.mysqld_socket, '=') + 1, 0)) + NullS, port, + socket, 0)) { is_connected= TRUE; pthread_mutex_unlock(&LOCK_instance); diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index b70f622fa73..12f0c799d50 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -110,14 +110,26 @@ err_new_instance: C_MODE_END -Instance_map::Instance_map() +Instance_map::Instance_map(const char *default_mysqld_path_arg, + const char *default_admin_user_arg, + const char *default_admin_password_arg) { - hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, - get_instance_key, delete_instance, 0); + mysqld_path= default_mysqld_path_arg; + user= default_admin_user_arg; + password= default_admin_password_arg; + pthread_mutex_init(&LOCK_instance_map, 0); } +int Instance_map::init() +{ + if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, + get_instance_key, delete_instance, 0)) + return 1; + return 0; +} + Instance_map::~Instance_map() { pthread_mutex_lock(&LOCK_instance_map); diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 21d8c5caa9f..e1bfe6ab391 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -48,7 +48,7 @@ public: Instance_map *instance_map; public: Iterator(Instance_map *instance_map_arg) : - instance_map(instance_map_arg), current_instance(0) + current_instance(0), instance_map(instance_map_arg) {} void go_to_first(); @@ -63,8 +63,11 @@ public: int cleanup(); int lock(); int unlock(); + int init(); - Instance_map(); + Instance_map(const char *default_mysqld_path_arg, + const char *default_admin_user_arg, + const char *default_admin_password_arg); ~Instance_map(); /* loads options from config files */ diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 8311e4f7bc0..4f299252bac 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -86,90 +86,58 @@ err: int Instance_options::add_option(const char* option) { - uint elements_count=0; - static const char socket[]= "--socket="; - static const char port[]= "--port="; - static const char datadir[]= "--datadir="; - static const char language[]= "--bind-address="; - static const char pid_file[]= "--pid-file="; - static const char path[]= "--mysqld_path="; - static const char user[]= "--admin_user="; - static const char password[]= "--admin_password="; - static const char guarded[]= "--guarded"; char *tmp; + enum { SAVE_VALUE= 1, SAVE_WHOLE, SAVE_WHOLE_AND_ADD }; + struct selected_options_st + { + const char *name; + uint length; + const char **value; + uint type; + } options[]= + { + {"--socket=", 9, &mysqld_socket, SAVE_WHOLE_AND_ADD}, + {"--port=", 7, &mysqld_port, SAVE_WHOLE_AND_ADD}, + {"--datadir=", 10, &mysqld_datadir, SAVE_WHOLE_AND_ADD}, + {"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD}, + {"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD}, + {"--mysqld_path=", 14, &mysqld_path, SAVE_VALUE}, + {"--admin_user=", 13, &mysqld_user, SAVE_VALUE}, + {"--admin_password=", 17, &mysqld_password, SAVE_VALUE}, + {"--guarded", 9, &is_guarded, SAVE_WHOLE}, + {NULL, 0, NULL, 0} + }; + struct selected_options_st *selected_options; if (!(tmp= strdup_root(&alloc, option))) goto err; - /* To get rid the final zero in a string we subtract 1 from sizeof value */ - if (strncmp(tmp, socket, sizeof socket - 1) == 0) - { - mysqld_socket= tmp; - goto add_options; - } + for (selected_options= options; selected_options->name; selected_options++) + { + if (!strncmp(tmp, selected_options->name, selected_options->length)) + 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; + defaut: + break; + } + } - if (strncmp(tmp, port, sizeof port - 1) == 0) - { - mysqld_port= tmp; - goto add_options; - } - - if (strncmp(tmp, datadir, sizeof datadir - 1) == 0) - { - mysqld_datadir= tmp; - goto add_options; - } - - if (strncmp(tmp, language, sizeof language - 1) == 0) - { - mysqld_bind_address= tmp; - goto add_options; - } - - if (strncmp(tmp, pid_file, sizeof pid_file - 1) == 0) - { - mysqld_pid_file= tmp; - goto add_options; - } - - /* - We don't need a prefix in the next three optios. - We also don't need to add them to argv array => - return instead of goto. - */ - - if (strncmp(tmp, path, sizeof path - 1) == 0) - { - mysqld_path= strchr(tmp, '=') + 1; - return 0; - } - - if (strncmp(tmp, user, sizeof user - 1) == 0) - { - mysqld_user= strchr(tmp, '=') + 1; - return 0; - } - - if (strncmp(tmp, password, sizeof password - 1) == 0) - { - mysqld_password= strchr(tmp, '=') + 1; - return 0; - } - - if (strncmp(tmp, guarded, sizeof guarded - 1) == 0) - { - is_guarded= tmp; - return 0; - } - -add_options: - insert_dynamic(&options_array,(gptr) &tmp); return 0; err: return 1; } + int Instance_options::add_to_argv(const char* option) { DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS); @@ -191,7 +159,8 @@ int Instance_options::init(const char *instance_name_arg) init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); - my_init_dynamic_array(&options_array, sizeof(char *), 0, 32); + if (my_init_dynamic_array(&options_array, sizeof(char *), 0, 32)) + goto err; if (!(instance_name= strmake_root(&alloc, (char *) instance_name_arg, instance_name_len))) diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 4a13dc39ec7..cb650e7cf9d 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -132,7 +132,7 @@ void Listener_thread::run() /* set the socket nonblocking */ flags= fcntl(ip_socket, F_GETFL, 0); fcntl(ip_socket, F_SETFL, flags | O_NONBLOCK); - /* make shure that instances won't be listening our sockets */ + /* make sure that instances won't be listening our sockets */ flags= fcntl(ip_socket, F_GETFD, 0); fcntl(ip_socket, F_SETFD, flags | FD_CLOEXEC); @@ -184,7 +184,7 @@ void Listener_thread::run() /* set the socket nonblocking */ flags= fcntl(unix_socket, F_GETFL, 0); fcntl(unix_socket, F_SETFL, flags | O_NONBLOCK); - /* make shure that instances won't be listening our sockets */ + /* make sure that instances won't be listening our sockets */ flags= fcntl(unix_socket, F_GETFD, 0); fcntl(unix_socket, F_SETFD, flags | FD_CLOEXEC); } @@ -253,6 +253,7 @@ void Listener_thread::run() log_info("Listener_thread::run(): shutdown requested, exiting..."); close(unix_socket); + close(ip_socket); unlink(unix_socket_address.sun_path); } diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 1c23aa602d4..60b233bdaac 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -65,7 +65,9 @@ void manager(const Options &options) */ User_map user_map; - Instance_map instance_map; + Instance_map instance_map(options.default_mysqld_path, + options.default_admin_user, + options.default_admin_password); Guardian_thread guardian_thread(thread_registry, &instance_map, options.monitoring_interval); @@ -73,16 +75,10 @@ void manager(const Options &options) Listener_thread_args listener_args(thread_registry, options, user_map, instance_map); - instance_map.mysqld_path= options.default_mysqld_path; - instance_map.user= options.default_admin_user; - instance_map.password= options.default_admin_password; instance_map.guardian= &guardian_thread; - - if (instance_map.load()) - return; - - if (user_map.load(options.password_file_name)) + if (instance_map.init() || user_map.init() || instance_map.load() || + user_map.load(options.password_file_name)) return; /* write pid file */ diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc index 2f1f95a5f05..581157ccd72 100644 --- a/server-tools/instance-manager/protocol.cc +++ b/server-tools/instance-manager/protocol.cc @@ -99,16 +99,22 @@ char *net_store_length(char *pkg, uint length) } -void store_to_string(Buffer *buf, const char *string, uint *position) +int store_to_string(Buffer *buf, const char *string, uint *position) { uint currpos; uint string_len; string_len= strlen(string); - buf->reserve(*position, 2); + if (buf->reserve(*position, 2)) + goto err; currpos= (net_store_length(buf->buffer + *position, string_len) - buf->buffer); - buf->append(currpos, string, string_len); + if (buf->append(currpos, string, string_len)) + goto err; *position= *position + string_len + (currpos - *position); + + return 0; +err: + return 1; } @@ -134,7 +140,8 @@ int send_fields(struct st_net *net, LIST *fields) /* send the number of fileds */ net_store_length(small_buff, (uint) list_length(fields)); - my_net_write(net, small_buff, (uint) 1); + if (my_net_write(net, small_buff, (uint) 1)) + goto err; while (tmp) { @@ -147,7 +154,8 @@ int send_fields(struct st_net *net, LIST *fields) store_to_string(&send_buff, (char *) "", &position); /* table name alias */ store_to_string(&send_buff, field->name, &position); /* column name */ store_to_string(&send_buff, field->name, &position); /* column name alias */ - send_buff.reserve(position, 12); + if (send_buff.reserve(position, 12)) + goto err; send_buff.buffer[position++]= 12; int2store(send_buff.buffer + position, 1); /* charsetnr */ int4store(send_buff.buffer + position + 2, field->length); /* field length */ @@ -162,7 +170,7 @@ int send_fields(struct st_net *net, LIST *fields) tmp= rest(tmp); } - if ( my_net_write(net, eof_buff, 1)) + if (my_net_write(net, eof_buff, 1)) goto err; return 0; diff --git a/server-tools/instance-manager/protocol.h b/server-tools/instance-manager/protocol.h index 7bce0e35b5b..1102f95ff8f 100644 --- a/server-tools/instance-manager/protocol.h +++ b/server-tools/instance-manager/protocol.h @@ -36,7 +36,7 @@ int send_fields(struct st_net *net, LIST *fields); char *net_store_length(char *pkg, uint length); -void store_to_string(Buffer *buf, const char *string, uint *position); +int store_to_string(Buffer *buf, const char *string, uint *position); int send_eof(struct st_net *net); diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index 7fff324a521..b921152d858 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -93,13 +93,16 @@ static void delete_user(void *u) C_MODE_END -User_map::User_map() +int User_map::init() { enum { START_HASH_SIZE = 16 }; - hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, - get_user_key, delete_user, 0); + if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, + get_user_key, delete_user, 0)) + return 1; + return 0; } + User_map::~User_map() { hash_free(&hash); @@ -134,7 +137,8 @@ int User_map::load(const char *password_file_name) while (fgets(line, sizeof(line), file)) { /* skip comments and empty lines */ - if (line[0] == '#' || line[0] == '\n' && line[1] == '\0') + if (line[0] == '#' || line[0] == '\n' && + (line[1] == '\0' || line[1] == '\r')) continue; if ((user= new User) == 0) goto done; diff --git a/server-tools/instance-manager/user_map.h b/server-tools/instance-manager/user_map.h index acee0b3c02b..dcd2fd1494d 100644 --- a/server-tools/instance-manager/user_map.h +++ b/server-tools/instance-manager/user_map.h @@ -31,9 +31,9 @@ class User_map { public: - User_map(); ~User_map(); + int init(); int load(const char *password_file_name); int authenticate(const char *user_name, uint length, const char *scrambled_password, From bb63229331d461baedecd0ef439914ff9458e441 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 Nov 2004 02:42:22 +0300 Subject: [PATCH 07/11] linking problem fix server-tools/instance-manager/Makefile.am: fix that should solve the linking problem (libmysqlclient conflicts with libmysys) server-tools/instance-manager/instance_map.cc: propagae phtead_mutex_* functions from the wrapper --- server-tools/instance-manager/Makefile.am | 32 ++++++++++++------- server-tools/instance-manager/instance_map.cc | 4 +-- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 71f2a5d66ce..0c2c6230ac3 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -23,7 +23,7 @@ DEFS= -DMYSQL_INSTANCE_MANAGER # default_options.h, generated from default_options.h.in) # See automake/autoconf docs for details -noinst_LIBRARIES= liboptions.a libnet.a +noinst_LIBRARIES= liboptions.a libnet.a libalarm.a liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_PID_FILE_NAME="$(localstatedir)/mysqlmanager.pid" \ @@ -45,7 +45,14 @@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc nodist_libnet_a_SOURCES= password.c pack.c sql_state.c net_serv.cc nodist_libnet_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER -CLEANFILES= net_serv.cc password.c pack.c sql_state.c +nodist_libalarm_a_SOURCES= thr_alarm.c +nodist_libalarm_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER +libalarm_a_LIBADD= $(top_builddir)/mysys/mf_qsort2.o \ + $(top_builddir)/mysys/queues.o \ + $(top_builddir)/mysys/my_new.o + + +CLEANFILES= net_serv.cc password.c pack.c sql_state.c thr_alarm.c net_serv.cc: Makefile rm -f $(srcdir)/net_serv.cc @@ -63,32 +70,35 @@ sql_state.c: Makefile rm -f $(srcdir)/sql_state.c @LN_CP_F@ $(top_srcdir)/sql/sql_state.c $(srcdir)/sql_state.c +thr_alarm.c: Makefile + rm -f $(srcdir)/thr_alarm.c + @LN_CP_F@ $(top_srcdir)/mysys/thr_alarm.c $(srcdir)/thr_alarm.c + bin_PROGRAMS= mysqlmanager -mysqlmanager_SOURCES= mysqlmanager.cc manager.h manager.cc log.h log.cc \ +mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ + manager.h manager.cc log.h log.cc \ thread_registry.h thread_registry.cc \ - listener.h listener.cc \ + listener.h listener.cc protocol.h protocol.cc \ mysql_connection.h mysql_connection.cc \ - protocol.h protocol.cc \ user_map.h user_map.cc \ messages.h messages.cc \ $(top_srcdir)/sql/sql_string.cc \ - command.h command.cc \ commands.h commands.cc \ factory.h factory.cc \ instance.h instance.cc \ instance_map.h instance_map.cc\ instance_options.h instance_options.cc \ buffer.h buffer.cc parse.cc parse.h \ - guardian.cc guardian.h mysql_manager_error.h + guardian.cc guardian.h common_structures.h \ + mysql_manager_error.h mysqlmanager_LDADD= liboptions.a \ libnet.a \ + libalarm.a \ $(top_builddir)/vio/libvio.a \ - $(top_builddir)/mysys/libmysys.a \ - $(top_builddir)/strings/libmystrings.a \ - $(top_builddir)/dbug/libdbug.a \ - $(top_builddir)/libmysql/libmysqlclient.la + $(top_builddir)/libmysql_r/libmysqlclient_r.la \ + $(top_builddir)/dbug/libdbug.a -lz tags: diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 12f0c799d50..3a0d408dc1c 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -141,13 +141,13 @@ Instance_map::~Instance_map() int Instance_map::lock() { - pthread_mutex_lock(&LOCK_instance_map); + return pthread_mutex_lock(&LOCK_instance_map); } int Instance_map::unlock() { - pthread_mutex_unlock(&LOCK_instance_map); + return pthread_mutex_unlock(&LOCK_instance_map); } From 598bdc38b1bcdb71a43528ded08c2c1aacd40074 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 Nov 2004 11:24:14 +0300 Subject: [PATCH 08/11] protability fix server-tools/instance-manager/Makefile.am: portability fix BitKeeper/etc/ignore: Added server-tools/instance-manager/thr_alarm.c to the ignore list --- .bzrignore | 1 + server-tools/instance-manager/Makefile.am | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.bzrignore b/.bzrignore index c83b4ed4388..47ba7d06bc4 100644 --- a/.bzrignore +++ b/.bzrignore @@ -939,3 +939,4 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl +server-tools/instance-manager/thr_alarm.c diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 0c2c6230ac3..d485a8798f5 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -47,9 +47,9 @@ nodist_libnet_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER nodist_libalarm_a_SOURCES= thr_alarm.c nodist_libalarm_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER -libalarm_a_LIBADD= $(top_builddir)/mysys/mf_qsort2.o \ - $(top_builddir)/mysys/queues.o \ - $(top_builddir)/mysys/my_new.o +libalarm_a_LIBADD= $(top_builddir)/mysys/mf_qsort2.$(OBJEXT) \ + $(top_builddir)/mysys/queues.$(OBJEXT) \ + $(top_builddir)/mysys/my_new.$(OBJEXT) CLEANFILES= net_serv.cc password.c pack.c sql_state.c thr_alarm.c From 4f32ec18822f90ff118f4cbe655b80f977d7077f Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Nov 2004 02:14:56 +0300 Subject: [PATCH 09/11] fix for the IM linking problem BitKeeper/etc/ignore: Added mysql-5.0.2-alpha.tar.gz server-tools/instance-manager/client.c server-tools/instance-manager/client_settings.h server-tools/instance-manager/errmsg.c to the ignore list server-tools/instance-manager/instance_map.cc: Comment updated server-tools/instance-manager/priv.cc: added variables needed by net_serv.cc server-tools/instance-manager/priv.h: declared variables needed by net_serv.cc sql/net_serv.cc: added some IM-specific defines --- .bzrignore | 4 ++ server-tools/instance-manager/Makefile.am | 54 ++++++++----------- server-tools/instance-manager/client_func.c | 32 +++++++++++ server-tools/instance-manager/instance_map.cc | 7 ++- server-tools/instance-manager/priv.cc | 5 ++ server-tools/instance-manager/priv.h | 4 ++ sql/net_serv.cc | 25 ++++----- 7 files changed, 81 insertions(+), 50 deletions(-) create mode 100644 server-tools/instance-manager/client_func.c diff --git a/.bzrignore b/.bzrignore index 47ba7d06bc4..dec7eb8ffdf 100644 --- a/.bzrignore +++ b/.bzrignore @@ -940,3 +940,7 @@ vio/test-sslclient vio/test-sslserver vio/viotest-ssl server-tools/instance-manager/thr_alarm.c +mysql-5.0.2-alpha.tar.gz +server-tools/instance-manager/client.c +server-tools/instance-manager/client_settings.h +server-tools/instance-manager/errmsg.c diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index d485a8798f5..4c3a772111a 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -16,14 +16,14 @@ INCLUDES= -I$(top_srcdir)/include -DEFS= -DMYSQL_INSTANCE_MANAGER +DEFS= -DMYSQL_INSTANCE_MANAGER -DMYSQL_SERVER # As all autoconf variables depend from ${prefix} and being resolved only when # make is run, we can not put these defines to a header file (e.g. to # default_options.h, generated from default_options.h.in) # See automake/autoconf docs for details -noinst_LIBRARIES= liboptions.a libnet.a libalarm.a +noinst_LIBRARIES= liboptions.a libnet.a liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_PID_FILE_NAME="$(localstatedir)/mysqlmanager.pid" \ @@ -34,7 +34,7 @@ liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_USER="root" \ -DDEFAULT_PASSWORD="" \ -DDEFAULT_MONITORING_INTERVAL="5" \ - -DDEFAULT_PORT="3406" \ + -DDEFAULT_PORT="33006" \ -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc @@ -42,37 +42,28 @@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc # MySQL sometimes uses symlinks to reuse code # All symlinked files are grouped in libnet.a -nodist_libnet_a_SOURCES= password.c pack.c sql_state.c net_serv.cc -nodist_libnet_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER +nodist_libnet_a_SOURCES= net_serv.cc client.c errmsg.c +libnet_a_LIBADD= $(top_builddir)/sql/password.$(OBJEXT) \ + $(top_builddir)/sql/pack.$(OBJEXT) \ + $(top_builddir)/sql/sql_state.$(OBJEXT) -nodist_libalarm_a_SOURCES= thr_alarm.c -nodist_libalarm_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER -libalarm_a_LIBADD= $(top_builddir)/mysys/mf_qsort2.$(OBJEXT) \ - $(top_builddir)/mysys/queues.$(OBJEXT) \ - $(top_builddir)/mysys/my_new.$(OBJEXT) - - -CLEANFILES= net_serv.cc password.c pack.c sql_state.c thr_alarm.c +CLEANFILES= net_serv.cc client.c client_settings.h errmsg.c net_serv.cc: Makefile rm -f $(srcdir)/net_serv.cc @LN_CP_F@ $(top_srcdir)/sql/net_serv.cc $(srcdir)/net_serv.cc -password.c: Makefile - rm -f $(srcdir)/password.c - @LN_CP_F@ $(top_srcdir)/sql/password.c $(srcdir)/password.c +client.c: Makefile + rm -f $(srcdir)/client.c + @LN_CP_F@ $(top_srcdir)/sql-common/client.c $(srcdir)/client.c -pack.c: Makefile - rm -f $(srcdir)/pack.c - @LN_CP_F@ $(top_srcdir)/sql-common/pack.c $(srcdir)/pack.c +errmsg.c: Makefile + rm -f $(srcdir)/errmsg.c + @LN_CP_F@ $(top_srcdir)/libmysql/errmsg.c $(srcdir)/errmsg.c -sql_state.c: Makefile - rm -f $(srcdir)/sql_state.c - @LN_CP_F@ $(top_srcdir)/sql/sql_state.c $(srcdir)/sql_state.c - -thr_alarm.c: Makefile - rm -f $(srcdir)/thr_alarm.c - @LN_CP_F@ $(top_srcdir)/mysys/thr_alarm.c $(srcdir)/thr_alarm.c +client_settings.h: Makefile + rm -f $(srcdir)/client_settings.h + @LN_CP_F@ $(top_srcdir)/sql/client_settings.h $(srcdir)/client_settings.h bin_PROGRAMS= mysqlmanager @@ -90,15 +81,16 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ instance_map.h instance_map.cc\ instance_options.h instance_options.cc \ buffer.h buffer.cc parse.cc parse.h \ - guardian.cc guardian.h common_structures.h \ - mysql_manager_error.h + guardian.cc guardian.h \ + mysql_manager_error.h client_func.c mysqlmanager_LDADD= liboptions.a \ libnet.a \ - libalarm.a \ $(top_builddir)/vio/libvio.a \ - $(top_builddir)/libmysql_r/libmysqlclient_r.la \ - $(top_builddir)/dbug/libdbug.a -lz + $(top_builddir)/mysys/libmysys.a \ + $(top_builddir)/strings/libmystrings.a \ + $(top_builddir)/dbug/libdbug.a \ + @openssl_libs@ @ZLIB_LIBS@ tags: diff --git a/server-tools/instance-manager/client_func.c b/server-tools/instance-manager/client_func.c new file mode 100644 index 00000000000..a7ff1d27a8f --- /dev/null +++ b/server-tools/instance-manager/client_func.c @@ -0,0 +1,32 @@ +#include +#include +#include + +/* + Currently we cannot use libmysqlclient directly becouse of the linking + issues. Here we provide needed libmysqlclient functions. + TODO: to think how to use libmysqlclient code instead of copy&paste. + The other possible solution is to use simple_command directly. +*/ + +const char * STDCALL +mysql_get_server_info(MYSQL *mysql) +{ + return((char*) mysql->server_version); +} + +int STDCALL +mysql_ping(MYSQL *mysql) +{ + DBUG_ENTER("mysql_ping"); + DBUG_RETURN(simple_command(mysql,COM_PING,0,0,0)); +} + +int STDCALL +mysql_shutdown(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level) +{ + uchar level[1]; + DBUG_ENTER("mysql_shutdown"); + level[0]= (uchar) shutdown_level; + DBUG_RETURN(simple_command(mysql, COM_SHUTDOWN, (char *)level, 1, 0)); +} diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 3a0d408dc1c..41b23c5dd00 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -27,10 +27,9 @@ #include /* - TODO: Currently there are some mysql-connection specific functions. - As we are going to suppost different types of connections, we shouldn't - have them here in future. To avoid it we could put such - connection-specific functions to the Command-derived class instead. + Note: As we are going to suppost different types of connections, + we shouldn't have connection-specific functions. To avoid it we could + put such functions to the Command-derived class instead. The command could be easily constructed for a specific connection if we would provide a special factory for each connection. */ diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc index e449df9f540..8112ebd41d8 100644 --- a/server-tools/instance-manager/priv.cc +++ b/server-tools/instance-manager/priv.cc @@ -32,3 +32,8 @@ unsigned long net_write_timeout= 60; // same as in mysqld unsigned long net_retry_count= 10; // same as in mysqld +/* needed by net_serv.cc */ +unsigned int test_flags= 0; +unsigned long bytes_sent = 0L, bytes_received = 0L; +unsigned long mysqld_net_retry_count = 10L; +unsigned long open_files_limit; diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h index 73ee87552c8..8014df7260c 100644 --- a/server-tools/instance-manager/priv.h +++ b/server-tools/instance-manager/priv.h @@ -56,5 +56,9 @@ extern unsigned long net_write_timeout; */ extern unsigned long net_retry_count; +extern unsigned int test_flags; +extern unsigned long bytes_sent, bytes_received; +extern unsigned long mysqld_net_retry_count; +extern unsigned long open_files_limit; #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 0a728440e4d..ed4cc6a5942 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -78,11 +78,11 @@ my_bool net_flush(NET *net); can't normally do this the client should have a bigger max_allowed_packet. */ -#if (defined(__WIN__) || (!defined(MYSQL_SERVER) && !defined(MYSQL_INSTANCE_MANAGER))) +#if defined(__WIN__) || !defined(MYSQL_SERVER) /* The following is because alarms doesn't work on windows. */ #define NO_ALARM #endif - + #ifndef NO_ALARM #include "my_pthread.h" void sql_print_error(const char *format,...); @@ -93,7 +93,6 @@ void sql_print_error(const char *format,...); #include "thr_alarm.h" #ifdef MYSQL_SERVER -#define USE_QUERY_CACHE /* The following variables/functions should really not be declared extern, but as it's hard to include mysql_priv.h here, we have to @@ -102,9 +101,14 @@ void sql_print_error(const char *format,...); extern uint test_flags; extern ulong bytes_sent, bytes_received, net_big_packet_count; extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; +#ifndef MYSQL_INSTANCE_MANAGER extern void query_cache_insert(NET *net, const char *packet, ulong length); +#define USE_QUERY_CACHE #define update_statistics(A) A -#else +#endif /* MYSQL_INSTANCE_MANGER */ +#endif /* defined(MYSQL_SERVER) && !defined(MYSQL_INSTANCE_MANAGER) */ + +#if !defined(MYSQL_SERVER) || defined(MYSQL_INSTANCE_MANAGER) #define update_statistics(A) #define thd_increment_bytes_sent() #endif @@ -453,7 +457,8 @@ net_real_write(NET *net,const char *packet,ulong len) my_bool net_blocking = vio_is_blocking(net->vio); DBUG_ENTER("net_real_write"); -#if defined(MYSQL_SERVER) && defined(HAVE_QUERY_CACHE) +#if defined(MYSQL_SERVER) && defined(HAVE_QUERY_CACHE) \ + && !defined(MYSQL_INSTANCE_MANAGER) if (net->query_cache_query != 0) query_cache_insert(net, packet, len); #endif @@ -663,13 +668,6 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed, } #endif /* NO_ALARM */ -/* - If we are inside of the instance manageer, we need to simulate mysql - server for the following function. -*/ -#ifdef MYSQL_INSTANCE_MANAGER -#define MYSQL_SERVER -#endif /* Reads one packet to net->buff + net->where_b @@ -859,9 +857,6 @@ end: return(len); } -#ifdef MYSQL_INSTANCE_MANAGER -#undef MYSQL_SERVER -#endif /* Read a packet from the client/server and return it without the internal From 0157ffa5fd491b886586dbb0236fef8ae0af4d49 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Nov 2004 23:18:28 +0300 Subject: [PATCH 10/11] few more fixes - fix makefile and get rid of strncasecmp in favour of my_strnncoll server-tools/instance-manager/Makefile.am: one more makefile fix server-tools/instance-manager/parse.cc: get rid of non-portable strnacasecmp --- server-tools/instance-manager/Makefile.am | 2 +- server-tools/instance-manager/parse.cc | 27 ++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 4c3a772111a..d3702ba9464 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -42,7 +42,7 @@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc # MySQL sometimes uses symlinks to reuse code # All symlinked files are grouped in libnet.a -nodist_libnet_a_SOURCES= net_serv.cc client.c errmsg.c +nodist_libnet_a_SOURCES= net_serv.cc client_settings.h client.c errmsg.c libnet_a_LIBADD= $(top_builddir)/sql/password.$(OBJEXT) \ $(top_builddir)/sql/pack.$(OBJEXT) \ $(top_builddir)/sql/sql_state.$(OBJEXT) diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc index 09a60062946..38e10c7f2f5 100644 --- a/server-tools/instance-manager/parse.cc +++ b/server-tools/instance-manager/parse.cc @@ -31,15 +31,21 @@ enum Token TOK_END }; -static const char *tokens[]= { - "FLUSH", - "INSTANCE", - "INSTANCES", - "OPTIONS", - "START", - "STATUS", - "STOP", - "SHOW", +struct tokens_st +{ + uint length; + const char *tok_name; +}; + +static struct tokens_st tokens[]= { + {5, "FLUSH"}, + {8, "INSTANCE"}, + {9, "INSTANCES"}, + {7, "OPTIONS"}, + {5, "START"}, + {6, "STATUS"}, + {4, "STOP"}, + {4, "SHOW"} }; @@ -76,7 +82,8 @@ inline Token find_token(const char *word, uint word_len) int i= 0; do { - if (strncasecmp(tokens[i], word, word_len) == 0) + if (my_strnncoll(default_charset_info, (const uchar *) tokens[i].tok_name, + tokens[i].length, (const uchar *) word, word_len) == 0) break; } while (++i < TOK_NOT_FOUND); From b5536bb962da6751bbc62ef9b7aba720bb04d018 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 Nov 2004 14:53:30 +0300 Subject: [PATCH 11/11] Some minor IM fixes server-tools/instance-manager/Makefile.am: Makefile.am cleanup server-tools/instance-manager/commands.cc: cleanup server-tools/instance-manager/instance_map.cc: fix the problem caused thread deadlock (as load ends up in the find() call which now also locks an instance map mutex) server-tools/instance-manager/listener.cc: portability fix server-tools/instance-manager/manager.cc: Ignore SIGPIPE server-tools/instance-manager/mysqlmanager.cc: cleanup server-tools/instance-manager/options.cc: options renamed --- server-tools/instance-manager/Makefile.am | 17 +++++------------ server-tools/instance-manager/commands.cc | 2 +- server-tools/instance-manager/instance_map.cc | 2 +- server-tools/instance-manager/listener.cc | 1 + server-tools/instance-manager/manager.cc | 3 +++ server-tools/instance-manager/mysqlmanager.cc | 2 +- server-tools/instance-manager/options.cc | 10 +++++----- 7 files changed, 17 insertions(+), 20 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index d3702ba9464..050f9b9bfd2 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -42,25 +42,19 @@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc # MySQL sometimes uses symlinks to reuse code # All symlinked files are grouped in libnet.a -nodist_libnet_a_SOURCES= net_serv.cc client_settings.h client.c errmsg.c +nodist_libnet_a_SOURCES= net_serv.cc client_settings.h libnet_a_LIBADD= $(top_builddir)/sql/password.$(OBJEXT) \ $(top_builddir)/sql/pack.$(OBJEXT) \ - $(top_builddir)/sql/sql_state.$(OBJEXT) + $(top_builddir)/sql/sql_state.$(OBJEXT) \ + $(top_builddir)/sql/mini_client_errors.$(OBJEXT)\ + $(top_builddir)/sql/client.$(OBJEXT) -CLEANFILES= net_serv.cc client.c client_settings.h errmsg.c +CLEANFILES= net_serv.cc client_settings.h net_serv.cc: Makefile rm -f $(srcdir)/net_serv.cc @LN_CP_F@ $(top_srcdir)/sql/net_serv.cc $(srcdir)/net_serv.cc -client.c: Makefile - rm -f $(srcdir)/client.c - @LN_CP_F@ $(top_srcdir)/sql-common/client.c $(srcdir)/client.c - -errmsg.c: Makefile - rm -f $(srcdir)/errmsg.c - @LN_CP_F@ $(top_srcdir)/libmysql/errmsg.c $(srcdir)/errmsg.c - client_settings.h: Makefile rm -f $(srcdir)/client_settings.h @LN_CP_F@ $(top_srcdir)/sql/client_settings.h $(srcdir)/client_settings.h @@ -74,7 +68,6 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ mysql_connection.h mysql_connection.cc \ user_map.h user_map.cc \ messages.h messages.cc \ - $(top_srcdir)/sql/sql_string.cc \ commands.h commands.cc \ factory.h factory.cc \ instance.h instance.cc \ diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 50f7bea7daf..2ac97382aa8 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -408,7 +408,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id) stop_guard(instance); if (err_code= instance->stop()) return err_code; - + printf("instance was stopped\n"); net_send_ok(net, connection_id); return 0; } diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 41b23c5dd00..355b51269d5 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -158,8 +158,8 @@ int Instance_map::flush_instances() hash_free(&hash); hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, get_instance_key, delete_instance, 0); - rc= load(); pthread_mutex_unlock(&LOCK_instance_map); + rc= load(); return rc; } diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index cb650e7cf9d..15f57e7e595 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include "thread_registry.h" #include "options.h" diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 60b233bdaac..07d4f1ed33e 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -90,6 +90,7 @@ void manager(const Options &options) sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGPIPE); sigaddset(&mask, SIGHUP); /* We want this signal to be blocked in all theads but the signal @@ -159,6 +160,8 @@ void manager(const Options &options) */ guardian_thread.start(); + signal(SIGPIPE, SIG_IGN); + while (!shutdown_complete) { sigwait(&mask, &signo); diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index bd8f3c6b870..78fa3ac565b 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -179,7 +179,7 @@ static volatile sig_atomic_t is_terminated= 0; void terminate(int signo) { - is_terminated= signo; + is_terminated= signo; } diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 01d83e2d994..05493e10ad8 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -79,7 +79,7 @@ static struct my_option my_long_options[] = (gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name, 0, GET_STR, REQUIRED_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, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, @@ -93,24 +93,24 @@ static struct my_option my_long_options[] = (gptr *) &Options::password_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "default_mysqld_path", OPT_MYSQLD_PATH, "Where to look for MySQL" + { "default-mysqld-path", OPT_MYSQLD_PATH, "Where to look for MySQL" " Server binary.", (gptr *) &Options::default_mysqld_path, (gptr *) &Options::default_mysqld_path, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "default_admin_user", OPT_DEFAULT_ADMIN_USER, "Username to shutdown MySQL" + { "default-admin-user", OPT_DEFAULT_ADMIN_USER, "Username to shutdown MySQL" " instances.", (gptr *) &Options::default_admin_user, (gptr *) &Options::default_admin_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "default_admin_password", OPT_DEFAULT_ADMIN_PASSWORD, "Password to" + { "default-admin-password", OPT_DEFAULT_ADMIN_PASSWORD, "Password to" "shutdown MySQL instances.", (gptr *) &Options::default_admin_password, (gptr *) &Options::default_admin_password, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "monitoring_interval", OPT_MONITORING_INTERVAL, "Interval to monitor instances" + { "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor instances" " in seconds.", (gptr *) &Options::monitoring_interval, (gptr *) &Options::monitoring_interval,