summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <petr@mysql.com>2004-10-27 10:55:10 +0400
committerunknown <petr@mysql.com>2004-10-27 10:55:10 +0400
commit573f1bb8a14695154f14064a64859603b0c951ec (patch)
treeb557ab510173a5cb02c0f3f10dbbcb72e5a4f54a
parent0a52a67566c3b62a3b4d17f218edeca504cb39a3 (diff)
parent825f58e9482336e657ff9835384eca4412b00c7c (diff)
downloadmariadb-git-573f1bb8a14695154f14064a64859603b0c951ec.tar.gz
Merge mysql.com:/home/cps/mysql/trees/mysql-5.0
into mysql.com:/home/cps/mysql/devel/im/prereview/mysql-5.0
-rw-r--r--server-tools/instance-manager/Makefile.am94
-rw-r--r--server-tools/instance-manager/buffer.cc92
-rw-r--r--server-tools/instance-manager/buffer.h57
-rw-r--r--server-tools/instance-manager/command.cc33
-rw-r--r--server-tools/instance-manager/command.h47
-rw-r--r--server-tools/instance-manager/commands.cc404
-rw-r--r--server-tools/instance-manager/commands.h131
-rw-r--r--server-tools/instance-manager/factory.cc60
-rw-r--r--server-tools/instance-manager/factory.h45
-rw-r--r--server-tools/instance-manager/guardian.cc213
-rw-r--r--server-tools/instance-manager/guardian.h86
-rw-r--r--server-tools/instance-manager/instance.cc154
-rw-r--r--server-tools/instance-manager/instance.h60
-rw-r--r--server-tools/instance-manager/instance_map.cc245
-rw-r--r--server-tools/instance-manager/instance_map.h90
-rw-r--r--server-tools/instance-manager/instance_options.cc212
-rw-r--r--server-tools/instance-manager/instance_options.h77
-rw-r--r--server-tools/instance-manager/listener.cc352
-rw-r--r--server-tools/instance-manager/listener.h31
-rw-r--r--server-tools/instance-manager/log.cc6
-rw-r--r--server-tools/instance-manager/log.h9
-rw-r--r--server-tools/instance-manager/manager.cc152
-rw-r--r--server-tools/instance-manager/manager.h6
-rw-r--r--server-tools/instance-manager/messages.cc73
-rw-r--r--server-tools/instance-manager/messages.h26
-rw-r--r--server-tools/instance-manager/mysql_connection.cc388
-rw-r--r--server-tools/instance-manager/mysql_connection.h60
-rw-r--r--server-tools/instance-manager/mysql_manager_error.h27
-rw-r--r--server-tools/instance-manager/mysqlmanager.cc67
-rw-r--r--server-tools/instance-manager/options.cc88
-rw-r--r--server-tools/instance-manager/options.h12
-rw-r--r--server-tools/instance-manager/parse.cc200
-rw-r--r--server-tools/instance-manager/parse.h23
-rw-r--r--server-tools/instance-manager/priv.cc34
-rw-r--r--server-tools/instance-manager/priv.h60
-rw-r--r--server-tools/instance-manager/protocol.cc171
-rw-r--r--server-tools/instance-manager/protocol.h43
-rw-r--r--server-tools/instance-manager/thread_registry.cc206
-rw-r--r--server-tools/instance-manager/thread_registry.h116
-rw-r--r--server-tools/instance-manager/user_map.cc172
-rw-r--r--server-tools/instance-manager/user_map.h45
-rw-r--r--sql/net_serv.cc14
42 files changed, 4337 insertions, 144 deletions
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 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
+
+CLEANFILES= net_serv.cc password.c pack.c sql_state.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
+
+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
-liboptions_a_SOURCES= options.h options.cc
+bin_PROGRAMS= mysqlmanager
-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_SOURCES= mysqlmanager.cc manager.h manager.cc log.h log.cc \
- listener.h listener.cc \
- thread_repository.h thread_repository.cc
+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
-mysqlmanager_LDADD= liboptions.a \
- $(top_builddir)/mysys/libmysys.a \
- $(top_builddir)/strings/libmystrings.a \
- $(top_builddir)/dbug/libdbug.a
-tags:
+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..212260bf9e0
--- /dev/null
+++ b/server-tools/instance-manager/buffer.cc
@@ -0,0 +1,92 @@
+/* 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 <m_string.h>
+
+
+/*
+ Puts the given string to the buffer.
+
+ SYNOPSYS
+ append()
+ position start position in the buffer
+ string string to be put in the buffer
+ len_arg the length of the string. This way we can avoid some
+ strlens.
+
+ DESCRIPTION
+
+ The method puts a string into the buffer, starting from position .
+ In the case when the buffer is too small it reallocs the buffer. The
+ total size of the buffer is restricted with 16.
+
+ RETURN
+ 0 - ok
+ 1 - The buffer came to 16Mb barrier
+*/
+
+int Buffer::append(uint position, const char *string, uint len_arg)
+{
+ if (reserve(position, len_arg))
+ return 1;
+
+ strnmov(buffer + position, string, len_arg);
+ return 0;
+}
+
+
+/*
+ Checks whether the current buffer size is ok to put a string of the length
+ "len_arg" starting from "position" and reallocs it if no.
+
+ SYNOPSYS
+ reserve()
+ position the number starting byte on the buffer to store a buffer
+ len_arg the length of the string.
+
+ DESCRIPTION
+
+ The method checks whether it is possible to 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::reserve(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..66860bd67b5
--- /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 <my_global.h>
+
+#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 };
+ size_t buffer_size;
+public:
+ Buffer()
+ {
+ buffer=(char *) malloc(BUFFER_INITIAL_SIZE);
+ buffer_size= BUFFER_INITIAL_SIZE;
+ }
+
+ ~Buffer()
+ {
+ free(buffer);
+ }
+
+public:
+ char *buffer;
+ int append(uint position, 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
new file mode 100644
index 00000000000..818c4b0cae2
--- /dev/null
+++ b/server-tools/instance-manager/command.cc
@@ -0,0 +1,33 @@
+/* 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"
+
+
+Command::Command(Instance_map *instance_map_arg)
+ :instance_map(instance_map_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..8ae4e33b92f
--- /dev/null
+++ b/server-tools/instance-manager/command.h
@@ -0,0 +1,47 @@
+#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 <my_global.h>
+
+/* Class responsible for allocation of im commands. */
+
+class Instance_map;
+
+/*
+ Command - entry point for any command.
+ GangOf4: 'Command' design pattern
+*/
+
+class Command
+{
+public:
+ Command(Instance_map *instance_map_arg= 0);
+ virtual ~Command();
+
+ /* method of executing: */
+ virtual int execute(struct st_net *net, ulong connection_id) = 0;
+
+protected:
+ 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
new file mode 100644
index 00000000000..102a3df00e5
--- /dev/null
+++ b/server-tools/instance-manager/commands.cc
@@ -0,0 +1,404 @@
+/* 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.h"
+#include "instance_map.h"
+#include "messages.h"
+#include "protocol.h"
+#include "buffer.h"
+#include <m_string.h>
+
+
+/* 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;
+ Instance_map::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 (do_command(net))
+ return ER_OUT_OF_RESOURCES;
+
+ return 0;
+}
+
+
+/* implementation for Flush_instances: */
+
+int Flush_instances::execute(struct st_net *net, ulong connection_id)
+{
+ if (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(Instance_map *instance_map_arg,
+ const char *name, uint len)
+ :Command(instance_map_arg)
+{
+ Instance *instance;
+
+ /* we make a search here, since we don't want t store the name */
+ if (instance= instance_map->find(name, len))
+ {
+ instance_name= instance->options.instance_name;
+ }
+ else
+ instance_name= NULL;
+}
+
+
+/*
+ The method sends a table with a 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 (do_command(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(Instance_map *instance_map_arg,
+ const char *name, uint len):
+ Command(instance_map_arg)
+{
+ Instance *instance;
+
+ /* we make a search here, since we don't want t store the name */
+ if (instance= instance_map->find(name, len))
+ {
+ instance_name= instance->options.instance_name;
+ }
+ else
+ instance_name= NULL;
+}
+
+
+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 (do_command(net, instance_name))
+ return ER_OUT_OF_RESOURCES;
+ return 0;
+ }
+ else
+ {
+ return ER_BAD_INSTANCE_NAME;
+ }
+}
+
+
+/* Implementation for Start_instance */
+
+Start_instance::Start_instance(Instance_map *instance_map_arg,
+ const char *name, uint len)
+ :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))
+ 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)
+ instance_map->guardian->guard(instance);
+
+ net_send_ok(net, connection_id);
+ return 0;
+ }
+}
+
+
+/* Implementation for Stop_instance: */
+
+Stop_instance::Stop_instance(Instance_map *instance_map_arg,
+ const char *name, uint len)
+ :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))
+ 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)
+ instance_map->guardian->
+ stop_guard(instance);
+ 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..cd13bf09549
--- /dev/null
+++ b/server-tools/instance-manager/commands.h
@@ -0,0 +1,131 @@
+#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(Instance_map *instance_map_arg): Command(instance_map_arg)
+ {}
+
+ int do_command(struct st_net *net);
+ 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(Instance_map *instance_map_arg): Command(instance_map_arg)
+ {}
+
+ int execute(struct st_net *net, ulong connection_id);
+};
+
+
+/*
+ Print status of an instance.
+ Grammar: SHOW ISTANCE STATUS <instance_name>
+*/
+
+class Show_instance_status : public Command
+{
+public:
+
+ 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;
+};
+
+
+/*
+ Print options if chosen instance.
+ Grammar: SHOW INSTANCE OPTIONS <instance_name>
+*/
+
+class Show_instance_options : public Command
+{
+public:
+
+ 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);
+ const char *instance_name;
+};
+
+
+/*
+ Start an instance.
+ Grammar: START INSTANCE <instance_name>
+*/
+
+class Start_instance : public Command
+{
+public:
+ 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;
+ Instance *instance;
+};
+
+
+/*
+ Stop an instance.
+ Grammar: STOP INSTANCE <instance_name>
+*/
+
+class Stop_instance : public Command
+{
+public:
+ Stop_instance(Instance_map *instance_map_arg, const char *name, uint len);
+
+ Instance *instance;
+ int execute(struct st_net *net, ulong connection_id);
+ const char *instance_name;
+};
+
+
+/*
+ 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
+{
+public:
+ 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..cde5d0564aa
--- /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 <stdio.h>
+#include <ctype.h>
+
+
+Show_instances *Command_factory::new_Show_instances()
+{
+ return new Show_instances(&instance_map);
+}
+
+Flush_instances *Command_factory::new_Flush_instances()
+{
+ 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(&instance_map, name, len);
+}
+
+Show_instance_options *Command_factory::
+ new_Show_instance_options(const char *name, uint 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(&instance_map, name, len);
+}
+
+Stop_instance *Command_factory::new_Stop_instance(const char *name, uint len)
+{
+ return new Stop_instance(&instance_map, 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..c7c0f1b0a77
--- /dev/null
+++ b/server-tools/instance-manager/guardian.cc
@@ -0,0 +1,213 @@
+/* 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 "mysql_manager_error.h"
+#include "log.h"
+#include <string.h>
+
+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;
+ starting_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.
+*/
+
+void Guardian_thread::run()
+{
+ Instance *instance;
+ LIST *loop;
+
+ my_thread_init();
+
+ while (!thread_registry.is_shutdown())
+ {
+ pthread_mutex_lock(&LOCK_guardian);
+ loop= guarded_instances;
+ while (loop != NULL)
+ {
+ instance= (Instance *) loop->data;
+ /* instance-> start already checks whether instance is running */
+ 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);
+ }
+
+ my_thread_end();
+}
+
+
+int Guardian_thread::start()
+{
+ Instance *instance;
+ Instance_map::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
+
+ 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(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;
+
+ node= (LIST *) alloc_root(&alloc, sizeof(LIST));
+ if (node == NULL)
+ return 1;
+ /* we store the pointers to instances from the instance_map's MEM_ROOT */
+ node->data= (void *) instance;
+
+ pthread_mutex_lock(&LOCK_guardian);
+ *list= list_add(*list, node);
+ 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(Instance *instance)
+{
+ LIST *node;
+
+ pthread_mutex_lock(&LOCK_guardian);
+ node= guarded_instances;
+
+ while (node != NULL)
+ {
+ /*
+ We compare only pointers, as we always use pointers from the
+ instance_map's MEM_ROOT.
+ */
+ if ((Instance *) node->data == instance)
+ {
+ guarded_instances= list_delete(guarded_instances, node);
+ pthread_mutex_unlock(&LOCK_guardian);
+ return 0;
+ }
+ else
+ node= node->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..0ae2161f1dc
--- /dev/null
+++ b/server-tools/instance-manager/guardian.h
@@ -0,0 +1,86 @@
+#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 <my_global.h>
+#include <my_sys.h>
+#include <my_list.h>
+
+#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 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 };
+};
+
+#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..1b9ce09d782
--- /dev/null
+++ b/server-tools/instance-manager/instance.cc
@@ -0,0 +1,154 @@
+/* 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 "log.h"
+#include <my_sys.h>
+#include <signal.h>
+#include <m_string.h>
+
+
+/*
+ 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())
+ {
+ log_info("trying to start instance %s", options.instance_name);
+ 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 <my_global.h>
+#include <my_sys.h>
+#include <mysql.h>
+#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..b70f622fa73
--- /dev/null
+++ b/server-tools/instance-manager/instance_map.cc
@@ -0,0 +1,245 @@
+/* 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 <m_ctype.h>
+#include <my_sys.h>
+#include <mysql_com.h>
+#include <m_string.h>
+
+/*
+ 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::lock()
+{
+ pthread_mutex_lock(&LOCK_instance_map);
+}
+
+
+int Instance_map::unlock()
+{
+ pthread_mutex_unlock(&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::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;
+}
+
+
+/*--- Implementaton of the Instance map iterator class ---*/
+
+
+void Instance_map::Iterator::go_to_first()
+{
+ current_instance=0;
+}
+
+
+Instance *Instance_map::Iterator::next()
+{
+ if (current_instance < instance_map->hash.records)
+ return (Instance *) hash_element(&instance_map->hash, current_instance++);
+ else
+ return NULL;
+}
+
diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h
new file mode 100644
index 00000000000..21d8c5caa9f
--- /dev/null
+++ b/server-tools/instance-manager/instance_map.h
@@ -0,0 +1,90 @@
+#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 <my_global.h>
+#include <my_sys.h>
+#include <hash.h>
+
+#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
+{
+ 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);
+ Instance *find(uint instance_number);
+
+ int flush_instances();
+ int cleanup();
+ int lock();
+ int unlock();
+
+ 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..8311e4f7bc0
--- /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 <my_sys.h>
+#include <mysql.h>
+#include <signal.h>
+#include <m_string.h>
+
+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);
+
+ 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..5bc46497d2a
--- /dev/null
+++ b/server-tools/instance-manager/instance_options.h
@@ -0,0 +1,77 @@
+#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 <my_global.h>
+#include <my_sys.h>
+
+#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= 1 };
+ 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..4a13dc39ec7 100644
--- a/server-tools/instance-manager/listener.cc
+++ b/server-tools/instance-manager/listener.cc
@@ -1,72 +1,314 @@
+/* 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 <m_string.h>
+#include <mysql.h>
+#include <violite.h>
+#include <sys/un.h>
+
+#include "thread_registry.h"
+#include "options.h"
+#include "instance_map.h"
#include "log.h"
+#include "mysql_connection.h"
-C_MODE_START
-pthread_handler_decl(listener, arg)
+/*
+ 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()
{
- Thread_info info(pthread_self());
- Thread_repository &thread_repository=
- ((Listener_thread_args *) arg)->thread_repository;
- thread_repository.register_thread(&info);
+ enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size
+ int flags;
+ int arg= 1; /* value to be set by setsockopt */
+ /* I. prepare 'listen' sockets */
- while (true)
+ int ip_socket= socket(AF_INET, SOCK_STREAM, 0);
+ if (ip_socket == INVALID_SOCKET)
{
- log_info("listener is alive");
- sleep(2);
- if (thread_repository.is_shutdown())
- break;
+ log_error("Listener_thead::run(): socket(AF_INET) failed, %s",
+ strerror(errno));
+ thread_registry.request_shutdown();
+ return;
}
- log_info("listener(): shutdown requested, exiting...");
- thread_repository.unregister_thread(&info);
- return 0;
+ struct sockaddr_in ip_socket_address;
+ bzero(&ip_socket_address, 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;
+ }
+ /* 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");
+
+ /*--------------------------------------------------------------*/
+ 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;
+ 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,
+ 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);
+ /* 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);
+
+ /* 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);
}
-C_MODE_END
-#if 0
- while (true)
+/*
+ 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)
+{
+ Listener_thread_args *args= (Listener_thread_args *) arg;
+ Listener_thread listener(*args);
+ listener.run();
/*
- 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.
+ args is a stack variable because listener thread lives as long as the
+ manager process itself
*/
+ return 0;
+}
+
+
+C_MODE_END
- 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 <my_global.h>
#include <my_pthread.h>
+
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;
-
- 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) {}
+ Thread_registry &thread_registry;
+ const Options &options;
+ const User_map &user_map;
+ Instance_map &instance_map;
+
+ Listener_thread_args(Thread_registry &thread_registry_arg,
+ const Options &options_arg,
+ const User_map &user_map_arg,
+ Instance_map &instance_map_arg) :
+ thread_registry(thread_registry_arg)
+ ,options(options_arg)
+ ,user_map(user_map_arg)
+ ,instance_map(instance_map_arg)
+ {}
};
-#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..1c23aa602d4 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,77 @@
#include "manager.h"
#include <my_global.h>
+#include <my_sys.h>
+#include <m_string.h>
#include <signal.h>
+#include <thr_alarm.h>
-#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"
+
+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;
+}
-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);
+
+ 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))
+ return;
/* write pid file */
+ if (create_pid_file(options.pid_file_name))
+ return;
/* block signals */
sigset_t mask;
@@ -37,26 +95,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.
+ */
+ guardian_thread.start();
+
+ 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 <my_global.h>
+#include <mysql_com.h>
+
+#include <assert.h>
+
+
+static const char *mysqld_error_message(unsigned sql_errno)
+{
+ switch (sql_errno) {
+ case ER_HANDSHAKE_ERROR:
+ return "Bad handshake";
+ case ER_OUT_OF_RESOURCES:
+ return "Out of memory; Check if mysqld or some other process"
+ " uses all available memory. If not you may have to use"
+ " 'ulimit' to allow mysqld to use more memory or you can"
+ " add more swap space";
+ case ER_ACCESS_DENIED_ERROR:
+ return "Access denied. Bad username/password pair";
+ case ER_NOT_SUPPORTED_AUTH_MODE:
+ return "Client does not support authentication protocol requested by"
+ " server; consider upgrading MySQL client";
+ case ER_UNKNOWN_COM_ERROR:
+ return "Unknown command";
+ case ER_SYNTAX_ERROR:
+ return "You have an error in your command syntax. Check the manual that"
+ " corresponds to your MySQL Instance Manager version for the right"
+ " syntax to use";
+ case ER_BAD_INSTANCE_NAME:
+ return "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..0ebcb0eea8d
--- /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 <mysql.h>
+#include <violite.h>
+#include <mysql_com.h>
+#include <m_string.h>
+#include <my_sys.h>
+
+#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 <my_global.h>
+#include <my_pthread.h>
+
+
+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 <my_global.h>
#include <my_sys.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
-#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/stat.h>
-#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 <my_sys.h>
#include <my_getopt.h>
+#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 <my_global.h>
-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 <string.h>
+
+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..2f1f95a5f05
--- /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 <my_global.h>
+#include <my_sys.h>
+#include <mysql_com.h>
+#include <m_string.h>
+
+#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)
+{
+ uint currpos;
+ uint string_len;
+
+ string_len= strlen(string);
+ buf->reserve(*position, 2);
+ currpos= (net_store_length(buf->buffer + *position, string_len) - buf->buffer);
+ buf->append(currpos, string, string_len);
+ *position= *position + string_len + (currpos - *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.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 */
+ 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 <my_list.h>
+
+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 <assert.h>
+#include <signal.h>
+#include <thr_alarm.h>
+#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 <my_global.h>
+#include <my_pthread.h>
+
+
+/*
+ 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 <mysql_com.h>
+#include <m_string.h>
+
+#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 <my_global.h>
+#include <my_sys.h>
+#include <hash.h>
+
+/*
+ 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