mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
Intermediate commit - just to make new files visible to bk in the new
tree
This commit is contained in:
parent
7590eed389
commit
a8e2db4ecd
42 changed files with 4261 additions and 148 deletions
|
@ -1,30 +1,98 @@
|
|||
# Copyright (C) 2004 MySQL AB
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
INCLUDES= -I$(top_srcdir)/include
|
||||
|
||||
DEFS= -DMYSQL_INSTANCE_MANAGER
|
||||
|
||||
# As all autoconf variables depend from ${prefix} and being resolved only when
|
||||
# make is run, we can't put these defines to a header file (e.g. to
|
||||
# make is run, we can not put these defines to a header file (e.g. to
|
||||
# default_options.h, generated from default_options.h.in)
|
||||
# See automake/autoconf docs for details
|
||||
|
||||
noinst_LIBRARIES= liboptions.a
|
||||
noinst_LIBRARIES= liboptions.a libnet.a
|
||||
|
||||
liboptions_a_CPPFLAGS= $(CPPFLAGS) \
|
||||
-DDEFAULT_PID_FILE_NAME="$(localstatedir)/mysqlmanager.pid" \
|
||||
-DDEFAULT_LOG_FILE_NAME="$(localstatedir)/mysqlmanager.log" \
|
||||
-DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock"
|
||||
-DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" \
|
||||
-DDEFAULT_PASSWORD_FILE_NAME="$(sysconfdir)/mysqlmanager.passwd" \
|
||||
-DDEFAULT_MYSQLD_PATH="$(bindir)/mysqld$(EXEEXT)" \
|
||||
-DDEFAULT_USER="root" \
|
||||
-DDEFAULT_PASSWORD="" \
|
||||
-DDEFAULT_MONITORING_INTERVAL="5" \
|
||||
-DDEFAULT_PORT="3406" \
|
||||
-DPROTOCOL_VERSION=@PROTOCOL_VERSION@
|
||||
|
||||
liboptions_a_SOURCES= options.h options.cc
|
||||
liboptions_a_SOURCES= options.h options.cc priv.h priv.cc
|
||||
|
||||
bin_PROGRAMS = mysqlmanager
|
||||
# MySQL sometimes uses symlinks to reuse code
|
||||
# All symlinked files are grouped in libnet.a
|
||||
|
||||
mysqlmanager_SOURCES= mysqlmanager.cc manager.h manager.cc log.h log.cc \
|
||||
listener.h listener.cc \
|
||||
thread_repository.h thread_repository.cc
|
||||
nodist_libnet_a_SOURCES= password.c pack.c sql_state.c net_serv.cc
|
||||
nodist_libnet_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER
|
||||
|
||||
mysqlmanager_LDADD= liboptions.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/strings/libmystrings.a \
|
||||
$(top_builddir)/dbug/libdbug.a
|
||||
CLEANFILES= net_serv.cc password.c pack.c sql_state.c
|
||||
|
||||
tags:
|
||||
net_serv.cc: Makefile
|
||||
rm -f $(srcdir)/net_serv.cc
|
||||
@LN_CP_F@ $(top_srcdir)/sql/net_serv.cc $(srcdir)/net_serv.cc
|
||||
|
||||
password.c: Makefile
|
||||
rm -f $(srcdir)/password.c
|
||||
@LN_CP_F@ $(top_srcdir)/sql/password.c $(srcdir)/password.c
|
||||
|
||||
pack.c: Makefile
|
||||
rm -f $(srcdir)/pack.c
|
||||
@LN_CP_F@ $(top_srcdir)/sql-common/pack.c $(srcdir)/pack.c
|
||||
|
||||
sql_state.c: Makefile
|
||||
rm -f $(srcdir)/sql_state.c
|
||||
@LN_CP_F@ $(top_srcdir)/sql/sql_state.c $(srcdir)/sql_state.c
|
||||
|
||||
bin_PROGRAMS= mysqlmanager
|
||||
|
||||
mysqlmanager_SOURCES= mysqlmanager.cc manager.h manager.cc log.h log.cc \
|
||||
thread_registry.h thread_registry.cc \
|
||||
listener.h listener.cc \
|
||||
mysql_connection.h mysql_connection.cc \
|
||||
protocol.h protocol.cc \
|
||||
user_map.h user_map.cc \
|
||||
messages.h messages.cc \
|
||||
$(top_srcdir)/sql/sql_string.cc \
|
||||
command.h command.cc \
|
||||
commands.h commands.cc \
|
||||
factory.h factory.cc \
|
||||
instance.h instance.cc \
|
||||
instance_map.h instance_map.cc\
|
||||
instance_options.h instance_options.cc \
|
||||
buffer.h buffer.cc parse.cc parse.h \
|
||||
guardian.cc guardian.h common_structures.h \
|
||||
mysql_manager_error.h
|
||||
|
||||
mysqlmanager_LDADD= liboptions.a \
|
||||
libnet.a \
|
||||
$(top_builddir)/vio/libvio.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/strings/libmystrings.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/libmysql/libmysqlclient.la
|
||||
|
||||
|
||||
tags:
|
||||
ctags -R *.h *.cc
|
||||
|
||||
# Don't update the files from bitkeeper
|
||||
|
|
91
server-tools/instance-manager/buffer.cc
Normal file
91
server-tools/instance-manager/buffer.cc
Normal file
|
@ -0,0 +1,91 @@
|
|||
/* Copyright (C) 2004 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma implementation
|
||||
#endif
|
||||
|
||||
#include "buffer.h"
|
||||
#include <m_string.h>
|
||||
|
||||
|
||||
/*
|
||||
Puts the given string to the buffer.
|
||||
|
||||
SYNOPSYS
|
||||
put_to_buffer()
|
||||
start_pos start position in the buffer
|
||||
string string to be put in the buffer
|
||||
len_arg the length of the string. This way we can avoid some
|
||||
strlens.
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
The method puts a string into the buffer, starting from start_pos .
|
||||
In the case when the buffer is too small it reallocs the buffer. The
|
||||
total size of the buffer is restricted with 16.
|
||||
|
||||
RETURN
|
||||
0 - ok
|
||||
1 - The buffer came to 16Mb barrier
|
||||
*/
|
||||
|
||||
int Buffer::put_to_buffer(char *start_pos, const char *string, uint len_arg)
|
||||
{
|
||||
if (check_and_add(start_pos - buffer, len_arg))
|
||||
return 1;
|
||||
|
||||
strnmov(start_pos, string, len_arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Checks whether the current buffer size is ok to put a string of the length
|
||||
"len_arg" starting from "position" and reallocs it if no.
|
||||
|
||||
SYNOPSYS
|
||||
check_and_add()
|
||||
position the number starting byte on the buffer to store a buffer
|
||||
len_arg the length of the string.
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
The method checks whether it is possible to pus a string of teh "len_arg"
|
||||
length into the buffer, starting from "position" byte. In the case when the
|
||||
buffer is too small it reallocs the buffer. The total size of the buffer is
|
||||
restricted with 16 Mb.
|
||||
|
||||
RETURN
|
||||
0 - ok
|
||||
1 - The buffer came to 16Mb barrier
|
||||
*/
|
||||
|
||||
int Buffer::check_and_add(uint position, uint len_arg)
|
||||
{
|
||||
if (position + len_arg >= MAX_BUFFER_SIZE)
|
||||
return 1;
|
||||
|
||||
if (position + len_arg>= buffer_size)
|
||||
{
|
||||
buffer= (char *) realloc(buffer,
|
||||
min(MAX_BUFFER_SIZE,
|
||||
max((uint) buffer_size*1.5,
|
||||
position + len_arg)));
|
||||
buffer_size= (uint) buffer_size*1.5;
|
||||
}
|
||||
return 0;
|
||||
}
|
57
server-tools/instance-manager/buffer.h
Normal file
57
server-tools/instance-manager/buffer.h
Normal file
|
@ -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 };
|
||||
uint buffer_size;
|
||||
public:
|
||||
Buffer()
|
||||
{
|
||||
buffer=(char *) malloc(BUFFER_INITIAL_SIZE);
|
||||
buffer_size= BUFFER_INITIAL_SIZE;
|
||||
}
|
||||
|
||||
~Buffer()
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
public:
|
||||
char *buffer;
|
||||
int put_to_buffer(char *start_pos, const char *string, uint len_arg);
|
||||
int check_and_add(uint position, uint len_arg);
|
||||
};
|
||||
|
||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H */
|
43
server-tools/instance-manager/command.cc
Normal file
43
server-tools/instance-manager/command.cc
Normal file
|
@ -0,0 +1,43 @@
|
|||
/* Copyright (C) 2004 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma implementation
|
||||
#endif
|
||||
|
||||
#include "command.h"
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <m_ctype.h>
|
||||
#include <m_string.h>
|
||||
#include <mysql_com.h>
|
||||
#include <mysqld_error.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "protocol.h"
|
||||
#include "instance_map.h"
|
||||
|
||||
Command::Command(Command_factory *factory_arg)
|
||||
:factory(factory_arg)
|
||||
{}
|
||||
|
||||
Command::~Command()
|
||||
{}
|
||||
|
||||
#ifdef __GNUC__
|
||||
FIX_GCC_LINKING_PROBLEM
|
||||
#endif
|
49
server-tools/instance-manager/command.h
Normal file
49
server-tools/instance-manager/command.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H
|
||||
#define INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H
|
||||
/* Copyright (C) 2004 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma interface
|
||||
#endif
|
||||
|
||||
#include <my_global.h>
|
||||
|
||||
/* Class responsible for allocation and deallocation of im classes. */
|
||||
|
||||
class Command_factory;
|
||||
|
||||
/*
|
||||
Command - entry point for any command.
|
||||
GangOf4: 'Command' design pattern
|
||||
*/
|
||||
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
Command(Command_factory *factory_arg= 0);
|
||||
virtual ~Command();
|
||||
|
||||
/* method of executing: */
|
||||
virtual int execute(struct st_net *net, ulong connection_id) = 0;
|
||||
|
||||
protected:
|
||||
Command_factory *factory;
|
||||
};
|
||||
|
||||
#define CONST_STR(a) String(a,sizeof(a),&my_charset_latin1)
|
||||
|
||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H */
|
183
server-tools/instance-manager/commands.cc
Normal file
183
server-tools/instance-manager/commands.cc
Normal file
|
@ -0,0 +1,183 @@
|
|||
/* Copyright (C) 2004 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "command.h"
|
||||
#include "factory.h"
|
||||
#include "commands.h"
|
||||
#include "instance.h"
|
||||
#include "instance_map.h"
|
||||
#include "messages.h"
|
||||
|
||||
|
||||
/* implementation for Show_instances: */
|
||||
|
||||
int Show_instances::execute(struct st_net *net, ulong connection_id)
|
||||
{
|
||||
if (factory->instance_map.show_instances(net))
|
||||
return ER_OUT_OF_RESOURCES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* implementation for Flush_instances: */
|
||||
|
||||
int Flush_instances::execute(struct st_net *net, ulong connection_id)
|
||||
{
|
||||
if (factory->instance_map.flush_instances())
|
||||
return ER_OUT_OF_RESOURCES;
|
||||
|
||||
net_send_ok(net, connection_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* implementation for Show_instance_status: */
|
||||
|
||||
Show_instance_status::Show_instance_status(Command_factory *factory,
|
||||
const char *name, uint len)
|
||||
:Command(factory)
|
||||
{
|
||||
Instance *instance;
|
||||
|
||||
/* we make a search here, since we don't want t store the name */
|
||||
if (instance= (factory->instance_map).find(name, len))
|
||||
{
|
||||
instance_name= instance->options.instance_name;
|
||||
}
|
||||
else instance_name= NULL;
|
||||
}
|
||||
|
||||
|
||||
int Show_instance_status::execute(struct st_net *net, ulong connection_id)
|
||||
{
|
||||
if (instance_name != NULL)
|
||||
{
|
||||
if (factory->instance_map.show_instance_status(net, instance_name))
|
||||
return ER_OUT_OF_RESOURCES;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ER_BAD_INSTANCE_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Implementation for Show_instance_options */
|
||||
|
||||
Show_instance_options::Show_instance_options(Command_factory *factory,
|
||||
const char *name, uint len):
|
||||
Command(factory)
|
||||
{
|
||||
Instance *instance;
|
||||
|
||||
/* we make a search here, since we don't want t store the name */
|
||||
if (instance= (factory->instance_map).find(name, len))
|
||||
{
|
||||
instance_name= instance->options.instance_name;
|
||||
}
|
||||
else instance_name= NULL;
|
||||
}
|
||||
|
||||
|
||||
int Show_instance_options::execute(struct st_net *net, ulong connection_id)
|
||||
{
|
||||
if (instance_name != NULL)
|
||||
{
|
||||
if (factory->instance_map.show_instance_options(net, instance_name))
|
||||
return ER_OUT_OF_RESOURCES;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ER_BAD_INSTANCE_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Implementation for Start_instance */
|
||||
|
||||
Start_instance::Start_instance(Command_factory *factory,
|
||||
const char *name, uint len)
|
||||
:Command(factory)
|
||||
{
|
||||
/* we make a search here, since we don't want t store the name */
|
||||
if (instance= (factory->instance_map).find(name, len))
|
||||
instance_name= instance->options.instance_name;
|
||||
}
|
||||
|
||||
|
||||
int Start_instance::execute(struct st_net *net, ulong connection_id)
|
||||
{
|
||||
uint err_code;
|
||||
if (instance == 0)
|
||||
{
|
||||
return ER_BAD_INSTANCE_NAME; /* haven't found an instance */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (err_code= instance->start())
|
||||
return err_code;
|
||||
|
||||
if (instance->options.is_guarded != NULL)
|
||||
factory->instance_map.guardian->guard(instance->options.instance_name,
|
||||
instance->options.instance_name_len);
|
||||
|
||||
net_send_ok(net, connection_id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Implementation for Stop_instance: */
|
||||
|
||||
Stop_instance::Stop_instance(Command_factory *factory,
|
||||
const char *name, uint len)
|
||||
:Command(factory)
|
||||
{
|
||||
/* we make a search here, since we don't want t store the name */
|
||||
if (instance= (factory->instance_map).find(name, len))
|
||||
instance_name= instance->options.instance_name;
|
||||
}
|
||||
|
||||
|
||||
int Stop_instance::execute(struct st_net *net, ulong connection_id)
|
||||
{
|
||||
uint err_code;
|
||||
|
||||
if (instance == 0)
|
||||
{
|
||||
return ER_BAD_INSTANCE_NAME; /* haven't found an instance */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (instance->options.is_guarded != NULL)
|
||||
factory->instance_map.guardian->
|
||||
stop_guard(instance_name, instance->options.instance_name_len);
|
||||
if (err_code= instance->stop())
|
||||
return err_code;
|
||||
|
||||
net_send_ok(net, connection_id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Syntax_error::execute(struct st_net *net, ulong connection_id)
|
||||
{
|
||||
return ER_SYNTAX_ERROR;
|
||||
}
|
129
server-tools/instance-manager/commands.h
Normal file
129
server-tools/instance-manager/commands.h
Normal file
|
@ -0,0 +1,129 @@
|
|||
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H
|
||||
#define INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H
|
||||
/* Copyright (C) 2004 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "instance.h"
|
||||
#include "my_global.h"
|
||||
|
||||
/*
|
||||
Print all instances of this instance manager.
|
||||
Grammar: SHOW ISTANCES
|
||||
*/
|
||||
|
||||
class Show_instances : public Command
|
||||
{
|
||||
public:
|
||||
Show_instances(Command_factory *factory): Command(factory)
|
||||
{}
|
||||
|
||||
int execute(struct st_net *net, ulong connection_id);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Reread configuration file and refresh instance map.
|
||||
Grammar: FLUSH INSTANCES
|
||||
*/
|
||||
|
||||
class Flush_instances : public Command
|
||||
{
|
||||
public:
|
||||
Flush_instances(Command_factory *factory): Command(factory)
|
||||
{}
|
||||
|
||||
int execute(struct st_net *net, ulong connection_id);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Print status of an instance.
|
||||
Grammar: SHOW ISTANCE STATUS <instance_name>
|
||||
*/
|
||||
|
||||
class Show_instance_status : public Command
|
||||
{
|
||||
public:
|
||||
|
||||
Show_instance_status(Command_factory *factory, const char *name, uint len);
|
||||
|
||||
int execute(struct st_net *net, ulong connection_id);
|
||||
const char *instance_name;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Print options if chosen instance.
|
||||
Grammar: SHOW INSTANCE OPTIONS <instance_name>
|
||||
*/
|
||||
|
||||
class Show_instance_options : public Command
|
||||
{
|
||||
public:
|
||||
|
||||
Show_instance_options(Command_factory *factory, const char *name, uint len);
|
||||
|
||||
int execute(struct st_net *net, ulong connection_id);
|
||||
const char *instance_name;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Start an instance.
|
||||
Grammar: START INSTANCE <instance_name>
|
||||
*/
|
||||
|
||||
class Start_instance : public Command
|
||||
{
|
||||
public:
|
||||
Start_instance(Command_factory *factory, const char *name, uint len);
|
||||
|
||||
Instance *instance;
|
||||
int execute(struct st_net *net, ulong connection_id);
|
||||
const char *instance_name;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Stop an instance.
|
||||
Grammar: STOP INSTANCE <instance_name>
|
||||
*/
|
||||
|
||||
class Stop_instance : public Command
|
||||
{
|
||||
public:
|
||||
Stop_instance(Command_factory *factory, const char *name, uint len);
|
||||
|
||||
Instance *instance;
|
||||
int execute(struct st_net *net, ulong connection_id);
|
||||
const char *instance_name;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Syntax error command.
|
||||
*/
|
||||
|
||||
class Syntax_error : public Command
|
||||
{
|
||||
public:
|
||||
Syntax_error()
|
||||
{}
|
||||
|
||||
int execute(struct st_net *net, ulong connection_id);
|
||||
};
|
||||
|
||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H */
|
60
server-tools/instance-manager/factory.cc
Normal file
60
server-tools/instance-manager/factory.cc
Normal file
|
@ -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(this);
|
||||
}
|
||||
|
||||
Flush_instances *Command_factory::new_Flush_instances()
|
||||
{
|
||||
return new Flush_instances(this);
|
||||
}
|
||||
|
||||
Show_instance_status *Command_factory::
|
||||
new_Show_instance_status(const char *name, uint len)
|
||||
{
|
||||
return new Show_instance_status(this, name, len);
|
||||
}
|
||||
|
||||
Show_instance_options *Command_factory::
|
||||
new_Show_instance_options(const char *name, uint len)
|
||||
{
|
||||
return new Show_instance_options(this, name, len);
|
||||
}
|
||||
|
||||
Start_instance *Command_factory::
|
||||
new_Start_instance(const char *name, uint len)
|
||||
{
|
||||
return new Start_instance(this, name, len);
|
||||
}
|
||||
|
||||
Stop_instance *Command_factory::new_Stop_instance(const char *name, uint len)
|
||||
{
|
||||
return new Stop_instance(this, name, len);
|
||||
}
|
||||
|
||||
Syntax_error *Command_factory::new_Syntax_error()
|
||||
{
|
||||
return new Syntax_error();
|
||||
}
|
45
server-tools/instance-manager/factory.h
Normal file
45
server-tools/instance-manager/factory.h
Normal file
|
@ -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 */
|
180
server-tools/instance-manager/guardian.cc
Normal file
180
server-tools/instance-manager/guardian.cc
Normal file
|
@ -0,0 +1,180 @@
|
|||
/* Copyright (C) 2004 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma implementation
|
||||
#endif
|
||||
|
||||
#include "guardian.h"
|
||||
#include "instance_map.h"
|
||||
#include <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;
|
||||
}
|
||||
|
||||
|
||||
Guardian_thread::~Guardian_thread()
|
||||
{
|
||||
/* delay guardian destruction to the moment when no one needs it */
|
||||
pthread_mutex_lock(&LOCK_guardian);
|
||||
free_root(&alloc, MYF(0));
|
||||
thread_registry.unregister_thread(&thread_info);
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
pthread_mutex_destroy(&LOCK_guardian);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Run guardian thread
|
||||
|
||||
SYNOPSYS
|
||||
run()
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
Check for all guarded instances and restart them if needed. If everything
|
||||
is fine go and sleep for some time.
|
||||
|
||||
RETURN
|
||||
The function return no value
|
||||
*/
|
||||
|
||||
void Guardian_thread::run()
|
||||
{
|
||||
Instance *instance;
|
||||
LIST *loop;
|
||||
int i=0;
|
||||
|
||||
my_thread_init();
|
||||
|
||||
while (!thread_registry.is_shutdown())
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_guardian);
|
||||
loop= guarded_instances;
|
||||
while (loop != NULL)
|
||||
{
|
||||
instance= (Instance *) loop->data;
|
||||
if (instance != NULL)
|
||||
{
|
||||
if (!instance->is_running())
|
||||
instance->start();
|
||||
}
|
||||
loop= loop->next;
|
||||
}
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
sleep(monitoring_interval);
|
||||
}
|
||||
|
||||
my_thread_end();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Start instance guarding
|
||||
|
||||
SYNOPSYS
|
||||
guard()
|
||||
instance_name the name of the instance to be guarded
|
||||
name_len the length of the name
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
The instance is added to the list of guarded instances.
|
||||
|
||||
RETURN
|
||||
0 - ok
|
||||
1 - error occured
|
||||
*/
|
||||
|
||||
int Guardian_thread::guard(const char *instance_name, uint name_len)
|
||||
{
|
||||
LIST *lst;
|
||||
Instance *instance;
|
||||
|
||||
lst= (LIST *) alloc_root(&alloc, sizeof(LIST));
|
||||
if (lst == NULL) return 1;
|
||||
instance= instance_map->find(instance_name, name_len);
|
||||
/* we store the pointers to instances from the instance_map's MEM_ROOT */
|
||||
lst->data= (void *) instance;
|
||||
|
||||
pthread_mutex_lock(&LOCK_guardian);
|
||||
guarded_instances= list_add(guarded_instances, lst);
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
TODO: perhaps it would make sense to create a pool of the LIST elements
|
||||
elements and give them upon request. Now we are loosing a bit of memory when
|
||||
guarded instance was stopped and then restarted (since we cannot free just
|
||||
a piece of the MEM_ROOT).
|
||||
*/
|
||||
|
||||
int Guardian_thread::stop_guard(const char *instance_name, uint name_len)
|
||||
{
|
||||
LIST *lst;
|
||||
Instance *instance;
|
||||
|
||||
instance= instance_map->find(instance_name, name_len);
|
||||
|
||||
lst= guarded_instances;
|
||||
if (lst == NULL) return 1;
|
||||
|
||||
pthread_mutex_lock(&LOCK_guardian);
|
||||
while (lst != NULL)
|
||||
{
|
||||
/*
|
||||
We compare only pointers, as we always use pointers from the
|
||||
instance_map's MEM_ROOT.
|
||||
*/
|
||||
if ((Instance *) lst->data == instance)
|
||||
{
|
||||
guarded_instances= list_delete(guarded_instances, lst);
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
return 0;
|
||||
}
|
||||
else lst= lst->next;
|
||||
}
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
/* if there is nothing to delete it is also fine */
|
||||
return 0;
|
||||
}
|
||||
|
80
server-tools/instance-manager/guardian.h
Normal file
80
server-tools/instance-manager/guardian.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H
|
||||
#define INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H
|
||||
/* Copyright (C) 2004 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include <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 guard(const char *instance_name, uint name_len);
|
||||
int stop_guard(const char *instance_name, uint name_len);
|
||||
|
||||
private:
|
||||
pthread_mutex_t LOCK_guardian;
|
||||
Thread_info thread_info;
|
||||
LIST *guarded_instances;
|
||||
MEM_ROOT alloc;
|
||||
enum { MEM_ROOT_BLOCK_SIZE= 512 };
|
||||
};
|
||||
|
||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */
|
152
server-tools/instance-manager/instance.cc
Normal file
152
server-tools/instance-manager/instance.cc
Normal file
|
@ -0,0 +1,152 @@
|
|||
/* Copyright (C) 2004 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma implementation
|
||||
#endif
|
||||
|
||||
#include "instance.h"
|
||||
#include "mysql_manager_error.h"
|
||||
#include <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())
|
||||
{
|
||||
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);
|
||||
}
|
60
server-tools/instance-manager/instance.h
Normal file
60
server-tools/instance-manager/instance.h
Normal file
|
@ -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 */
|
450
server-tools/instance-manager/instance_map.cc
Normal file
450
server-tools/instance-manager/instance_map.cc
Normal file
|
@ -0,0 +1,450 @@
|
|||
/* Copyright (C) 2004 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma implementation
|
||||
#endif
|
||||
|
||||
#include "instance_map.h"
|
||||
#include "buffer.h"
|
||||
#include "instance.h"
|
||||
#include <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::flush_instances()
|
||||
{
|
||||
int rc;
|
||||
|
||||
pthread_mutex_lock(&LOCK_instance_map);
|
||||
hash_free(&hash);
|
||||
hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
|
||||
get_instance_key, delete_instance, 0);
|
||||
rc= load();
|
||||
pthread_mutex_unlock(&LOCK_instance_map);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int Instance_map::show_instance_options(struct st_net *net,
|
||||
const char *instance_name)
|
||||
{
|
||||
enum { MAX_VERSION_LENGTH= 40 };
|
||||
Buffer send_buff; /* buffer for packets */
|
||||
LIST name, option;
|
||||
LIST *field_list;
|
||||
NAME_WITH_LENGTH name_field, option_field;
|
||||
uint position=0;
|
||||
|
||||
/* create list of the fileds to be passed to send_fields */
|
||||
name_field.name= (char *) "option_name";
|
||||
name_field.length= 20;
|
||||
name.data= &name_field;
|
||||
option_field.name= (char *) "value";
|
||||
option_field.length= 20;
|
||||
option.data= &option_field;
|
||||
field_list= list_add(NULL, &option);
|
||||
field_list= list_add(field_list, &name);
|
||||
|
||||
send_fields(net, field_list);
|
||||
|
||||
{
|
||||
Instance *instance;
|
||||
|
||||
if ((instance= find(instance_name, strlen(instance_name))) == NULL)
|
||||
goto err;
|
||||
store_to_string(&send_buff, (char *) "instance_name", &position);
|
||||
store_to_string(&send_buff, (char *) instance_name, &position);
|
||||
my_net_write(net, send_buff.buffer, (uint) position);
|
||||
if (instance->options.mysqld_path != NULL)
|
||||
{
|
||||
position= 0;
|
||||
store_to_string(&send_buff, (char *) "mysqld_path", &position);
|
||||
store_to_string(&send_buff,
|
||||
(char *) instance->options.mysqld_path,
|
||||
&position);
|
||||
my_net_write(net, send_buff.buffer, (uint) position);
|
||||
}
|
||||
|
||||
if (instance->options.mysqld_user != NULL)
|
||||
{
|
||||
position= 0;
|
||||
store_to_string(&send_buff, (char *) "admin_user", &position);
|
||||
store_to_string(&send_buff,
|
||||
(char *) instance->options.mysqld_user,
|
||||
&position);
|
||||
my_net_write(net, send_buff.buffer, (uint) position);
|
||||
}
|
||||
|
||||
if (instance->options.mysqld_password != NULL)
|
||||
{
|
||||
position= 0;
|
||||
store_to_string(&send_buff, (char *) "admin_password", &position);
|
||||
store_to_string(&send_buff,
|
||||
(char *) instance->options.mysqld_password,
|
||||
&position);
|
||||
my_net_write(net, send_buff.buffer, (uint) position);
|
||||
}
|
||||
|
||||
/* loop through the options stored in DYNAMIC_ARRAY */
|
||||
for (int i= 0; i < instance->options.options_array.elements; i++)
|
||||
{
|
||||
char *tmp_option, *option_value;
|
||||
get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i);
|
||||
option_value= strchr(tmp_option, '=');
|
||||
/* split the option string into two parts */
|
||||
*option_value= 0;
|
||||
position= 0;
|
||||
store_to_string(&send_buff, tmp_option + 2, &position);
|
||||
store_to_string(&send_buff, option_value + 1, &position);
|
||||
/* join name and the value into the same option again */
|
||||
*option_value= '=';
|
||||
my_net_write(net, send_buff.buffer, (uint) position);
|
||||
}
|
||||
}
|
||||
|
||||
send_eof(net);
|
||||
net_flush(net);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* return the list of running guarded instances */
|
||||
int Instance_map::init_guardian()
|
||||
{
|
||||
Instance *instance;
|
||||
uint i= 0;
|
||||
|
||||
while (i < hash.records)
|
||||
{
|
||||
instance= (Instance *) hash_element(&hash, i);
|
||||
if ((instance->options.is_guarded != NULL) && (instance->is_running()))
|
||||
if (guardian->guard(instance->options.instance_name,
|
||||
instance->options.instance_name_len))
|
||||
return 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
The method sends a list of instances in the instance map to the client.
|
||||
|
||||
SYNOPSYS
|
||||
show_instances()
|
||||
net The network connection to the client.
|
||||
|
||||
RETURN
|
||||
0 - ok
|
||||
1 - error occured
|
||||
*/
|
||||
|
||||
int Instance_map::show_instances(struct st_net *net)
|
||||
{
|
||||
Buffer send_buff; /* buffer for packets */
|
||||
LIST name, status;
|
||||
NAME_WITH_LENGTH name_field, status_field;
|
||||
LIST *field_list;
|
||||
uint position=0;
|
||||
|
||||
name_field.name= (char *) "instance_name";
|
||||
name_field.length= 20;
|
||||
name.data= &name_field;
|
||||
status_field.name= (char *) "status";
|
||||
status_field.length= 20;
|
||||
status.data= &status_field;
|
||||
field_list= list_add(NULL, &status);
|
||||
field_list= list_add(field_list, &name);
|
||||
|
||||
send_fields(net, field_list);
|
||||
|
||||
{
|
||||
Instance *instance;
|
||||
uint i= 0;
|
||||
|
||||
pthread_mutex_lock(&LOCK_instance_map);
|
||||
while (i < hash.records)
|
||||
{
|
||||
position= 0;
|
||||
instance= (Instance *) hash_element(&hash, i);
|
||||
store_to_string(&send_buff, instance->options.instance_name, &position);
|
||||
if (instance->is_running())
|
||||
store_to_string(&send_buff, (char *) "online", &position);
|
||||
else
|
||||
store_to_string(&send_buff, (char *) "offline", &position);
|
||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
||||
goto err;
|
||||
i++;
|
||||
}
|
||||
pthread_mutex_unlock(&LOCK_instance_map);
|
||||
}
|
||||
if (send_eof(net))
|
||||
goto err;
|
||||
if (net_flush(net))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
The method sends a table with a status of requested instance to the client.
|
||||
|
||||
SYNOPSYS
|
||||
show_instance_status()
|
||||
net The network connection to the client.
|
||||
instance_name The name of the instance.
|
||||
|
||||
RETURN
|
||||
0 - ok
|
||||
1 - error occured
|
||||
*/
|
||||
|
||||
int Instance_map::show_instance_status(struct st_net *net,
|
||||
const char *instance_name)
|
||||
{
|
||||
enum { MAX_VERSION_LENGTH= 40 };
|
||||
Buffer send_buff; /* buffer for packets */
|
||||
LIST name, status, version;
|
||||
LIST *field_list;
|
||||
NAME_WITH_LENGTH name_field, status_field, version_field;
|
||||
uint position=0;
|
||||
|
||||
/* create list of the fileds to be passed to send_fields */
|
||||
name_field.name= (char *) "instance_name";
|
||||
name_field.length= 20;
|
||||
name.data= &name_field;
|
||||
status_field.name= (char *) "status";
|
||||
status_field.length= 20;
|
||||
status.data= &status_field;
|
||||
version_field.name= (char *) "version";
|
||||
version_field.length= MAX_VERSION_LENGTH;
|
||||
version.data= &version_field;
|
||||
field_list= list_add(NULL, &version);
|
||||
field_list= list_add(field_list, &status);
|
||||
field_list= list_add(field_list, &name);
|
||||
|
||||
send_fields(net, field_list);
|
||||
|
||||
{
|
||||
Instance *instance;
|
||||
|
||||
store_to_string(&send_buff, (char *) instance_name, &position);
|
||||
if ((instance= find(instance_name, strlen(instance_name))) == NULL)
|
||||
goto err;
|
||||
if (instance->is_running())
|
||||
{
|
||||
store_to_string(&send_buff, (char *) "online", &position);
|
||||
store_to_string(&send_buff, mysql_get_server_info(&(instance->mysql)), &position);
|
||||
}
|
||||
else
|
||||
{
|
||||
store_to_string(&send_buff, (char *) "offline", &position);
|
||||
store_to_string(&send_buff, (char *) "unknown", &position);
|
||||
}
|
||||
|
||||
|
||||
my_net_write(net, send_buff.buffer, (uint) position);
|
||||
}
|
||||
|
||||
send_eof(net);
|
||||
net_flush(net);
|
||||
|
||||
err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Instance_map::add_instance(Instance *instance)
|
||||
{
|
||||
return my_hash_insert(&hash, (byte *) instance);
|
||||
}
|
||||
|
||||
|
||||
Instance *
|
||||
Instance_map::find(const char *name, uint name_len)
|
||||
{
|
||||
Instance *instance;
|
||||
pthread_mutex_lock(&LOCK_instance_map);
|
||||
instance= (Instance *) hash_search(&hash, (byte *) name, name_len);
|
||||
pthread_mutex_unlock(&LOCK_instance_map);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
void Instance_map::complete_initialization()
|
||||
{
|
||||
Instance *instance;
|
||||
uint i= 0;
|
||||
|
||||
while (i < hash.records)
|
||||
{
|
||||
instance= (Instance *) hash_element(&hash, i);
|
||||
instance->options.complete_initialization(mysqld_path, user, password);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Instance_map::cleanup()
|
||||
{
|
||||
Instance *instance;
|
||||
uint i= 0;
|
||||
|
||||
while (i < hash.records)
|
||||
{
|
||||
instance= (Instance *) hash_element(&hash, i);
|
||||
instance->cleanup();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Instance *
|
||||
Instance_map::find(uint instance_number)
|
||||
{
|
||||
Instance *instance;
|
||||
char name[80];
|
||||
|
||||
sprintf(name, "mysqld%i", instance_number);
|
||||
pthread_mutex_lock(&LOCK_instance_map);
|
||||
instance= (Instance *) hash_search(&hash, (byte *) name, strlen(name));
|
||||
pthread_mutex_unlock(&LOCK_instance_map);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
/* load options from config files and create appropriate instance structures */
|
||||
|
||||
int Instance_map::load()
|
||||
{
|
||||
int error;
|
||||
|
||||
error= process_default_option_files("my", process_option, (void *) this);
|
||||
|
||||
complete_initialization();
|
||||
|
||||
return error;
|
||||
}
|
76
server-tools/instance-manager/instance_map.h
Normal file
76
server-tools/instance-manager/instance_map.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H
|
||||
#define INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H
|
||||
/* Copyright (C) 2004 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include <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
|
||||
{
|
||||
public:
|
||||
/* returns a pointer to the instance or NULL, if there is no such instance */
|
||||
Instance *find(const char *name, uint name_len);
|
||||
Instance *find(uint instance_number);
|
||||
|
||||
int show_instances(struct st_net *net);
|
||||
int show_instance_status(struct st_net *net, const char *instance_name);
|
||||
int show_instance_options(struct st_net *net, const char *instance_name);
|
||||
int flush_instances();
|
||||
int init_guardian();
|
||||
int cleanup();
|
||||
|
||||
Instance_map();
|
||||
~Instance_map();
|
||||
|
||||
/* loads options from config files */
|
||||
int load();
|
||||
/* adds instance to internal hash */
|
||||
int add_instance(Instance *instance);
|
||||
/* inits instances argv's after all options have been loaded */
|
||||
void complete_initialization();
|
||||
|
||||
public:
|
||||
const char *mysqld_path;
|
||||
/* user an password to shutdown MySQL */
|
||||
const char *user;
|
||||
const char *password;
|
||||
Guardian_thread *guardian;
|
||||
|
||||
private:
|
||||
enum { START_HASH_SIZE = 16 };
|
||||
pthread_mutex_t LOCK_instance_map;
|
||||
HASH hash;
|
||||
};
|
||||
|
||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H */
|
212
server-tools/instance-manager/instance_options.cc
Normal file
212
server-tools/instance-manager/instance_options.cc
Normal file
|
@ -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 + 1));
|
||||
|
||||
if (option != NULL)
|
||||
argv[filled_default_options++]= (char *) option;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
We execute this function to initialize some options.
|
||||
Return value: 0 - ok. 1 - unable to allocate memory.
|
||||
*/
|
||||
|
||||
int Instance_options::init(const char *instance_name_arg)
|
||||
{
|
||||
instance_name_len= strlen(instance_name_arg);
|
||||
|
||||
init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
|
||||
|
||||
my_init_dynamic_array(&options_array, sizeof(char *), 0, 32);
|
||||
|
||||
if (!(instance_name= strmake_root(&alloc, (char *) instance_name_arg,
|
||||
instance_name_len)))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
Instance_options::~Instance_options()
|
||||
{
|
||||
free_root(&alloc, MYF(0));
|
||||
delete_dynamic(&options_array);
|
||||
}
|
||||
|
76
server-tools/instance-manager/instance_options.h
Normal file
76
server-tools/instance-manager/instance_options.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H
|
||||
#define INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H
|
||||
/* Copyright (C) 2004 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include <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= 3 };
|
||||
enum { MEM_ROOT_BLOCK_SIZE= 512 };
|
||||
char **argv;
|
||||
/* We need the some options, so we store them as a separate pointers */
|
||||
const char *mysqld_socket;
|
||||
const char *mysqld_datadir;
|
||||
const char *mysqld_bind_address;
|
||||
const char *mysqld_pid_file;
|
||||
const char *mysqld_port;
|
||||
uint instance_name_len;
|
||||
const char *instance_name;
|
||||
const char *mysqld_path;
|
||||
const char *mysqld_user;
|
||||
const char *mysqld_password;
|
||||
const char *is_guarded;
|
||||
DYNAMIC_ARRAY options_array;
|
||||
private:
|
||||
int add_to_argv(const char *option);
|
||||
private:
|
||||
uint filled_default_options;
|
||||
MEM_ROOT alloc;
|
||||
};
|
||||
|
||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H */
|
|
@ -1,72 +1,306 @@
|
|||
/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma implementation
|
||||
#endif
|
||||
|
||||
#include "listener.h"
|
||||
#include "thread_repository.h"
|
||||
|
||||
#include <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"
|
||||
|
||||
|
||||
/*
|
||||
Listener_thread - incapsulates listening functionality
|
||||
*/
|
||||
|
||||
class Listener_thread: public Listener_thread_args
|
||||
{
|
||||
public:
|
||||
Listener_thread(const Listener_thread_args &args);
|
||||
~Listener_thread();
|
||||
void run();
|
||||
private:
|
||||
ulong total_connection_count;
|
||||
Thread_info thread_info;
|
||||
private:
|
||||
void handle_new_mysql_connection(Vio *vio);
|
||||
};
|
||||
|
||||
|
||||
Listener_thread::Listener_thread(const Listener_thread_args &args) :
|
||||
Listener_thread_args(args.thread_registry, args.options, args.user_map,
|
||||
args.instance_map)
|
||||
,total_connection_count(0)
|
||||
,thread_info(pthread_self())
|
||||
{
|
||||
thread_registry.register_thread(&thread_info);
|
||||
}
|
||||
|
||||
|
||||
Listener_thread::~Listener_thread()
|
||||
{
|
||||
thread_registry.unregister_thread(&thread_info);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Listener_thread::run() - listen all supported sockets and spawn a thread
|
||||
to handle incoming connection.
|
||||
Using 'die' in case of syscall failure is OK now - we don't hold any
|
||||
resources and 'die' kills the signal thread automatically. To be rewritten
|
||||
one day.
|
||||
See also comments in mysqlmanager.cc to picture general Instance Manager
|
||||
architecture.
|
||||
*/
|
||||
|
||||
void Listener_thread::run()
|
||||
{
|
||||
enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size
|
||||
int flags;
|
||||
int arg= 1; /* value to be set by setsockopt */
|
||||
/* I. prepare 'listen' sockets */
|
||||
|
||||
int ip_socket= socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (ip_socket == INVALID_SOCKET)
|
||||
{
|
||||
log_error("Listener_thead::run(): socket(AF_INET) failed, %s",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
struct sockaddr_in ip_socket_address;
|
||||
memset(&ip_socket_address, 0, sizeof(ip_socket_address));
|
||||
|
||||
ulong im_bind_addr;
|
||||
if (options.bind_address != 0)
|
||||
{
|
||||
if ((im_bind_addr= (ulong) inet_addr(options.bind_address)) == INADDR_NONE)
|
||||
im_bind_addr= htonl(INADDR_ANY);
|
||||
}
|
||||
else im_bind_addr= htonl(INADDR_ANY);
|
||||
uint im_port= options.port_number;
|
||||
|
||||
ip_socket_address.sin_family= AF_INET;
|
||||
ip_socket_address.sin_addr.s_addr = im_bind_addr;
|
||||
|
||||
|
||||
ip_socket_address.sin_port= (unsigned short)
|
||||
htons((unsigned short) im_port);
|
||||
|
||||
setsockopt(ip_socket, SOL_SOCKET, SO_REUSEADDR, (char*) &arg, sizeof(arg));
|
||||
if (bind(ip_socket, (struct sockaddr *) &ip_socket_address,
|
||||
sizeof(ip_socket_address)))
|
||||
{
|
||||
log_error("Listener_thread::run(): bind(ip socket) failed, '%s'",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen(ip_socket, LISTEN_BACK_LOG_SIZE))
|
||||
{
|
||||
log_error("Listener_thread::run(): listen(ip socket) failed, %s",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
}
|
||||
flags= fcntl(ip_socket, F_GETFL, 0);
|
||||
fcntl(ip_socket, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
log_info("accepting connections on ip socket");
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (unix_socket == INVALID_SOCKET)
|
||||
{
|
||||
log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
struct sockaddr_un unix_socket_address;
|
||||
memset(&unix_socket_address, 0, sizeof(unix_socket_address));
|
||||
|
||||
unix_socket_address.sun_family= AF_UNIX;
|
||||
strmake(unix_socket_address.sun_path, options.socket_file_name,
|
||||
sizeof(unix_socket_address.sun_path));
|
||||
unlink(unix_socket_address.sun_path); // in case we have stale socket file
|
||||
|
||||
{
|
||||
/*
|
||||
POSIX specifies default permissions for a pathname created by bind
|
||||
to be 0777. We need everybody to have access to the socket.
|
||||
*/
|
||||
mode_t old_mask= umask(0);
|
||||
if (bind(unix_socket, (struct sockaddr *) &unix_socket_address,
|
||||
sizeof(unix_socket_address)))
|
||||
{
|
||||
log_error("Listener_thread::run(): bind(unix socket) failed, "
|
||||
"socket file name is '%s', error '%s'",
|
||||
unix_socket_address.sun_path, strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
}
|
||||
umask(old_mask);
|
||||
|
||||
if (listen(unix_socket, LISTEN_BACK_LOG_SIZE))
|
||||
{
|
||||
log_error("Listener_thread::run(): listen(unix socket) failed, %s",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
/* set the socket nonblocking */
|
||||
flags= fcntl(unix_socket, F_GETFL, 0);
|
||||
fcntl(unix_socket, F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
log_info("accepting connections on unix socket %s",
|
||||
unix_socket_address.sun_path);
|
||||
|
||||
/* II. Listen sockets and spawn childs */
|
||||
|
||||
{
|
||||
int n= max(unix_socket, ip_socket) + 1;
|
||||
fd_set read_fds;
|
||||
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(unix_socket, &read_fds);
|
||||
FD_SET(ip_socket, &read_fds);
|
||||
|
||||
while (thread_registry.is_shutdown() == false)
|
||||
{
|
||||
fd_set read_fds_arg= read_fds;
|
||||
int rc= select(n, &read_fds_arg, 0, 0, 0);
|
||||
if (rc == -1 && errno != EINTR)
|
||||
log_error("Listener_thread::run(): select() failed, %s",
|
||||
strerror(errno));
|
||||
else
|
||||
{
|
||||
/* Assuming that rc > 0 as we asked to wait forever */
|
||||
if (FD_ISSET(unix_socket, &read_fds_arg))
|
||||
{
|
||||
int client_fd= accept(unix_socket, 0, 0);
|
||||
/* accept may return -1 (failure or spurious wakeup) */
|
||||
if (client_fd >= 0) // connection established
|
||||
{
|
||||
if (Vio *vio= vio_new(client_fd, VIO_TYPE_SOCKET, 1))
|
||||
handle_new_mysql_connection(vio);
|
||||
else
|
||||
{
|
||||
shutdown(client_fd, SHUT_RDWR);
|
||||
close(client_fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (FD_ISSET(ip_socket, &read_fds_arg))
|
||||
{
|
||||
int client_fd= accept(ip_socket, 0, 0);
|
||||
/* accept may return -1 (failure or spurious wakeup) */
|
||||
if (client_fd >= 0) // connection established
|
||||
{
|
||||
if (Vio *vio= vio_new(client_fd, VIO_TYPE_TCPIP, 0))
|
||||
{
|
||||
handle_new_mysql_connection(vio);
|
||||
}
|
||||
else
|
||||
{
|
||||
shutdown(client_fd, SHUT_RDWR);
|
||||
close(client_fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* III. Release all resources and exit */
|
||||
|
||||
log_info("Listener_thread::run(): shutdown requested, exiting...");
|
||||
|
||||
close(unix_socket);
|
||||
unlink(unix_socket_address.sun_path);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Create new mysql connection. Created thread is responsible for deletion of
|
||||
the Mysql_connection_thread_args and Vio instances passed to it.
|
||||
SYNOPSYS
|
||||
handle_new_mysql_connection()
|
||||
*/
|
||||
|
||||
void Listener_thread::handle_new_mysql_connection(Vio *vio)
|
||||
{
|
||||
if (Mysql_connection_thread_args *mysql_thread_args=
|
||||
new Mysql_connection_thread_args(vio, thread_registry, user_map,
|
||||
++total_connection_count,
|
||||
instance_map)
|
||||
)
|
||||
{
|
||||
/*
|
||||
Initialize thread attributes to create detached thread; it seems
|
||||
easier to do it ad-hoc than have a global variable for attributes.
|
||||
*/
|
||||
pthread_t mysql_thd_id;
|
||||
pthread_attr_t mysql_thd_attr;
|
||||
pthread_attr_init(&mysql_thd_attr);
|
||||
pthread_attr_setdetachstate(&mysql_thd_attr, PTHREAD_CREATE_DETACHED);
|
||||
if (pthread_create(&mysql_thd_id, &mysql_thd_attr, mysql_connection,
|
||||
mysql_thread_args))
|
||||
{
|
||||
delete mysql_thread_args;
|
||||
vio_delete(vio);
|
||||
log_error("handle_one_mysql_connection(): pthread_create(mysql) failed");
|
||||
}
|
||||
pthread_attr_destroy(&mysql_thd_attr);
|
||||
}
|
||||
else
|
||||
vio_delete(vio);
|
||||
}
|
||||
|
||||
|
||||
C_MODE_START
|
||||
|
||||
|
||||
pthread_handler_decl(listener, arg)
|
||||
{
|
||||
Thread_info info(pthread_self());
|
||||
Thread_repository &thread_repository=
|
||||
((Listener_thread_args *) arg)->thread_repository;
|
||||
thread_repository.register_thread(&info);
|
||||
|
||||
while (true)
|
||||
{
|
||||
log_info("listener is alive");
|
||||
sleep(2);
|
||||
if (thread_repository.is_shutdown())
|
||||
break;
|
||||
}
|
||||
log_info("listener(): shutdown requested, exiting...");
|
||||
|
||||
thread_repository.unregister_thread(&info);
|
||||
Listener_thread_args *args= (Listener_thread_args *) arg;
|
||||
Listener_thread listener(*args);
|
||||
listener.run();
|
||||
/*
|
||||
args is a stack variable because listener thread lives as long as the
|
||||
manager process itself
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
C_MODE_END
|
||||
|
||||
#if 0
|
||||
while (true)
|
||||
{
|
||||
}
|
||||
/*
|
||||
Dummy manager implementation: listens on a UNIX socket and
|
||||
starts echo server in a dedicated thread for each accepted connection.
|
||||
Enough to test startup/shutdown/options/logging of the instance manager.
|
||||
*/
|
||||
|
||||
int fd= socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (!fd)
|
||||
die("socket(): failed");
|
||||
|
||||
struct sockaddr_un address;
|
||||
bzero(&address, sizeof(address));
|
||||
address.sun_family= AF_UNIX;
|
||||
strcpy(address.sun_path, socket_path);
|
||||
int opt= 1;
|
||||
|
||||
if (unlink(socket_path) ||
|
||||
bind(fd, (struct sockaddr *) &address, sizeof(address)) ||
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
|
||||
die("unlink | bind | setsockopt failed");
|
||||
|
||||
if (listen(fd, 5))
|
||||
die("listen() failed");
|
||||
|
||||
int client_fd;
|
||||
while ((client_fd= accept(fd, 0, 0)) != -1);
|
||||
{
|
||||
printf("accepted\n");
|
||||
const char *message= "\n10hel";
|
||||
send(client_fd, message, strlen(message), 0);
|
||||
|
||||
int sleep_seconds= argc > 1 && atoi(argv[1]) ? atoi(argv[1]) : 1;
|
||||
printf("sleeping %d seconds\n", sleep_seconds);
|
||||
sleep(sleep_seconds);
|
||||
close(client_fd);
|
||||
}
|
||||
printf("accept(): failed\n");
|
||||
close(fd);
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
Thread_registry &thread_registry;
|
||||
const Options &options;
|
||||
const User_map &user_map;
|
||||
Instance_map &instance_map;
|
||||
|
||||
Listener_thread_args(Thread_repository &thread_repository_arg,
|
||||
const char *socket_file_name_arg) :
|
||||
thread_repository(thread_repository_arg),
|
||||
socket_file_name(socket_file_name_arg) {}
|
||||
Listener_thread_args(Thread_registry &thread_registry_arg,
|
||||
const Options &options_arg,
|
||||
const User_map &user_map_arg,
|
||||
Instance_map &instance_map_arg) :
|
||||
thread_registry(thread_registry_arg)
|
||||
,options(options_arg)
|
||||
,user_map(user_map_arg)
|
||||
,instance_map(instance_map_arg)
|
||||
{}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -17,19 +17,68 @@
|
|||
#include "manager.h"
|
||||
|
||||
#include <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"
|
||||
|
||||
|
||||
void manager(const char *socket_file_name)
|
||||
/*
|
||||
manager - entry point to the main instance manager process: start
|
||||
listener thread, write pid file and enter into signal handling.
|
||||
See also comments in mysqlmanager.cc to picture general Instance Manager
|
||||
architecture.
|
||||
*/
|
||||
|
||||
void manager(const Options &options)
|
||||
{
|
||||
Thread_repository thread_repository;
|
||||
Listener_thread_args listener_args(thread_repository, socket_file_name);
|
||||
Thread_registry thread_registry;
|
||||
/*
|
||||
All objects created in the manager() function live as long as
|
||||
thread_registry lives, and thread_registry is alive until there are
|
||||
working threads.
|
||||
*/
|
||||
|
||||
User_map user_map;
|
||||
Instance_map instance_map;
|
||||
Guardian_thread guardian_thread(thread_registry,
|
||||
&instance_map,
|
||||
options.monitoring_interval);
|
||||
|
||||
instance_map.mysqld_path= options.default_mysqld_path;
|
||||
instance_map.user= options.default_admin_user;
|
||||
instance_map.password= options.default_admin_password;
|
||||
instance_map.guardian= &guardian_thread;
|
||||
instance_map.load();
|
||||
|
||||
Listener_thread_args listener_args(thread_registry, options, user_map,
|
||||
instance_map);
|
||||
|
||||
|
||||
if (user_map.load(options.password_file_name))
|
||||
return;
|
||||
|
||||
/* write pid file */
|
||||
if (FILE *pid_file= my_fopen(options.pid_file_name,
|
||||
O_WRONLY | O_CREAT | O_BINARY, MYF(0)))
|
||||
{
|
||||
fprintf(pid_file, "%d\n", (int) getpid());
|
||||
my_fclose(pid_file, MYF(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error("can't create pid file %s: errno=%d, %s",
|
||||
options.pid_file_name, errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
/* block signals */
|
||||
sigset_t mask;
|
||||
|
@ -37,26 +86,98 @@ void manager(const char *socket_file_name)
|
|||
sigaddset(&mask, SIGINT);
|
||||
sigaddset(&mask, SIGTERM);
|
||||
sigaddset(&mask, SIGHUP);
|
||||
/*
|
||||
We want this signal to be blocked in all theads but the signal
|
||||
one. It is needed for the thr_alarm subsystem to work.
|
||||
*/
|
||||
sigaddset(&mask,THR_SERVER_ALARM);
|
||||
|
||||
/* all new threads will inherite this signal mask */
|
||||
pthread_sigmask(SIG_BLOCK, &mask, NULL);
|
||||
|
||||
/* create the listener */
|
||||
{
|
||||
/* create the listener */
|
||||
pthread_t listener_thd_id;
|
||||
pthread_attr_t listener_thd_attr;
|
||||
int rc;
|
||||
|
||||
pthread_attr_init(&listener_thd_attr);
|
||||
pthread_attr_setdetachstate(&listener_thd_attr, PTHREAD_CREATE_DETACHED);
|
||||
if (pthread_create(&listener_thd_id, &listener_thd_attr, listener,
|
||||
&listener_args))
|
||||
die("manager(): pthread_create(listener) failed");
|
||||
rc= pthread_create(&listener_thd_id, &listener_thd_attr, listener,
|
||||
&listener_args);
|
||||
pthread_attr_destroy(&listener_thd_attr);
|
||||
if (rc)
|
||||
{
|
||||
log_error("manager(): pthread_create(listener) failed");
|
||||
goto err;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* create guardian thread */
|
||||
{
|
||||
pthread_t guardian_thd_id;
|
||||
pthread_attr_t guardian_thd_attr;
|
||||
int rc;
|
||||
|
||||
pthread_attr_init(&guardian_thd_attr);
|
||||
pthread_attr_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED);
|
||||
rc= pthread_create(&guardian_thd_id, &guardian_thd_attr, guardian,
|
||||
&guardian_thread);
|
||||
pthread_attr_destroy(&guardian_thd_attr);
|
||||
if (rc)
|
||||
{
|
||||
log_error("manager(): pthread_create(guardian) failed");
|
||||
goto err;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
To work nicely with LinuxThreads, the signal thread is the first thread
|
||||
in the process.
|
||||
*/
|
||||
int signo;
|
||||
sigwait(&mask, &signo);
|
||||
thread_repository.deliver_shutdown();
|
||||
bool shutdown_complete;
|
||||
|
||||
shutdown_complete= FALSE;
|
||||
/*
|
||||
In our case the signal thread also implements functions of alarm thread.
|
||||
Here we init alarm thread functionality. We suppose that we won't have
|
||||
more then 10 alarms at the same time.
|
||||
*/
|
||||
init_thr_alarm(10);
|
||||
/*
|
||||
Now we can init the list of guarded instances. We have to do it after
|
||||
alarm structures initialization as we have to use net_* functions while
|
||||
making the list. And they in their turn need alarms for timeout suppport.
|
||||
*/
|
||||
instance_map.init_guardian();
|
||||
|
||||
while (!shutdown_complete)
|
||||
{
|
||||
sigwait(&mask, &signo);
|
||||
switch (signo)
|
||||
{
|
||||
case THR_SERVER_ALARM:
|
||||
process_alarm(signo);
|
||||
break;
|
||||
default:
|
||||
thread_registry.deliver_shutdown();
|
||||
shutdown_complete= TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
/* delete the pid file */
|
||||
my_delete(options.pid_file_name, MYF(0));
|
||||
|
||||
/* close permanent connections to the running instances */
|
||||
instance_map.cleanup();
|
||||
|
||||
/* free alarm structures */
|
||||
end_thr_alarm(1);
|
||||
/* don't pthread_exit to kill all threads who did not shut down in time */
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
73
server-tools/instance-manager/messages.cc
Normal file
73
server-tools/instance-manager/messages.cc
Normal file
|
@ -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);
|
||||
}
|
26
server-tools/instance-manager/messages.h
Normal file
26
server-tools/instance-manager/messages.h
Normal file
|
@ -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
|
388
server-tools/instance-manager/mysql_connection.cc
Normal file
388
server-tools/instance-manager/mysql_connection.cc
Normal file
|
@ -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
|
||||
*/
|
60
server-tools/instance-manager/mysql_connection.h
Normal file
60
server-tools/instance-manager/mysql_connection.h
Normal file
|
@ -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
|
27
server-tools/instance-manager/mysql_manager_error.h
Normal file
27
server-tools/instance-manager/mysql_manager_error.h
Normal file
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
200
server-tools/instance-manager/parse.cc
Normal file
200
server-tools/instance-manager/parse.cc
Normal file
|
@ -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;
|
||||
}
|
||||
|
23
server-tools/instance-manager/parse.h
Normal file
23
server-tools/instance-manager/parse.h
Normal file
|
@ -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 */
|
34
server-tools/instance-manager/priv.cc
Normal file
34
server-tools/instance-manager/priv.cc
Normal file
|
@ -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
|
||||
|
60
server-tools/instance-manager/priv.h
Normal file
60
server-tools/instance-manager/priv.h
Normal file
|
@ -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
|
171
server-tools/instance-manager/protocol.cc
Normal file
171
server-tools/instance-manager/protocol.cc
Normal file
|
@ -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)
|
||||
{
|
||||
char* currpos;
|
||||
uint string_len;
|
||||
|
||||
string_len= strlen(string);
|
||||
buf->check_and_add(*position, 2);
|
||||
currpos= net_store_length(buf->buffer + *position, string_len);
|
||||
buf->put_to_buffer(currpos, string, string_len);
|
||||
*position= *position + string_len + (currpos - buf->buffer - *position);
|
||||
}
|
||||
|
||||
|
||||
int send_eof(struct st_net *net)
|
||||
{
|
||||
char buff[1 + /* eof packet code */
|
||||
2 + /* warning count */
|
||||
2]; /* server status */
|
||||
|
||||
buff[0]=254;
|
||||
int2store(buff+1, 0);
|
||||
int2store(buff+3, 0);
|
||||
return my_net_write(net, buff, sizeof buff);
|
||||
}
|
||||
|
||||
int send_fields(struct st_net *net, LIST *fields)
|
||||
{
|
||||
LIST *tmp= fields;
|
||||
Buffer send_buff;
|
||||
char small_buff[4];
|
||||
uint position= 0;
|
||||
NAME_WITH_LENGTH *field;
|
||||
|
||||
/* send the number of fileds */
|
||||
net_store_length(small_buff, (uint) list_length(fields));
|
||||
my_net_write(net, small_buff, (uint) 1);
|
||||
|
||||
while (tmp)
|
||||
{
|
||||
position= 0;
|
||||
field= (NAME_WITH_LENGTH *) tmp->data;
|
||||
|
||||
store_to_string(&send_buff, (char *) "", &position); /* catalog name */
|
||||
store_to_string(&send_buff, (char *) "", &position); /* db name */
|
||||
store_to_string(&send_buff, (char *) "", &position); /* table name */
|
||||
store_to_string(&send_buff, (char *) "", &position); /* table name alias */
|
||||
store_to_string(&send_buff, field->name, &position); /* column name */
|
||||
store_to_string(&send_buff, field->name, &position); /* column name alias */
|
||||
send_buff.check_and_add(position, 12);
|
||||
send_buff.buffer[position++]= 12;
|
||||
int2store(send_buff.buffer + position, 1); /* charsetnr */
|
||||
int4store(send_buff.buffer + position + 2, field->length); /* field length */
|
||||
send_buff.buffer[position+6]= FIELD_TYPE_STRING; /* type */
|
||||
int2store(send_buff.buffer + position + 7, 0); /* flags */
|
||||
send_buff.buffer[position + 9]= (char) 0; /* decimals */
|
||||
send_buff.buffer[position + 10]= 0;
|
||||
send_buff.buffer[position + 11]= 0;
|
||||
position+= 12;
|
||||
if (my_net_write(net, send_buff.buffer, (uint) position+1))
|
||||
goto err;
|
||||
tmp= rest(tmp);
|
||||
}
|
||||
|
||||
if ( my_net_write(net, eof_buff, 1))
|
||||
goto err;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return 1;
|
||||
}
|
43
server-tools/instance-manager/protocol.h
Normal file
43
server-tools/instance-manager/protocol.h
Normal file
|
@ -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 */
|
206
server-tools/instance-manager/thread_registry.cc
Normal file
206
server-tools/instance-manager/thread_registry.cc
Normal file
|
@ -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);
|
||||
}
|
116
server-tools/instance-manager/thread_registry.h
Normal file
116
server-tools/instance-manager/thread_registry.h
Normal file
|
@ -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 */
|
172
server-tools/instance-manager/user_map.cc
Normal file
172
server-tools/instance-manager/user_map.cc
Normal file
|
@ -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;
|
||||
}
|
45
server-tools/instance-manager/user_map.h
Normal file
45
server-tools/instance-manager/user_map.h
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue