Commit ee08b749 authored by petr@mysql.com's avatar petr@mysql.com

IM mostly fixed according to Brian's directions. Will need to do some...

IM mostly fixed according to Brian's directions. Will need to do some additional option handling and cleanups
parent d4e9719a
......@@ -75,6 +75,7 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
instance_options.h instance_options.cc \
buffer.h buffer.cc parse.cc parse.h \
guardian.cc guardian.h \
parse_output.cc parse_output.h \
mysql_manager_error.h client_func.c
mysqlmanager_LDADD= liboptions.a \
......
......@@ -3,7 +3,7 @@
#include <mysql.h>
/*
Currently we cannot use libmysqlclient directly becouse of the linking
Currently we cannot use libmysqlclient directly because of the linking
issues. Here we provide needed libmysqlclient functions.
TODO: to think how to use libmysqlclient code instead of copy&paste.
The other possible solution is to use simple_command directly.
......
......@@ -175,7 +175,7 @@ int Show_instance_status::do_command(struct st_net *net,
if (instance->is_running())
{
store_to_string(&send_buff, (char *) "online", &position);
store_to_string(&send_buff, mysql_get_server_info(&(instance->mysql)), &position);
store_to_string(&send_buff, "unknown", &position);
}
else
{
......@@ -283,28 +283,6 @@ int Show_instance_options::do_command(struct st_net *net,
goto err;
}
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);
if (my_net_write(net, send_buff.buffer, (uint) position))
goto err;
}
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);
if (my_net_write(net, send_buff.buffer, (uint) position))
goto err;
}
/* loop through the options stored in DYNAMIC_ARRAY */
for (uint i= 0; i < instance->options.options_array.elements; i++)
{
......
......@@ -45,6 +45,7 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg,
thread_info(pthread_self())
{
pthread_mutex_init(&LOCK_guardian, 0);
pthread_cond_init(&COND_guardian, 0);
thread_registry.register_thread(&thread_info);
init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
guarded_instances= NULL;
......@@ -60,6 +61,7 @@ Guardian_thread::~Guardian_thread()
thread_registry.unregister_thread(&thread_info);
pthread_mutex_unlock(&LOCK_guardian);
pthread_mutex_destroy(&LOCK_guardian);
pthread_cond_destroy(&COND_guardian);
}
......@@ -79,27 +81,32 @@ void Guardian_thread::run()
{
Instance *instance;
LIST *loop;
struct timespec timeout;
my_thread_init();
pthread_mutex_lock(&LOCK_guardian);
while (!thread_registry.is_shutdown())
{
pthread_mutex_lock(&LOCK_guardian);
loop= guarded_instances;
while (loop != NULL)
{
instance= (Instance *) loop->data;
/* instance-> start already checks whether instance is running */
/* instance-> start already checks whether the instance is running */
if (instance->start() != ER_INSTANCE_ALREADY_STARTED)
log_info("guardian attempted to restart instance %s",
instance->options.instance_name);
loop= loop->next;
}
move_to_list(&starting_instances, &guarded_instances);
pthread_mutex_unlock(&LOCK_guardian);
sleep(monitoring_interval);
timeout.tv_sec= time(NULL) + monitoring_interval;
timeout.tv_nsec= 0;
pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout);
}
pthread_mutex_unlock(&LOCK_guardian);
my_thread_end();
}
......
......@@ -70,6 +70,9 @@ class Guardian_thread: public Guardian_thread_args
int guard(Instance *instance);
int stop_guard(Instance *instance);
public:
pthread_cond_t COND_guardian;
private:
int add_instance_to_list(Instance *instance, LIST **list);
void move_to_list(LIST **from, LIST **to);
......
......@@ -21,6 +21,7 @@
#include "instance.h"
#include "mysql_manager_error.h"
#include "log.h"
#include "instance_map.h"
#include <my_sys.h>
#include <signal.h>
#include <m_string.h>
......@@ -41,24 +42,18 @@
int Instance::start()
{
pid_t pid;
/* echk for the pidfile and remove it */
if (!is_running())
{
log_info("trying to start instance %s", options.instance_name);
stop();
log_info("starting instance %s", options.instance_name);
switch (pid= fork()) {
case 0:
if (fork()) /* zombie protection */
exit(0); /* parent goes bye-bye */
else
{
execv(options.mysqld_path, options.argv);
exit(1);
}
execv(options.mysqld_path, options.argv);
exit(1);
case -1:
return ER_CANNOT_START_INSTANCE;
default:
waitpid(pid, NULL, 0);
return 0;
}
}
......@@ -67,15 +62,9 @@ int Instance::start()
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;
}
......@@ -88,8 +77,13 @@ Instance::~Instance()
bool Instance::is_running()
{
MYSQL mysql;
uint port= 0;
const char *socket= NULL;
const char *password= "321rarepassword213";
const char *username= "645rareusername945";
const char *access_denied_message= "Access denied for user";
bool return_val;
if (options.mysqld_port)
port= atoi(strchr(options.mysqld_port, '=') + 1);
......@@ -98,30 +92,40 @@ bool Instance::is_running()
socket= strchr(options.mysqld_socket, '=') + 1;
pthread_mutex_lock(&LOCK_instance);
if (!is_connected)
mysql_init(&mysql);
/* try to connect to a server with the fake username/password pair */
if (mysql_real_connect(&mysql, LOCAL_HOST, username,
password,
NullS, port,
socket, 0))
{
mysql_init(&mysql);
if (mysql_real_connect(&mysql, LOCAL_HOST, options.mysqld_user,
options.mysqld_password,
NullS, port,
socket, 0))
{
mysql.reconnect= 1;
is_connected= TRUE;
pthread_mutex_unlock(&LOCK_instance);
return TRUE;
}
/*
Very strange. We have successfully connected to the server using
bullshit as username/password. Write a warning to the logfile.
*/
log_info("The Instance Manager was able to log into you server \
with faked compiled-in password while checking server status. \
Looks like something is wrong.");
mysql_close(&mysql);
pthread_mutex_unlock(&LOCK_instance);
return FALSE;
return_val= TRUE; /* server is alive */
}
else if (!mysql_ping(&mysql))
else
{
pthread_mutex_unlock(&LOCK_instance);
return TRUE;
if (!strncmp(access_denied_message, mysql_error(&mysql),
sizeof(access_denied_message)-1))
{
return_val= TRUE;
}
else
return_val= FALSE;
}
mysql_close(&mysql);
pthread_mutex_unlock(&LOCK_instance);
return FALSE;
return return_val;
}
......@@ -139,13 +143,58 @@ bool Instance::is_running()
int Instance::stop()
{
if (is_running())
pid_t pid;
struct timespec timeout;
time_t waitchild= 35; /* */
if ((pid= options.get_pid()) != 0) /* get pid from pidfile */
{
if (mysql_shutdown(&mysql, SHUTDOWN_DEFAULT))
goto err;
/*
If we cannot kill mysqld, then it has propably crashed.
Let us try to remove staled pidfile and return succes as mysqld
is stopped
*/
if (kill(pid, SIGTERM))
{
if (options.unlink_pidfile())
log_error("cannot remove pidfile for instance %i, this might be \
since IM lacks permmissions or hasn't found the pidifle",
options.instance_name);
log_error("The instance %s has probably crashed or IM lacks permissions \
to kill it. in either case something seems to be wrong. \
Check your setup", options.instance_name);
return 0;
}
/* sleep on condition to wait for SIGCHLD */
timeout.tv_sec= time(NULL) + waitchild;
timeout.tv_nsec= 0;
if (pthread_mutex_lock(&instance_map->pid_cond.LOCK_pid))
goto err; /* perhaps this should be procecced differently */
while (options.get_pid() != 0)
{
int status;
status= pthread_cond_timedwait(&instance_map->pid_cond.COND_pid,
&instance_map->pid_cond.LOCK_pid,
&timeout);
if (status == ETIMEDOUT)
break;
}
pthread_mutex_unlock(&instance_map->pid_cond.LOCK_pid);
if (!kill(pid, SIGKILL))
{
log_error("The instance %s has been stopped forsibly. Normally \
it should not happed. Probably the instance has been \
hanging. You should also check your IM setup",
options.instance_name);
}
mysql_close(&mysql);
is_connected= FALSE;
return 0;
}
......@@ -166,3 +215,10 @@ int Instance::init(const char *name_arg)
return options.init(name_arg);
}
int Instance::complete_initialization(Instance_map *instance_map_arg)
{
instance_map= instance_map_arg;
return 0;
}
......@@ -25,14 +25,14 @@
#pragma interface
#endif
class Instance_map;
class Instance
{
public:
Instance(): is_connected(FALSE)
{}
~Instance();
int init(const char *name);
int complete_initialization(Instance_map *instance_map_arg);
/* check if the instance is running and set up mysql connection if yes */
bool is_running();
......@@ -44,7 +44,7 @@ class Instance
Instance_options options;
/* connection to the instance */
MYSQL mysql;
pid_t pid;
private:
/*
......@@ -53,8 +53,7 @@ class Instance
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;
Instance_map *instance_map;
};
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */
......@@ -123,6 +123,9 @@ Instance_map::Instance_map(const char *default_mysqld_path_arg,
int Instance_map::init()
{
pthread_mutex_init(&pid_cond.LOCK_pid, 0);
pthread_cond_init(&pid_cond.COND_pid, 0);
if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
get_instance_key, delete_instance, 0))
return 1;
......@@ -135,6 +138,8 @@ Instance_map::~Instance_map()
hash_free(&hash);
pthread_mutex_unlock(&LOCK_instance_map);
pthread_mutex_destroy(&LOCK_instance_map);
pthread_mutex_destroy(&pid_cond.LOCK_pid);
pthread_cond_destroy(&pid_cond.COND_pid);
}
......@@ -189,6 +194,7 @@ void Instance_map::complete_initialization()
while (i < hash.records)
{
instance= (Instance *) hash_element(&hash, i);
instance->complete_initialization(this);
instance->options.complete_initialization(mysqld_path, user, password);
i++;
}
......@@ -218,7 +224,8 @@ Instance_map::find(uint instance_number)
Instance *instance;
char name[80];
sprintf(name, "mysqld%i", instance_number);
snprintf(name, sizeof(name) - 1, "mysqld%i", instance_number);
name[sizeof(name) - 1]= 0; /* safety */
pthread_mutex_lock(&LOCK_instance_map);
instance= (Instance *) hash_search(&hash, (byte *) name, strlen(name));
pthread_mutex_unlock(&LOCK_instance_map);
......
......@@ -27,6 +27,12 @@
#include "protocol.h"
#include "guardian.h"
typedef struct st_instance_cond
{
pthread_mutex_t LOCK_pid;
pthread_cond_t COND_pid;
} CHILD_COND;
class Instance;
extern int load_all_groups(char ***groups, const char *filename);
extern void free_groups(char **groups);
......@@ -83,6 +89,8 @@ class Instance_map
const char *user;
const char *password;
Guardian_thread *guardian;
/* structure used for syncronization reasons in the stop command */
CHILD_COND pid_cond;
private:
enum { START_HASH_SIZE = 16 };
......
......@@ -19,20 +19,105 @@
#endif
#include "instance_options.h"
#include "parse_output.h"
#include "buffer.h"
#include <my_sys.h>
#include <mysql.h>
#include <signal.h>
#include <m_string.h>
/* option_name should be prefixed with "--" */
int Instance_options::get_default_option(char *result, const char *option_name,
size_t result_len)
{
int position= 0;
char verbose_option[]= " --no-defaults --verbose --help";
Buffer cmd;
cmd.append(position, mysqld_path, strlen(mysqld_path));
position+= strlen(mysqld_path);
cmd.append(position, verbose_option, sizeof(verbose_option) - 1);
position+= sizeof(verbose_option) - 1;
cmd.append(position, "\0", 1);
/* get the value from "mysqld --help --verbose" */
if (parse_output_and_get_value(cmd.buffer, option_name + 2,
result, result_len))
return 1;
return 0;
}
void Instance_options::get_pid_filename(char *result)
{
const char *pid_file= mysqld_pid_file;
char datadir[MAX_PATH_LEN];
if (mysqld_datadir == NULL)
{
get_default_option(datadir, "--datadir", MAX_PATH_LEN);
}
else
strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1,
"/", NullS);
/* well, we should never get it */
if (mysqld_pid_file != NULL)
pid_file= strchr(pid_file, '=') + 1;
else
DBUG_ASSERT(0);
/* get the full path to the pidfile */
my_load_path(result, pid_file, datadir);
}
int Instance_options::unlink_pidfile()
{
char pid_file_path[MAX_PATH_LEN];
/*
This works as we know that pid_file_path is of
MAX_PATH_LEN == FN_REFLEN length
*/
get_pid_filename((char *)&pid_file_path);
return unlink(pid_file_path);
}
pid_t Instance_options::get_pid()
{
char pid_file_path[MAX_PATH_LEN];
/*
This works as we know that pid_file_path is of
MAX_PATH_LEN == FN_REFLEN length
*/
get_pid_filename((char *)&pid_file_path);
/* get the pid */
if (FILE *pid_file_stream= my_fopen(pid_file_path,
O_RDONLY | O_BINARY, MYF(0)))
{
pid_t pid;
fscanf(pid_file_stream, "%i", &pid);
my_fclose(pid_file_stream, MYF(0));
return pid;
}
else
return 0;
}
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;
const char *tmp;
if (mysqld_path == NULL)
{
......@@ -40,22 +125,34 @@ int Instance_options::complete_initialization(const char *default_path,
goto err;
}
/* this option must be first in the argv */
if (add_to_argv(mysqld_path))
if (!(tmp= strdup_root(&alloc, "--no-defaults")))
goto err;
/* the following options are not for argv */
if (mysqld_user == NULL)
if (mysqld_pid_file == NULL)
{
if (!(mysqld_user= strdup_root(&alloc, default_user)))
goto err;
char pidfilename[MAX_PATH_LEN];
char hostname[MAX_PATH_LEN];
if (!gethostname(hostname, sizeof(hostname) - 1))
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname, "-",
instance_name, ".pid", NullS);
else
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name,
".pid", NullS);
add_option(pidfilename);
}
if (mysqld_password == NULL)
{
if (!(mysqld_password= strdup_root(&alloc, default_password)))
goto err;
}
/* 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;
/* the path must be first in the argv */
if (add_to_argv(mysqld_path))
goto err;
if (add_to_argv(tmp))
goto err;
memcpy((gptr) (argv + filled_default_options), options_array.buffer,
options_array.elements*sizeof(char*));
......@@ -102,8 +199,6 @@ int Instance_options::add_option(const char* option)
{"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD},
{"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD},
{"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE},
{"--admin-user=", 13, &mysqld_user, SAVE_VALUE},
{"--admin-password=", 17, &mysqld_password, SAVE_VALUE},
{"--guarded", 9, &is_guarded, SAVE_WHOLE},
{NULL, 0, NULL, 0}
};
......
......@@ -38,8 +38,8 @@ 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)
mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), is_guarded(0),
filled_default_options(0)
{}
~Instance_options();
/* fills in argv */
......@@ -49,9 +49,17 @@ class Instance_options
int add_option(const char* option);
int init(const char *instance_name_arg);
pid_t get_pid();
void get_pid_filename(char *result);
int unlink_pidfile();
public:
enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 1 };
/*
We need this value to be greater or equal then FN_REFLEN found in
my_global.h to use my_load_path()
*/
enum { MAX_PATH_LEN= 512 };
enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 2 };
enum { MEM_ROOT_BLOCK_SIZE= 512 };
char **argv;
/* We need the some options, so we store them as a separate pointers */
......@@ -63,12 +71,12 @@ class Instance_options
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);
int get_default_option(char *result, const char *option_name,
size_t result_len);
private:
uint filled_default_options;
MEM_ROOT alloc;
......
......@@ -16,12 +16,6 @@
#include "manager.h"
#include <my_global.h>
#include <my_sys.h>
#include <m_string.h>
#include <signal.h>
#include <thr_alarm.h>
#include "thread_registry.h"
#include "listener.h"
#include "instance_map.h"
......@@ -30,6 +24,14 @@
#include "log.h"
#include "guardian.h"
#include <my_global.h>
#include <my_sys.h>
#include <m_string.h>
#include <signal.h>
#include <thr_alarm.h>
#include <sys/wait.h>
static int create_pid_file(const char *pid_file_name)
{
if (FILE *pid_file= my_fopen(pid_file_name,
......@@ -90,6 +92,7 @@ void manager(const Options &options)
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGPIPE);
sigaddset(&mask, SIGHUP);
/*
......@@ -170,6 +173,13 @@ void manager(const Options &options)
case THR_SERVER_ALARM:
process_alarm(signo);
break;
case SIGCHLD:
wait(NULL);
/* wake threads waiting for an instance to shutdown */
pthread_cond_broadcast(&instance_map.pid_cond.COND_pid);
/* wake guardian */
pthread_cond_broadcast(&guardian_thread.COND_guardian);
break;
default:
thread_registry.deliver_shutdown();
shutdown_complete= TRUE;
......
/* 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 <stdio.h>
#include <my_global.h>
#include <my_sys.h>
#include <string.h>
/* buf should be of appropriate size. Otherwise the word will be truncated */
static int get_word(FILE *file, char *buf, size_t size)
{
int currchar;
currchar= getc(file);
/* skip space */
while (my_isspace(default_charset_info, (char) currchar) &&
currchar != EOF && size > 1)
{
currchar= getc(file);
}
while (!my_isspace(default_charset_info, (char) currchar) &&
currchar != EOF && size > 1)
{
*buf++= (char) currchar;
currchar= getc(file);
size--;
}
*buf= '\0';
return 0;
}
int parse_output_and_get_value(const char *command, const char *word,
char *result, size_t result_len)
{
FILE *output;
int wordlen;
wordlen= strlen(word);
output= popen(command, "r");
/*
We want fully buffered stream. We also want system to
allocate appropriate buffer.
*/
setvbuf(output, NULL, _IOFBF, 0);
get_word(output, result, result_len);
while (strncmp(word, result, wordlen) && *result != '\0')
{
get_word(output, result, result_len);
}
/*
If we have found the word, return the next one. This is usually
an option value.
*/
if (*result != '\0')
get_word(output, result, result_len);
if (pclose(output))
return 1;
return 0;
}
/* 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 */
int parse_output_and_get_value(const char *command, const char *word,
char *result, size_t result_len);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment