From ee08b749ddd075db0c3cffe8ce43958478c4e311 Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Tue, 25 Jan 2005 13:54:56 +0300 Subject: [PATCH 01/14] IM mostly fixed according to Brian's directions. Will need to do some additional option handling and cleanups --- server-tools/instance-manager/Makefile.am | 1 + server-tools/instance-manager/client_func.c | 2 +- server-tools/instance-manager/commands.cc | 24 +-- server-tools/instance-manager/guardian.cc | 15 +- server-tools/instance-manager/guardian.h | 3 + server-tools/instance-manager/instance.cc | 138 ++++++++++++------ server-tools/instance-manager/instance.h | 11 +- server-tools/instance-manager/instance_map.cc | 9 +- server-tools/instance-manager/instance_map.h | 8 + .../instance-manager/instance_options.cc | 135 ++++++++++++++--- .../instance-manager/instance_options.h | 18 ++- server-tools/instance-manager/manager.cc | 22 ++- server-tools/instance-manager/parse_output.cc | 82 +++++++++++ server-tools/instance-manager/parse_output.h | 19 +++ 14 files changed, 380 insertions(+), 107 deletions(-) create mode 100644 server-tools/instance-manager/parse_output.cc create mode 100644 server-tools/instance-manager/parse_output.h diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index c2bf501eca7..fcc4a60c7a8 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -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 \ diff --git a/server-tools/instance-manager/client_func.c b/server-tools/instance-manager/client_func.c index a7ff1d27a8f..92c106e5172 100644 --- a/server-tools/instance-manager/client_func.c +++ b/server-tools/instance-manager/client_func.c @@ -3,7 +3,7 @@ #include /* - 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. diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index dacace1af1e..4922aba4a06 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -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++) { diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index 7375453673b..f68ef6575a0 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -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(); } diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index 0ae2161f1dc..bcc249ed7d3 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -70,6 +70,9 @@ public: 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); diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 58bc5b85dd8..edb9ea61ad3 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -21,6 +21,7 @@ #include "instance.h" #include "mysql_manager_error.h" #include "log.h" +#include "instance_map.h" #include #include #include @@ -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()) - { - if (mysql_shutdown(&mysql, SHUTDOWN_DEFAULT)) - goto err; + pid_t pid; + struct timespec timeout; + time_t waitchild= 35; /* */ + + if ((pid= options.get_pid()) != 0) /* get pid from pidfile */ + { + /* + 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; +} diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 6733985116a..78b7768c121 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -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 @@ public: Instance_options options; /* connection to the instance */ - MYSQL mysql; + pid_t pid; private: /* @@ -53,8 +53,7 @@ private: 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 */ diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 9399d6e2563..2573729841b 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -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); diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 522785ce9b9..1f6072f2582 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -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 @@ public: 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 }; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index fab69865c85..c29f136955e 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -19,20 +19,105 @@ #endif #include "instance_options.h" +#include "parse_output.h" +#include "buffer.h" #include #include #include #include + +/* 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 (!(tmp= strdup_root(&alloc, "--no-defaults"))) + goto err; + + if (mysqld_pid_file == NULL) + { + 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); + } + + /* 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; - /* 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; - } + 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} }; diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 5bc46497d2a..26448c0c2b1 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -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 @@ public: 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 @@ public: 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; diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 07d4f1ed33e..a8c6406fb83 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -16,12 +16,6 @@ #include "manager.h" -#include -#include -#include -#include -#include - #include "thread_registry.h" #include "listener.h" #include "instance_map.h" @@ -30,6 +24,14 @@ #include "log.h" #include "guardian.h" +#include +#include +#include +#include +#include +#include + + 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; diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc new file mode 100644 index 00000000000..2bc51922e69 --- /dev/null +++ b/server-tools/instance-manager/parse_output.cc @@ -0,0 +1,82 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include +#include +#include +#include + +/* 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; +} diff --git a/server-tools/instance-manager/parse_output.h b/server-tools/instance-manager/parse_output.h new file mode 100644 index 00000000000..20503a74629 --- /dev/null +++ b/server-tools/instance-manager/parse_output.h @@ -0,0 +1,19 @@ +/* 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); + From 84d5b3a3320482f8498f08c6f2910c5929f688ec Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Mon, 31 Jan 2005 23:54:08 +0300 Subject: [PATCH 02/14] more fixes for IM to substitude mysqld_safe in startup scripts --- server-tools/instance-manager/commands.cc | 8 +- server-tools/instance-manager/guardian.cc | 107 ++++++++-- server-tools/instance-manager/guardian.h | 19 ++ server-tools/instance-manager/instance.cc | 22 ++- server-tools/instance-manager/instance.h | 4 +- server-tools/instance-manager/instance_map.cc | 11 +- server-tools/instance-manager/instance_map.h | 7 +- .../instance-manager/instance_options.cc | 7 +- .../instance-manager/instance_options.h | 9 +- server-tools/instance-manager/manager.cc | 17 +- server-tools/instance-manager/messages.cc | 4 +- server-tools/instance-manager/options.cc | 16 -- .../instance-manager/thread_repository.cc | 185 ------------------ .../instance-manager/thread_repository.h | 113 ----------- 14 files changed, 160 insertions(+), 369 deletions(-) delete mode 100644 server-tools/instance-manager/thread_repository.cc delete mode 100644 server-tools/instance-manager/thread_repository.h diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 4922aba4a06..70774115429 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -274,10 +274,10 @@ int Show_instance_options::do_command(struct st_net *net, goto err; } - if (instance->options.is_guarded != NULL) + if (instance->options.nonguarded == NULL) { position= 0; - store_to_string(&send_buff, (char *) "guarded", &position); + store_to_string(&send_buff, (char *) "nonguarded", &position); store_to_string(&send_buff, "", &position); if (my_net_write(net, send_buff.buffer, (uint) position)) goto err; @@ -350,7 +350,7 @@ int Start_instance::execute(struct st_net *net, ulong connection_id) if (err_code= instance->start()) return err_code; - if (instance->options.is_guarded != NULL) + if (instance->options.nonguarded == NULL) instance_map->guardian->guard(instance); net_send_ok(net, connection_id); @@ -381,7 +381,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id) } else { - if (instance->options.is_guarded != NULL) + if (instance->options.nonguarded == NULL) instance_map->guardian-> stop_guard(instance); if ((err_code= instance->stop())) diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index f68ef6575a0..e8f9068dbb9 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -46,6 +46,8 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, { pthread_mutex_init(&LOCK_guardian, 0); pthread_cond_init(&COND_guardian, 0); + shutdown_guardian= FALSE; + is_stopped= FALSE; thread_registry.register_thread(&thread_info); init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); guarded_instances= NULL; @@ -65,6 +67,22 @@ Guardian_thread::~Guardian_thread() } +void Guardian_thread::shutdown() +{ + pthread_mutex_lock(&LOCK_guardian); + shutdown_guardian= TRUE; + pthread_mutex_unlock(&LOCK_guardian); +} + + +void Guardian_thread::request_stop_instances() +{ + pthread_mutex_lock(&LOCK_guardian); + request_stop= TRUE; + pthread_mutex_unlock(&LOCK_guardian); +} + + /* Run guardian thread @@ -80,6 +98,7 @@ Guardian_thread::~Guardian_thread() void Guardian_thread::run() { Instance *instance; + int restart_retry= 100; LIST *loop; struct timespec timeout; @@ -87,26 +106,68 @@ void Guardian_thread::run() pthread_mutex_lock(&LOCK_guardian); - while (!thread_registry.is_shutdown()) + while (!shutdown_guardian) { + int status= 0; loop= guarded_instances; while (loop != NULL) { - instance= (Instance *) loop->data; - /* 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); + instance= ((GUARD_NODE *) loop->data)->instance; + if (!instance->is_running()) + { + int state= 0; /* state of guardian */ + + if ((((GUARD_NODE *) loop->data)->crash_moment == 0)) + state= 1; /* an instance just crashed */ + else + if (time(NULL) - ((GUARD_NODE *) loop->data)->crash_moment <= 2) + /* try to restart an instance immediately */ + state= 2; + else + state= 3; /* try to restart it */ + + if (state == 1) + ((GUARD_NODE *) loop->data)->crash_moment= time(NULL); + + if ((state == 1) || (state == 2)) + { + instance->start(); + ((GUARD_NODE *) loop->data)->restart_counter++; + log_info("guardian: starting instance %s", + instance->options.instance_name); + } + else + { + if ((status == ETIMEDOUT) && + (((GUARD_NODE *) loop->data)->restart_counter < restart_retry)) + { + instance->start(); + ((GUARD_NODE *) loop->data)->restart_counter++; + log_info("guardian: starting instance %s", + instance->options.instance_name); + } + } + } + else /* clear status fields */ + { + ((GUARD_NODE *) loop->data)->restart_counter= 0; + ((GUARD_NODE *) loop->data)->crash_moment= 0; + } loop= loop->next; } move_to_list(&starting_instances, &guarded_instances); timeout.tv_sec= time(NULL) + monitoring_interval; timeout.tv_nsec= 0; - pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout); + status= pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout); } pthread_mutex_unlock(&LOCK_guardian); + if (request_stop) + stop_instances(); + is_stopped= TRUE; + /* now, when the Guardian is stopped we can stop the IM */ + thread_registry.request_shutdown(); my_thread_end(); } @@ -119,7 +180,7 @@ int Guardian_thread::start() instance_map->lock(); while ((instance= iterator.next())) { - if ((instance->options.is_guarded != NULL) && (instance->is_running())) + if ((instance->options.nonguarded == NULL)) if (guard(instance)) return 1; } @@ -170,12 +231,18 @@ void Guardian_thread::move_to_list(LIST **from, LIST **to) int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list) { LIST *node; + GUARD_NODE *content; node= (LIST *) alloc_root(&alloc, sizeof(LIST)); - if (node == NULL) + content= (GUARD_NODE *) alloc_root(&alloc, sizeof(GUARD_NODE)); + + if ((node == NULL) || (content == NULL)) return 1; /* we store the pointers to instances from the instance_map's MEM_ROOT */ - node->data= (void *) instance; + content->instance= instance; + content->restart_counter= 0; + content->crash_moment= 0; + node->data= (void *) content; pthread_mutex_lock(&LOCK_guardian); *list= list_add(*list, node); @@ -205,7 +272,7 @@ int Guardian_thread::stop_guard(Instance *instance) We compare only pointers, as we always use pointers from the instance_map's MEM_ROOT. */ - if ((Instance *) node->data == instance) + if (((GUARD_NODE *) node->data)->instance == instance) { guarded_instances= list_delete(guarded_instances, node); pthread_mutex_unlock(&LOCK_guardian); @@ -219,3 +286,21 @@ int Guardian_thread::stop_guard(Instance *instance) return 0; } +int Guardian_thread::stop_instances() +{ + Instance *instance; + Instance_map::Iterator iterator(instance_map); + + while ((instance= iterator.next())) + { + if ((instance->options.nonguarded == NULL)) + { + if (stop_guard(instance)) + return 1; + /* let us try to stop the server */ + instance->stop(); + } + } + + return 0; +} diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index bcc249ed7d3..d96208247ae 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -24,6 +24,7 @@ #pragma interface #endif +class Instance; class Instance_map; #include "thread_registry.h" @@ -35,6 +36,13 @@ pthread_handler_decl(guardian, arg); C_MODE_END +typedef struct st_guard_node +{ + Instance *instance; + uint restart_counter; + time_t crash_moment; +} GUARD_NODE; + struct Guardian_thread_args { @@ -67,13 +75,17 @@ public: void run(); int init(); int start(); + void shutdown(); + void request_stop_instances(); int guard(Instance *instance); int stop_guard(Instance *instance); + bool is_stopped; public: pthread_cond_t COND_guardian; private: + int stop_instances(); int add_instance_to_list(Instance *instance, LIST **list); void move_to_list(LIST **from, LIST **to); @@ -84,6 +96,13 @@ private: LIST *starting_instances; MEM_ROOT alloc; enum { MEM_ROOT_BLOCK_SIZE= 512 }; + /* this variable is set to TRUE when we want to stop Guardian thread */ + bool shutdown_guardian; + /* + This var is usually set together with shutdown_guardian. this way we + request guardian to shut down all instances before termination + */ + bool request_stop; }; #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */ diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index edb9ea61ad3..6c32aa1d815 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -42,10 +42,16 @@ int Instance::start() { - /* echk for the pidfile and remove it */ + pid_t pid; + if (!is_running()) { - stop(); + if ((pid= options.get_pid()) != 0) /* check the pidfile */ + if (options.unlink_pidfile()) /* remove stalled 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_info("starting instance %s", options.instance_name); switch (pid= fork()) { case 0: @@ -145,14 +151,17 @@ int Instance::stop() { pid_t pid; struct timespec timeout; - time_t waitchild= 35; /* */ + int waitchild= DEFAULT_SHUTDOWN_DELAY; + + if (options.shutdown_delay != NULL) + waitchild= atoi(options.shutdown_delay); if ((pid= options.get_pid()) != 0) /* get pid from pidfile */ { /* If we cannot kill mysqld, then it has propably crashed. Let us try to remove staled pidfile and return succes as mysqld - is stopped + is probably stopped */ if (kill(pid, SIGTERM)) { @@ -161,9 +170,6 @@ int Instance::stop() 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; } @@ -174,7 +180,7 @@ int Instance::stop() if (pthread_mutex_lock(&instance_map->pid_cond.LOCK_pid)) goto err; /* perhaps this should be procecced differently */ - while (options.get_pid() != 0) + while (options.get_pid() != 0) /* while server isn't stopped */ { int status; diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 78b7768c121..29137c749c7 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -41,11 +41,9 @@ public: int cleanup(); public: + enum { DEFAULT_SHUTDOWN_DELAY= 35 }; Instance_options options; - /* connection to the instance */ - pid_t pid; - private: /* Mutex protecting the instance. Currently we use it to avoid the diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 2573729841b..26181ec56e0 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -82,7 +82,8 @@ static int process_option(void * ctx, const char *group, const char *option) map = (Instance_map*) ctx; if (strncmp(group, prefix, sizeof prefix) == 0 && - (my_isdigit(default_charset_info, group[sizeof prefix]))) + ((my_isdigit(default_charset_info, group[sizeof prefix])) + || group[sizeof(prefix)] == '\0')) { if ((instance= map->find(group, strlen(group))) == NULL) { @@ -109,13 +110,9 @@ err_new_instance: C_MODE_END -Instance_map::Instance_map(const char *default_mysqld_path_arg, - const char *default_admin_user_arg, - const char *default_admin_password_arg) +Instance_map::Instance_map(const char *default_mysqld_path_arg) { mysqld_path= default_mysqld_path_arg; - user= default_admin_user_arg; - password= default_admin_password_arg; pthread_mutex_init(&LOCK_instance_map, 0); } @@ -195,7 +192,7 @@ void Instance_map::complete_initialization() { instance= (Instance *) hash_element(&hash, i); instance->complete_initialization(this); - instance->options.complete_initialization(mysqld_path, user, password); + instance->options.complete_initialization(mysqld_path); i++; } } diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 1f6072f2582..05bc4d94908 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -71,9 +71,7 @@ public: int unlock(); int init(); - Instance_map(const char *default_mysqld_path_arg, - const char *default_admin_user_arg, - const char *default_admin_password_arg); + Instance_map(const char *default_mysqld_path_arg); ~Instance_map(); /* loads options from config files */ @@ -85,9 +83,6 @@ public: public: const char *mysqld_path; - /* user an password to shutdown MySQL */ - const char *user; - const char *password; Guardian_thread *guardian; /* structure used for syncronization reasons in the stop command */ CHILD_COND pid_cond; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index c29f136955e..0e29dca2422 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -113,9 +113,7 @@ pid_t Instance_options::get_pid() } -int Instance_options::complete_initialization(const char *default_path, - const char *default_user, - const char *default_password) +int Instance_options::complete_initialization(const char *default_path) { const char *tmp; @@ -199,7 +197,8 @@ 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}, - {"--guarded", 9, &is_guarded, SAVE_WHOLE}, + {"--nonguarded", 9, &nonguarded, SAVE_WHOLE}, + {"--shutdown_delay", 9, &shutdown_delay, SAVE_VALUE}, {NULL, 0, NULL, 0} }; struct selected_options_st *selected_options; diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 26448c0c2b1..eae22f840aa 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -38,14 +38,12 @@ 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), is_guarded(0), + mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), nonguarded(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 complete_initialization(const char *default_path); int add_option(const char* option); int init(const char *instance_name_arg); @@ -71,7 +69,8 @@ public: uint instance_name_len; const char *instance_name; const char *mysqld_path; - const char *is_guarded; + const char *nonguarded; + const char *shutdown_delay; DYNAMIC_ARRAY options_array; private: int add_to_argv(const char *option); diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index a8c6406fb83..6d9ee569d04 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -67,9 +67,7 @@ void manager(const Options &options) */ User_map user_map; - Instance_map instance_map(options.default_mysqld_path, - options.default_admin_user, - options.default_admin_password); + Instance_map instance_map(options.default_mysqld_path); Guardian_thread guardian_thread(thread_registry, &instance_map, options.monitoring_interval); @@ -181,8 +179,17 @@ void manager(const Options &options) pthread_cond_broadcast(&guardian_thread.COND_guardian); break; default: - thread_registry.deliver_shutdown(); - shutdown_complete= TRUE; + if (!guardian_thread.is_stopped) + { + guardian_thread.request_stop_instances(); + guardian_thread.shutdown(); + pthread_cond_broadcast(&guardian_thread.COND_guardian); + } + else + { + thread_registry.deliver_shutdown(); + shutdown_complete= TRUE; + } break; } } diff --git a/server-tools/instance-manager/messages.cc b/server-tools/instance-manager/messages.cc index cc07352f58a..e3c2423a47e 100644 --- a/server-tools/instance-manager/messages.cc +++ b/server-tools/instance-manager/messages.cc @@ -45,8 +45,8 @@ static const char *mysqld_error_message(unsigned sql_errno) 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"; + return "Cannot stop instance. Perhaps the instance is not started, or was started" + "manually, so IM cannot find the pidfile."; case ER_INSTANCE_ALREADY_STARTED: return "The instance is already started"; case ER_CANNOT_START_INSTANCE: diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 05493e10ad8..828a5d7b4d0 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -35,8 +35,6 @@ 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; @@ -55,8 +53,6 @@ enum options { OPT_RUN_AS_SERVICE, OPT_USER, OPT_PASSWORD, - OPT_DEFAULT_ADMIN_USER, - OPT_DEFAULT_ADMIN_PASSWORD, OPT_MONITORING_INTERVAL, OPT_PORT, OPT_BIND_ADDRESS @@ -98,18 +94,6 @@ static struct my_option my_long_options[] = (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, diff --git a/server-tools/instance-manager/thread_repository.cc b/server-tools/instance-manager/thread_repository.cc deleted file mode 100644 index d0b302d29fb..00000000000 --- a/server-tools/instance-manager/thread_repository.cc +++ /dev/null @@ -1,185 +0,0 @@ -/* 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 */ - -#ifdef __GNUC__ -#pragma implementation -#endif - -#include "thread_repository.h" -#include -#include -#include "log.h" - - -/* Kick-off signal handler */ - -enum { THREAD_KICK_OFF_SIGNAL= SIGUSR2 }; - -static void handle_signal(int __attribute__((unused)) sig_no) -{ -} - - -/* - TODO: think about moving signal information (now it's shutdown_in_progress) - to Thread_info. It will reduce contention and allow signal deliverence to - a particular thread, not to the whole worker crew -*/ - -Thread_repository::Thread_repository() : - shutdown_in_progress(false) -{ - pthread_mutex_init(&LOCK_thread_repository, 0); - pthread_cond_init(&COND_thread_repository_is_empty, 0); - - /* head is used by-value to simplify nodes inserting */ - head.next= head.prev= &head; -} - - -Thread_repository::~Thread_repository() -{ - /* Check that no one uses the repository. */ - pthread_mutex_lock(&LOCK_thread_repository); - - /* All threads must unregister */ - DBUG_ASSERT(head.next == &head); - - pthread_mutex_unlock(&LOCK_thread_repository); - pthread_cond_destroy(&COND_thread_repository_is_empty); - pthread_mutex_destroy(&LOCK_thread_repository); -} - - -/* - - 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_repository::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_repository); - info->next= &head; - info->prev= head.prev; - head.prev->next= info; - head.prev= info; - pthread_mutex_unlock(&LOCK_thread_repository); -} - - -/* - 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_repository::unregister_thread(Thread_info *info) -{ - pthread_mutex_lock(&LOCK_thread_repository); - info->prev->next= info->next; - info->next->prev= info->prev; - if (head.next == &head) - pthread_cond_signal(&COND_thread_repository_is_empty); - pthread_mutex_unlock(&LOCK_thread_repository); -} - - -/* - Check whether shutdown is in progress, and if yes, return immidiately. - 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_repository::cond_wait(Thread_info *info, pthread_cond_t *cond, - pthread_mutex_t *mutex, bool *is_shutdown) -{ - pthread_mutex_lock(&LOCK_thread_repository); - *is_shutdown= shutdown_in_progress; - if (*is_shutdown) - { - pthread_mutex_unlock(&LOCK_thread_repository); - return 0; - } - info->current_cond= cond; - pthread_mutex_unlock(&LOCK_thread_repository); - /* sic: race condition here, cond can be signaled in deliver_shutdown */ - int rc= pthread_cond_wait(cond, mutex); - pthread_mutex_lock(&LOCK_thread_repository); - info->current_cond= 0; - *is_shutdown= shutdown_in_progress; - pthread_mutex_unlock(&LOCK_thread_repository); - return rc; -} - - -/* - Deliver shutdown message to the workers crew. - As it's impossible to avoid all race conditions, we signal latecomers - again. -*/ - -void Thread_repository::deliver_shutdown() -{ - struct timespec shutdown_time; - set_timespec(shutdown_time, 1); - Thread_info *info; - - pthread_mutex_lock(&LOCK_thread_repository); - shutdown_in_progress= true; - for (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); - } - while (pthread_cond_timedwait(&COND_thread_repository_is_empty, - &LOCK_thread_repository, - &shutdown_time) != ETIMEDOUT && - head.next != &head) - ; - /* - If previous signals did not reach some threads, they must be sleeping - in pthread_cond_wait or 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 (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_repository); -} - diff --git a/server-tools/instance-manager/thread_repository.h b/server-tools/instance-manager/thread_repository.h deleted file mode 100644 index 7bd21d66e3d..00000000000 --- a/server-tools/instance-manager/thread_repository.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH -#define INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* - A multi-threaded application shall nicely work with signals. - - This means it shall, first of all, shut down nicely on ``quit'' signals: - stop all running threads, cleanup and exit. - - Note, that a thread can't be shut down nicely if it doesn't want to be. - That's why to perform clean shutdown, all threads consituting a process - must observe certain rules. Here we use the rules, described in Butenhof - book 'Programming with POSIX threads', namely: - - all user signals are handled in 'signal thread' in synchronous manner - (by means of sigwait). To guarantee that the signal thread is the only who - can receive user signals, all threads block them, and signal thread is - the only who calls sigwait() with an apporpriate sigmask. - To propogate a signal to the workers the signal thread sets - a variable, corresponding to the signal. Additionally the signal thread - sends each worker an internal signal (by means of pthread_kill) to kick it - out from possible blocking syscall, and possibly pthread_cond_signal if - some thread is blocked in pthread_cond_[timed]wait. - - a worker handles only internal 'kick' signal (the handler does nothing). - In case when a syscall returns 'EINTR' the worker checks all - signal-related variables and behaves accordingly. - Also these variables shall be checked from time to time in long - CPU-bounded operations, and before/after pthread_cond_wait. (It's supposed - that a worker thread either waits in a syscall/conditional variable, or - computes something.) - - to guarantee signal deliverence, there should be some kind of feedback, - e. g. all workers shall account in the signal thread Thread Repository and - unregister from it on exit. - - Configuration reload (on SIGHUP) and thread timeouts/alarms can be handled - in manner, similar to ``quit'' signals. -*/ - -#ifdef __GNUC__ -#pragma interface -#endif - -#include -#include - - -/* - Thread_info - repository entry for each worker thread - All entries comprise double-linked list like: - 0 -- entry -- entry -- entry - 0 - Double-linked list is used to unregister threads easy. -*/ - -class Thread_info -{ - pthread_cond_t *current_cond; - Thread_info *prev, *next; - pthread_t thread_id; - Thread_info() {} - friend class Thread_repository; -public: - Thread_info(pthread_t thread_id_arg) : thread_id(thread_id_arg) {} -}; - - -/* - Thread_repository - contains handles for each worker thread to deliver - signal information to workers. -*/ - -class Thread_repository -{ -public: - Thread_repository(); - ~Thread_repository(); - - void register_thread(Thread_info *info); - void unregister_thread(Thread_info *info); - void deliver_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_repository; - pthread_cond_t COND_thread_repository_is_empty; -}; - - -inline bool Thread_repository::is_shutdown() -{ - pthread_mutex_lock(&LOCK_thread_repository); - bool res= shutdown_in_progress; - pthread_mutex_unlock(&LOCK_thread_repository); - return res; -} - -#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH From 63acf2a8be35545c4dc426bc7f94764b3ab780de Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Thu, 3 Feb 2005 20:48:58 +0300 Subject: [PATCH 03/14] Various fixes (cleanups, valgrind, makefiles, ...) --- server-tools/instance-manager/Makefile.am | 4 +- server-tools/instance-manager/guardian.cc | 8 ++-- server-tools/instance-manager/guardian.h | 1 - server-tools/instance-manager/instance.cc | 7 +-- server-tools/instance-manager/instance.h | 1 - server-tools/instance-manager/instance_map.cc | 17 ------- server-tools/instance-manager/instance_map.h | 1 - .../instance-manager/instance_options.cc | 10 +++++ .../instance-manager/instance_options.h | 1 + server-tools/instance-manager/listener.cc | 45 ++++++++++++------- server-tools/instance-manager/log.cc | 4 +- server-tools/instance-manager/log.h | 4 +- server-tools/instance-manager/manager.cc | 18 +++++--- server-tools/instance-manager/mysqlmanager.cc | 2 + server-tools/instance-manager/options.cc | 13 +++++- server-tools/instance-manager/options.h | 3 ++ .../instance-manager/thread_registry.cc | 1 + server-tools/instance-manager/user_map.cc | 5 ++- 18 files changed, 85 insertions(+), 60 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index fcc4a60c7a8..ff236b10ad5 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -33,7 +33,7 @@ liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_MYSQLD_PATH="$(bindir)/mysqld$(EXEEXT)" \ -DDEFAULT_USER="root" \ -DDEFAULT_PASSWORD="" \ - -DDEFAULT_MONITORING_INTERVAL="5" \ + -DDEFAULT_MONITORING_INTERVAL="20" \ -DDEFAULT_PORT="2273" \ -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ @@ -59,7 +59,7 @@ client_settings.h: Makefile rm -f $(srcdir)/client_settings.h @LN_CP_F@ $(top_srcdir)/sql/client_settings.h $(srcdir)/client_settings.h -bin_PROGRAMS= mysqlmanager +libexec_PROGRAMS= mysqlmanager mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ manager.h manager.cc log.h log.cc \ diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index e8f9068dbb9..bc05fda1a8f 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -48,7 +48,6 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, pthread_cond_init(&COND_guardian, 0); shutdown_guardian= FALSE; is_stopped= FALSE; - thread_registry.register_thread(&thread_info); init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); guarded_instances= NULL; starting_instances= NULL; @@ -60,7 +59,6 @@ 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); pthread_cond_destroy(&COND_guardian); @@ -102,6 +100,8 @@ void Guardian_thread::run() LIST *loop; struct timespec timeout; + thread_registry.register_thread(&thread_info); + my_thread_init(); pthread_mutex_lock(&LOCK_guardian); @@ -110,6 +110,7 @@ void Guardian_thread::run() { int status= 0; loop= guarded_instances; + while (loop != NULL) { instance= ((GUARD_NODE *) loop->data)->instance; @@ -167,6 +168,7 @@ void Guardian_thread::run() stop_instances(); is_stopped= TRUE; /* now, when the Guardian is stopped we can stop the IM */ + thread_registry.unregister_thread(&thread_info); thread_registry.request_shutdown(); my_thread_end(); } @@ -181,7 +183,7 @@ int Guardian_thread::start() while ((instance= iterator.next())) { if ((instance->options.nonguarded == NULL)) - if (guard(instance)) + if (add_instance_to_list(instance, &guarded_instances)) return 1; } instance_map->unlock(); diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index d96208247ae..bf96436a636 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -73,7 +73,6 @@ public: uint monitoring_interval_arg); ~Guardian_thread(); void run(); - int init(); int start(); void shutdown(); void request_stop_instances(); diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 6c32aa1d815..3ba943c9cd1 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -56,6 +56,7 @@ int Instance::start() switch (pid= fork()) { case 0: execv(options.mysqld_path, options.argv); + /* exec never returns */ exit(1); case -1: return ER_CANNOT_START_INSTANCE; @@ -69,12 +70,6 @@ int Instance::start() } -int Instance::cleanup() -{ - return 0; -} - - Instance::~Instance() { pthread_mutex_destroy(&LOCK_instance); diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 29137c749c7..95852542fb4 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -38,7 +38,6 @@ public: bool is_running(); int start(); int stop(); - int cleanup(); public: enum { DEFAULT_SHUTDOWN_DELAY= 35 }; diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 26181ec56e0..ba373087db4 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -198,23 +198,6 @@ void Instance_map::complete_initialization() } -int Instance_map::cleanup() -{ - Instance *instance; - uint i= 0; - - while (i < hash.records) - { - instance= (Instance *) hash_element(&hash, i); - if (instance->cleanup()) - return 1; - i++; - } - - return 0; -} - - Instance * Instance_map::find(uint instance_number) { diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 05bc4d94908..f2121f7141d 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -66,7 +66,6 @@ public: Instance *find(uint instance_number); int flush_instances(); - int cleanup(); int lock(); int unlock(); int init(); diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 0e29dca2422..31a1eafdd00 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -241,6 +241,16 @@ int Instance_options::add_to_argv(const char* option) return 0; } +void Instance_options::print_argv() +{ + int i; + printf("printing out an instance %s argv:\n", instance_name); + for (i=0; argv[i] != NULL; i++) + { + printf("argv: %s\n", argv[i]); + } +} + /* We execute this function to initialize some options. diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index eae22f840aa..1ed11fc9afa 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -50,6 +50,7 @@ public: pid_t get_pid(); void get_pid_filename(char *result); int unlink_pidfile(); + void print_argv(); public: /* diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 15f57e7e595..8266c0dac4c 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -57,13 +57,11 @@ Listener_thread::Listener_thread(const Listener_thread_args &args) : ,total_connection_count(0) ,thread_info(pthread_self()) { - thread_registry.register_thread(&thread_info); } Listener_thread::~Listener_thread() { - thread_registry.unregister_thread(&thread_info); } @@ -82,6 +80,11 @@ void Listener_thread::run() enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size int flags; int arg= 1; /* value to be set by setsockopt */ + int unix_socket; + uint im_port; + + thread_registry.register_thread(&thread_info); + /* I. prepare 'listen' sockets */ int ip_socket= socket(AF_INET, SOCK_STREAM, 0); @@ -89,8 +92,7 @@ void Listener_thread::run() { log_error("Listener_thead::run(): socket(AF_INET) failed, %s", strerror(errno)); - thread_registry.request_shutdown(); - return; + goto err; } struct sockaddr_in ip_socket_address; @@ -104,7 +106,7 @@ void Listener_thread::run() } else im_bind_addr= htonl(INADDR_ANY); - uint im_port= options.port_number; + im_port= options.port_number; ip_socket_address.sin_family= AF_INET; ip_socket_address.sin_addr.s_addr = im_bind_addr; @@ -119,16 +121,14 @@ void Listener_thread::run() { log_error("Listener_thread::run(): bind(ip socket) failed, '%s'", strerror(errno)); - thread_registry.request_shutdown(); - return; + goto err; } 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; + goto err; } /* set the socket nonblocking */ flags= fcntl(ip_socket, F_GETFL, 0); @@ -140,13 +140,12 @@ void Listener_thread::run() log_info("accepting connections on ip socket"); /*--------------------------------------------------------------*/ - int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0); + 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; + goto err; } struct sockaddr_un unix_socket_address; @@ -169,8 +168,7 @@ void Listener_thread::run() 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; + goto err; } umask(old_mask); @@ -178,8 +176,7 @@ void Listener_thread::run() { log_error("Listener_thread::run(): listen(unix socket) failed, %s", strerror(errno)); - thread_registry.request_shutdown(); - return; + goto err; } /* set the socket nonblocking */ @@ -205,7 +202,15 @@ void Listener_thread::run() while (thread_registry.is_shutdown() == false) { fd_set read_fds_arg= read_fds; + + /* + When using valgrind 2.0 this syscall doesn't get kicked off by a + signal during shutdown. This results in failing assert + (Thread_registry::~Thread_registry). Valgrind 2.2 works fine. + */ 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)); @@ -256,6 +261,14 @@ void Listener_thread::run() close(unix_socket); close(ip_socket); unlink(unix_socket_address.sun_path); + + thread_registry.unregister_thread(&thread_info); + return; + +err: + thread_registry.unregister_thread(&thread_info); + thread_registry.request_shutdown(); + return; } diff --git a/server-tools/instance-manager/log.cc b/server-tools/instance-manager/log.cc index f89f5e425b8..dbcea3f9c3d 100644 --- a/server-tools/instance-manager/log.cc +++ b/server-tools/instance-manager/log.cc @@ -32,8 +32,8 @@ SYNOPSYS log() */ - -static inline void log(FILE *file, const char *format, va_list args) + +static inline void log(FILE *file, const char *format, va_list args) { /* log() should be thread-safe; it implies that we either call fprintf() diff --git a/server-tools/instance-manager/log.h b/server-tools/instance-manager/log.h index a1441d5bd71..825d7515513 100644 --- a/server-tools/instance-manager/log.h +++ b/server-tools/instance-manager/log.h @@ -17,14 +17,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* - Logging facilities. + Logging facilities. 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 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 - no constructors/desctructors to make logging available all the time diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 6d9ee569d04..b48c020786f 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -127,6 +127,12 @@ void manager(const Options &options) pthread_attr_t guardian_thd_attr; int rc; + /* + NOTE: Guardian should be shutdowned first. Only then all other threads + need to be stopped. This should be done, as guardian is responsible for + shutting down the instances, and this is a long operation. + */ + 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, @@ -160,6 +166,11 @@ void manager(const Options &options) making the list. And they in their turn need alarms for timeout suppport. */ guardian_thread.start(); + /* + After the list of guarded instances have been initialized, + Guardian should start them. + */ + pthread_cond_signal(&guardian_thread.COND_guardian); signal(SIGPIPE, SIG_IGN); @@ -176,14 +187,14 @@ void manager(const Options &options) /* 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); + pthread_cond_signal(&guardian_thread.COND_guardian); break; default: if (!guardian_thread.is_stopped) { guardian_thread.request_stop_instances(); guardian_thread.shutdown(); - pthread_cond_broadcast(&guardian_thread.COND_guardian); + pthread_cond_signal(&guardian_thread.COND_guardian); } else { @@ -198,9 +209,6 @@ err: /* delete the pid file */ my_delete(options.pid_file_name, MYF(0)); - /* close permanent connections to the running instances */ - instance_map.cleanup(); - /* free alarm structures */ end_thr_alarm(1); /* don't pthread_exit to kill all threads who did not shut down in time */ diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index 9ad8ce2c858..bdd7c4ebe78 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -77,6 +77,8 @@ int main(int argc, char *argv[]) angel(options); } manager(options); + options.cleanup(); + my_end(0); return 0; } diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 828a5d7b4d0..db117de03e5 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -38,6 +38,8 @@ const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH); const char *Options::bind_address= 0; /* No default value */ uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL; uint Options::port_number= DEFAULT_PORT; +/* just to declare */ +char **Options::saved_argv; /* List of options, accepted by the instance manager. @@ -81,7 +83,7 @@ static struct my_option my_long_options[] = { "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 }, + 0, GET_UINT, REQUIRED_ARG, DEFAULT_PORT, 0, 0, 0, 0, 0 }, { "password-file", OPT_PASSWORD_FILE, "Look for Instane Manager users" " and passwords here.", @@ -98,7 +100,8 @@ static struct my_option my_long_options[] = " in seconds.", (gptr *) &Options::monitoring_interval, (gptr *) &Options::monitoring_interval, - 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + 0, GET_UINT, REQUIRED_ARG, DEFAULT_MONITORING_INTERVAL, + 0, 0, 0, 0, 0 }, { "run-as-service", OPT_RUN_AS_SERVICE, "Daemonize and start angel process.", (gptr *) &Options::run_as_service, @@ -171,5 +174,11 @@ void Options::load(int argc, char **argv) if (int rc= handle_options(&argc, &argv, my_long_options, get_one_option)) exit(rc); + Options::saved_argv= argv; } +void Options::cleanup() +{ + /* free_defaults returns nothing */ + free_defaults(Options::saved_argv); +} diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index fc2b44c8b53..8b673bd5fb1 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -40,7 +40,10 @@ struct Options static uint port_number; static const char *bind_address; + static char **saved_argv; + static void load(int argc, char **argv); + void cleanup(); }; #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc index d0bf51f3d61..bf01aa6d7b2 100644 --- a/server-tools/instance-manager/thread_registry.cc +++ b/server-tools/instance-manager/thread_registry.cc @@ -197,6 +197,7 @@ void Thread_registry::deliver_shutdown() if (info->current_cond) pthread_cond_signal(info->current_cond); } + pthread_mutex_unlock(&LOCK_thread_registry); } diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index 8cbceceac7c..f145b611a8d 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -128,9 +128,10 @@ int User_map::load(const char *password_file_name) 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, + /* Probably the password file wasn't specified. Try to leave without it */ + log_info("can't open password file %s: errno=%d, %s", password_file_name, errno, strerror(errno)); - return 1; + return 0; } while (fgets(line, sizeof(line), file)) From 321c53d9d8e425bbef2b11020ab3e3642c367d86 Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Sat, 5 Feb 2005 10:15:34 +0300 Subject: [PATCH 04/14] Fixed setup scripts to use mysqlmanager instead of mysqld_safe --- server-tools/instance-manager/Makefile.am | 4 +- .../instance-manager/instance_options.cc | 3 + server-tools/instance-manager/user_map.cc | 2 +- support-files/mysql.server.sh | 76 +++++++++---------- support-files/mysql.spec.sh | 1 + 5 files changed, 42 insertions(+), 44 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index ff236b10ad5..4b776fecc83 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -30,9 +30,7 @@ liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_LOG_FILE_NAME="$(localstatedir)/mysqlmanager.log" \ -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_MYSQLD_PATH="$(libexecdir)/mysqld$(EXEEXT)" \ -DDEFAULT_MONITORING_INTERVAL="20" \ -DDEFAULT_PORT="2273" \ -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 31a1eafdd00..e7e17691330 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -225,6 +225,9 @@ int Instance_options::add_option(const char* option) } } + /* if we haven't returned earlier we should just save the option */ + insert_dynamic(&options_array,(gptr) &tmp); + return 0; err: diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index f145b611a8d..21d66912813 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -129,7 +129,7 @@ int User_map::load(const char *password_file_name) if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0) { /* Probably the password file wasn't specified. Try to leave without it */ - log_info("can't open password file %s: errno=%d, %s", password_file_name, + log_info("[WARNING] can't open password file %s: errno=%d, %s", password_file_name, errno, strerror(errno)); return 0; } diff --git a/support-files/mysql.server.sh b/support-files/mysql.server.sh index 849f913bf6c..08e10c0192b 100644 --- a/support-files/mysql.server.sh +++ b/support-files/mysql.server.sh @@ -1,44 +1,29 @@ #!/bin/sh -# Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB +# Copyright (C) 2005 MySQL AB # This file is public domain and comes with NO WARRANTY of any kind -# MySQL daemon start/stop script. +# MySQL server management daemon start/stop script. # Usually this is put in /etc/init.d (at least on machines SYSV R4 based -# systems) and linked to /etc/rc3.d/S99mysql and /etc/rc0.d/K01mysql. +# systems) and linked to /etc/rc3.d/S99mysqlmanager and +# /etc/rc0.d/K01mysqlmanager # When this is done the mysql server will be started when the machine is # started and shut down when the systems goes down. -# Comments to support chkconfig on RedHat Linux -# chkconfig: 2345 90 20 -# description: A very fast and reliable SQL database engine. +# description: MySQL database server Instance Manager # Comments to support LSB init script conventions ### BEGIN INIT INFO -# Provides: mysql +# Provides: mysqlmanager # Required-Start: $local_fs $network $remote_fs # Required-Stop: $local_fs $network $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 -# Short-Description: start and stop MySQL -# Description: MySQL is a very fast and reliable SQL database engine. +# Short-Description: start and stop MySQL Instance Manager +# Description: MySQL Instance Manager is used to start/stop/status/monitor +# MySQL server instances ### END INIT INFO -# If you install MySQL on some other places than @prefix@, then you -# have to do one of the following things for this script to work: -# -# - Run this script from within the MySQL installation directory -# - Create a /etc/my.cnf file with the following information: -# [mysqld] -# basedir= -# - Add the above to any other configuration file (for example ~/.my.ini) -# and copy my_print_defaults to /usr/bin -# - Add the path to the mysql-installation-directory to the basedir variable -# below. -# -# If you want to affect other MySQL variables, you should make your changes -# in the /etc/my.cnf, ~/.my.cnf or other MySQL configuration files. - basedir= # The following variables are only set for letting mysql.server find things. @@ -50,8 +35,10 @@ if test -z "$basedir" then basedir=@prefix@ bindir=@bindir@ + sbindir=@sbindir@ else bindir="$basedir/bin" + sbindir="$basedir/sbin" fi PATH=/sbin:/usr/sbin:/bin:/usr/bin:$basedir/bin @@ -65,11 +52,18 @@ case `echo "testing\c"`,`echo -n testing` in *) echo_n= echo_c='\c' ;; esac -parse_arguments() { +parse_server_arguments() { for arg do case "$arg" in --basedir=*) basedir=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; --datadir=*) datadir=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; + esac + done +} + +parse_manager_arguments() { + for arg do + case "$arg" in --pid-file=*) pid_file=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; esac done @@ -125,14 +119,16 @@ then extra_args="-e $datadir/my.cnf" fi -parse_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server` +parse_server_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server` + +parse_manager_arguments `$print_defaults manager` # # Set pid file if not given # if test -z "$pid_file" then - pid_file=$datadir/`@HOSTNAME@`.pid + pid_file=$datadir/mysqlmanager-`@HOSTNAME@`.pid else case "$pid_file" in /* ) ;; @@ -147,18 +143,18 @@ case "$mode" in 'start') # Start daemon - if test -x $bindir/mysqld_safe + if test -x $sbindir/mysqlmanager then - # Give extra arguments to mysqld with the my.cnf file. This script may + # Give extra arguments to mysqlmanager with the my.cnf file. This script may # be overwritten at next upgrade. - $bindir/mysqld_safe --datadir=$datadir --pid-file=$pid_file >/dev/null 2>&1 & + $sbindir/mysqlmanager "--pid-file=$pid_file" --run-as-service >/dev/null 2>&1 & # Make lock for RedHat / SuSE if test -w /var/lock/subsys then - touch /var/lock/subsys/mysql + touch /var/lock/subsys/mysqlmanager fi else - echo "Can't execute $bindir/mysqld_safe from dir $basedir" + echo "Can't execute $sbindir/mysqlmanager from dir $basedir" fi ;; @@ -167,15 +163,15 @@ case "$mode" in # root password. if test -s "$pid_file" then - mysqld_pid=`cat $pid_file` - echo "Killing mysqld with pid $mysqld_pid" - kill $mysqld_pid - # mysqld should remove the pid_file when it exits, so wait for it. + mysqlmanager_pid=`cat $pid_file` + echo "Killing mysqlmanager with pid $mysqlmanager_pid" + kill $mysqlmanager_pid + # mysqlmanager should remove the pid_file when it exits, so wait for it. sleep 1 while [ -s $pid_file -a "$flags" != aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ] do - [ -z "$flags" ] && echo $echo_n "Wait for mysqld to exit$echo_c" || echo $echo_n ".$echo_c" + [ -z "$flags" ] && echo $echo_n "Wait for mysqlmanager to exit$echo_c" || echo $echo_n ".$echo_c" flags=a$flags sleep 1 done @@ -185,12 +181,12 @@ case "$mode" in then echo " done" fi # delete lock for RedHat / SuSE - if test -f /var/lock/subsys/mysql + if test -f /var/lock/subsys/mysqlmanager then - rm -f /var/lock/subsys/mysql + rm -f /var/lock/subsys/mysqlmanager fi else - echo "No mysqld pid file found. Looked for $pid_file." + echo "No mysqlmanager pid file found. Looked for $pid_file." fi ;; diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 4a7f70b0b87..4767508315e 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -515,6 +515,7 @@ fi %attr(755, root, root) %{_bindir}/safe_mysqld %attr(755, root, root) %{_sbindir}/mysqld +%attr(755, root, root) %{_sbindir}/mysqlmanager %attr(755, root, root) %{_sbindir}/rcmysql %attr(644, root, root) %{_libdir}/mysql/mysqld.sym From 1815de7b5528a6ac65498c751bdc1beea0d0d7ed Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Sat, 5 Feb 2005 14:04:49 +0300 Subject: [PATCH 05/14] --user option added to mysqlmanager --- .../instance-manager/mysql_connection.cc | 7 -- server-tools/instance-manager/mysqlmanager.cc | 84 +++++++++++++++++++ server-tools/instance-manager/options.cc | 9 +- server-tools/instance-manager/options.h | 3 +- 4 files changed, 92 insertions(+), 11 deletions(-) diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc index d3f58bf3771..7947de70459 100644 --- a/server-tools/instance-manager/mysql_connection.cc +++ b/server-tools/instance-manager/mysql_connection.cc @@ -82,7 +82,6 @@ private: 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); @@ -287,12 +286,6 @@ int Mysql_connection_thread::check_connection() } -int Mysql_connection_thread::check_user(const char *user, const char *password) -{ - return 0; -} - - int Mysql_connection_thread::do_command() { char *packet; diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index bdd7c4ebe78..e37dab911c1 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -54,6 +56,8 @@ static void init_environment(char *progname); static void daemonize(const char *log_file_name); static void angel(const Options &options); +static struct passwd *check_user(const char *user); +static int set_user(const char *user, struct passwd *user_info); /* @@ -68,7 +72,19 @@ int main(int argc, char *argv[]) { init_environment(argv[0]); Options options; + struct passwd *user_info; + options.load(argc, argv); + + if ((user_info= check_user(options.user))) + { + if (set_user(options.user, user_info)) + { + options.cleanup(); + return 1; + } + } + if (options.run_as_service) { /* forks, and returns only in child */ @@ -84,6 +100,74 @@ int main(int argc, char *argv[]) /******************* Auxilary functions implementation **********************/ +/* Change to run as another user if started with --user */ + +static struct passwd *check_user(const char *user) +{ +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) + struct passwd *user_info; + uid_t user_id= geteuid(); + + /* Don't bother if we aren't superuser */ + if (user_id) + { + if (user) + { + /* Don't give a warning, if real user is same as given with --user */ + user_info= getpwnam(user); + if ((!user_info || user_id != user_info->pw_uid)) + log_info("One can only use the --user switch if running as root\n"); + } + return NULL; + } + if (!user) + { + log_info("You are running mysqlmanager as root! This might introduce security problems. It is safer to use --user option istead.\n"); + return NULL; + } + if (!strcmp(user, "root")) + return NULL; /* Avoid problem with dynamic libraries */ + if (!(user_info= getpwnam(user))) + { + /* Allow a numeric uid to be used */ + const char *pos; + for (pos= user; my_isdigit(default_charset_info, *pos); pos++) ; + if (*pos) /* Not numeric id */ + goto err; + if (!(user_info= getpwuid(atoi(user)))) + goto err; + else + return user_info; + } + else + return user_info; + +err: + log_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n", user); +#endif + return NULL; +} + +static int set_user(const char *user, struct passwd *user_info) +{ + DBUG_ASSERT(user_info); +#ifdef HAVE_INITGROUPS + initgroups((char*) user,user_info->pw_gid); +#endif + if (setgid(user_info->pw_gid) == -1) + { + log_error("setgid() failed"); + return 1; + } + if (setuid(user_info->pw_uid) == -1) + { + log_error("setuid() failed"); + return 1; + } + return 0; +} + + /* Init environment, common for daemon and non-daemon diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index db117de03e5..bf6ba1e9163 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -35,7 +35,8 @@ 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::bind_address= 0; /* No default value */ +const char *Options::bind_address= 0; /* No default value */ +const char *Options::user= 0; /* No default value */ uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL; uint Options::port_number= DEFAULT_PORT; /* just to declare */ @@ -54,7 +55,6 @@ enum options { OPT_MYSQLD_PATH, OPT_RUN_AS_SERVICE, OPT_USER, - OPT_PASSWORD, OPT_MONITORING_INTERVAL, OPT_PORT, OPT_BIND_ADDRESS @@ -107,6 +107,11 @@ static struct my_option my_long_options[] = "Daemonize and start angel process.", (gptr *) &Options::run_as_service, 0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 }, + { "user", OPT_USER, "Username to start mysqlmanager", + (gptr *) &Options::user, + (gptr *) &Options::user, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index 8b673bd5fb1..bb6da51d1ca 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -34,8 +34,7 @@ struct Options 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 const char *user; static uint monitoring_interval; static uint port_number; static const char *bind_address; From 0eddb07ff7bab43de73b87a07e68bb40ab01b3f8 Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Sun, 6 Feb 2005 23:43:34 +0300 Subject: [PATCH 06/14] setup fixes --- support-files/Makefile.am | 1 + support-files/my.cnf | 2 ++ support-files/mysql.server.sh | 5 ++++- support-files/mysql.spec.sh | 10 +++------- 4 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 support-files/my.cnf diff --git a/support-files/Makefile.am b/support-files/Makefile.am index 7ae1071f9ec..c2131027db8 100644 --- a/support-files/Makefile.am +++ b/support-files/Makefile.am @@ -19,6 +19,7 @@ EXTRA_DIST = mysql.spec.sh \ my-small.cnf.sh \ + my.cnf \ my-medium.cnf.sh \ my-large.cnf.sh \ my-huge.cnf.sh \ diff --git a/support-files/my.cnf b/support-files/my.cnf new file mode 100644 index 00000000000..062c78be46a --- /dev/null +++ b/support-files/my.cnf @@ -0,0 +1,2 @@ +[mysqld] +port=3307 diff --git a/support-files/mysql.server.sh b/support-files/mysql.server.sh index 08e10c0192b..645b54b610a 100644 --- a/support-files/mysql.server.sh +++ b/support-files/mysql.server.sh @@ -136,6 +136,9 @@ else esac fi +user=@MYSQLD_USER@ +USER_OPTION="--user=$user" + # Safeguard (relative paths, core dumps..) cd $basedir @@ -147,7 +150,7 @@ case "$mode" in then # Give extra arguments to mysqlmanager with the my.cnf file. This script may # be overwritten at next upgrade. - $sbindir/mysqlmanager "--pid-file=$pid_file" --run-as-service >/dev/null 2>&1 & + $sbindir/mysqlmanager "--pid-file=$pid_file" $USER_OPTION --run-as-service >/dev/null 2>&1 & # Make lock for RedHat / SuSE if test -w /var/lock/subsys then diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 4767508315e..5f3dad86d89 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -362,8 +362,9 @@ install -s -m755 $MBD/sql/mysqld-max $RBR%{_sbindir}/mysqld-max install -m644 $MBD/sql/mysqld-max.sym $RBR%{_libdir}/mysql/mysqld-max.sym install -m644 $MBD/sql/mysqld.sym $RBR%{_libdir}/mysql/mysqld.sym -# Install logrotate and autostart +# Install logrotate, autostart and config file install -m644 $MBD/support-files/mysql-log-rotate $RBR%{_sysconfdir}/logrotate.d/mysql +install -m644 $MBD/support-files/my.cnf $RBR%{_sysconfdir}/my.cnf install -m755 $MBD/support-files/mysql.server $RBR%{_sysconfdir}/init.d/mysql # Create a symlink "rcmysql", pointing to the init.script. SuSE users @@ -374,10 +375,6 @@ ln -s %{_sysconfdir}/init.d/mysql $RPM_BUILD_ROOT%{_sbindir}/rcmysql # (safe_mysqld will be gone in MySQL 4.1) ln -sf ./mysqld_safe $RBR%{_bindir}/safe_mysqld -# Touch the place where the my.cnf config file might be located -# Just to make sure it's in the file list and marked as a config file -touch $RBR%{_sysconfdir}/my.cnf - %pre server # Shut down a previously installed server first if test -x %{_sysconfdir}/init.d/mysql @@ -486,8 +483,6 @@ fi %doc %attr(644, root, man) %{_mandir}/man1/perror.1* %doc %attr(644, root, man) %{_mandir}/man1/replace.1* -%ghost %config(noreplace,missingok) %{_sysconfdir}/my.cnf - %attr(755, root, root) %{_bindir}/my_print_defaults %attr(755, root, root) %{_bindir}/myisamchk %attr(755, root, root) %{_bindir}/myisam_ftdump @@ -520,6 +515,7 @@ fi %attr(644, root, root) %{_libdir}/mysql/mysqld.sym %attr(644, root, root) %config(noreplace,missingok) %{_sysconfdir}/logrotate.d/mysql +%attr(644, root, root) %config(noreplace,missingok) %{_sysconfdir}/my.cnf %attr(755, root, root) %{_sysconfdir}/init.d/mysql %attr(755, root, root) %{_datadir}/mysql/ From 6b50b5b087d83c28058f9adee099bcb761142b7a Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Fri, 11 Feb 2005 14:21:59 +0300 Subject: [PATCH 07/14] Post-review fixes + some bugs fixed + several minor features --- server-tools/instance-manager/Makefile.am | 2 +- server-tools/instance-manager/buffer.cc | 16 +- server-tools/instance-manager/buffer.h | 14 +- server-tools/instance-manager/client_func.c | 32 -- server-tools/instance-manager/commands.cc | 12 +- server-tools/instance-manager/guardian.cc | 317 ++++++++++++------ server-tools/instance-manager/guardian.h | 49 ++- server-tools/instance-manager/instance.cc | 196 +++++++---- server-tools/instance-manager/instance.h | 9 +- server-tools/instance-manager/instance_map.cc | 20 -- server-tools/instance-manager/instance_map.h | 8 - .../instance-manager/instance_options.cc | 89 ++--- .../instance-manager/instance_options.h | 8 +- server-tools/instance-manager/listener.cc | 4 + server-tools/instance-manager/manager.cc | 26 +- server-tools/instance-manager/options.cc | 37 +- server-tools/instance-manager/parse.cc | 23 -- server-tools/instance-manager/parse.h | 30 ++ server-tools/instance-manager/parse_output.cc | 72 ++-- server-tools/instance-manager/protocol.cc | 3 +- server-tools/instance-manager/user_map.cc | 2 +- 21 files changed, 593 insertions(+), 376 deletions(-) delete mode 100644 server-tools/instance-manager/client_func.c diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 4b776fecc83..21d11e2f7d6 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -74,7 +74,7 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.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 + mysql_manager_error.h mysqlmanager_LDADD= liboptions.a \ libnet.a \ diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index ca84adbfd10..3a2a818e478 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -40,7 +40,7 @@ RETURN 0 - ok - 1 - The buffer came to 16Mb barrier + 1 - got an error in reserve() */ int Buffer::append(uint position, const char *string, uint len_arg) @@ -71,7 +71,7 @@ int Buffer::append(uint position, const char *string, uint len_arg) RETURN 0 - ok - 1 - The buffer came to 16Mb barrier + 1 - realloc error or we have come to the 16Mb barrier */ int Buffer::reserve(uint position, uint len_arg) @@ -92,6 +92,18 @@ int Buffer::reserve(uint position, uint len_arg) return 0; err: + error= 1; return 1; } + +int Buffer::get_size() +{ + return buffer_size; +} + + +int Buffer::is_error() +{ + return error; +} diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h index 66860bd67b5..260a9ef92f8 100644 --- a/server-tools/instance-manager/buffer.h +++ b/server-tools/instance-manager/buffer.h @@ -36,11 +36,17 @@ private: /* maximum buffer size is 16Mb */ enum { MAX_BUFFER_SIZE= 16777216 }; size_t buffer_size; + /* Error flag. Triggered if we get an error of some kind */ + int error; public: - Buffer() + Buffer(size_t buffer_size_arg= BUFFER_INITIAL_SIZE) + :buffer_size(BUFFER_INITIAL_SIZE), error(0) { - buffer=(char *) malloc(BUFFER_INITIAL_SIZE); - buffer_size= BUFFER_INITIAL_SIZE; + /* + As append() will invokes realloc() anyway, it's ok if malloc returns 0 + */ + if (!(buffer= (char*) malloc(buffer_size))) + buffer_size= 0; } ~Buffer() @@ -50,6 +56,8 @@ public: public: char *buffer; + int get_size(); + int is_error(); int append(uint position, const char *string, uint len_arg); int reserve(uint position, uint len_arg); }; diff --git a/server-tools/instance-manager/client_func.c b/server-tools/instance-manager/client_func.c deleted file mode 100644 index 92c106e5172..00000000000 --- a/server-tools/instance-manager/client_func.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include -#include - -/* - 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. -*/ - -const char * STDCALL -mysql_get_server_info(MYSQL *mysql) -{ - return((char*) mysql->server_version); -} - -int STDCALL -mysql_ping(MYSQL *mysql) -{ - DBUG_ENTER("mysql_ping"); - DBUG_RETURN(simple_command(mysql,COM_PING,0,0,0)); -} - -int STDCALL -mysql_shutdown(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level) -{ - uchar level[1]; - DBUG_ENTER("mysql_shutdown"); - level[0]= (uchar) shutdown_level; - DBUG_RETURN(simple_command(mysql, COM_SHUTDOWN, (char *)level, 1, 0)); -} diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 70774115429..3f38f21b008 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -184,7 +184,8 @@ int Show_instance_status::do_command(struct st_net *net, } - if (my_net_write(net, send_buff.buffer, (uint) position)) + if (my_net_write(net, send_buff.buffer, (uint) position) || + send_buff.is_error()) goto err; } @@ -270,7 +271,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, (char *) instance->options.mysqld_path, &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) + if (my_net_write(net, send_buff.buffer, (uint) position) || + send_buff.is_error()) goto err; } @@ -279,7 +281,8 @@ int Show_instance_options::do_command(struct st_net *net, position= 0; store_to_string(&send_buff, (char *) "nonguarded", &position); store_to_string(&send_buff, "", &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) + if (my_net_write(net, send_buff.buffer, (uint) position) || + send_buff.is_error()) goto err; } @@ -296,7 +299,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, option_value + 1, &position); /* join name and the value into the same option again */ *option_value= '='; - if (my_net_write(net, send_buff.buffer, (uint) position)) + if (my_net_write(net, send_buff.buffer, (uint) position) || + send_buff.is_error()) goto err; } } diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index bc05fda1a8f..750c3495870 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -21,9 +21,32 @@ #include "guardian.h" #include "instance_map.h" +#include "instance.h" #include "mysql_manager_error.h" #include "log.h" #include +#include +#include + + +/* + The Guardian list node structure. Guardian utilizes it to store + guarded instances plus some additional info. +*/ + +struct GUARD_NODE +{ + Instance *instance; + /* state of an instance (i.e. STARTED, CRASHED, etc.) */ + int state; + /* the amount of attemts to restart instance (cleaned up at success) */ + int restart_counter; + /* triggered at a crash */ + time_t crash_moment; + /* General time field. Used to provide timeouts (at shutdown and restart) */ + time_t last_checked; +}; + C_MODE_START @@ -42,15 +65,13 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, uint monitoring_interval_arg) : Guardian_thread_args(thread_registry_arg, instance_map_arg, monitoring_interval_arg), - thread_info(pthread_self()) + thread_info(pthread_self()), guarded_instances(0) { pthread_mutex_init(&LOCK_guardian, 0); pthread_cond_init(&COND_guardian, 0); - shutdown_guardian= FALSE; - is_stopped= FALSE; + shutdown_requested= FALSE; + stopped= FALSE; init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); - guarded_instances= NULL; - starting_instances= NULL; } @@ -65,19 +86,107 @@ Guardian_thread::~Guardian_thread() } -void Guardian_thread::shutdown() +void Guardian_thread::request_shutdown(bool stop_instances_arg) { pthread_mutex_lock(&LOCK_guardian); - shutdown_guardian= TRUE; + /* stop instances or just clean up Guardian repository */ + stop_instances(stop_instances_arg); + shutdown_requested= TRUE; pthread_mutex_unlock(&LOCK_guardian); } -void Guardian_thread::request_stop_instances() +void Guardian_thread::process_instance(Instance *instance, + GUARD_NODE *current_node, + LIST **guarded_instances, + LIST *elem) { - pthread_mutex_lock(&LOCK_guardian); - request_stop= TRUE; - pthread_mutex_unlock(&LOCK_guardian); + int waitchild= Instance::DEFAULT_SHUTDOWN_DELAY; + /* The amount of times, Guardian attempts to restart an instance */ + int restart_retry= 100; + time_t current_time= time(NULL); + + if (current_node->state == STOPPING) + { + /* this brach is executed during shutdown */ + if (instance->options.shutdown_delay != NULL) + waitchild= atoi(instance->options.shutdown_delay); + + /* this returns true if and only if an instance was stopped for shure */ + if (instance->is_crashed()) + *guarded_instances= list_delete(*guarded_instances, elem); + else if (current_time - current_node->last_checked > waitchild) + { + instance->kill_instance(SIGKILL); + /* + Later we do elem= elem->next. This is ok, as we are only removing + the node from the list. The pointer to the next one is still valid. + */ + *guarded_instances= list_delete(*guarded_instances, elem); + } + + return; + } + + if (instance->is_running()) + { + /* clear status fields */ + current_node->restart_counter= 0; + current_node->crash_moment= 0; + current_node->state= STARTED; + } + else + { + switch (current_node->state) + { + case NOT_STARTED: + instance->start(); + current_node->last_checked= current_time; + log_info("guardian: starting instance %s", + instance->options.instance_name); + current_node->state= STARTING; + break; + case STARTED: /* fallthrough */ + case STARTING: /* let the instance start or crash */ + if (instance->is_crashed()) + { + current_node->crash_moment= current_time; + current_node->last_checked= current_time; + current_node->state= JUST_CRASHED; + /* fallthrough -- restart an instance immediately */ + } + else + break; + case JUST_CRASHED: + if (current_time - current_node->crash_moment <= 2) + { + instance->start(); + log_info("guardian: starting instance %s", + instance->options.instance_name); + } + else current_node->state= CRASHED; + break; + case CRASHED: /* just regular restarts */ + if (current_time - current_node->last_checked > + monitoring_interval) + { + if ((current_node->restart_counter < restart_retry)) + { + instance->start(); + current_node->last_checked= current_time; + ((GUARD_NODE *) elem->data)->restart_counter++; + log_info("guardian: starting instance %s", + instance->options.instance_name); + } + else current_node->state= CRASHED_AND_ABANDONED; + } + break; + case CRASHED_AND_ABANDONED: + break; /* do nothing */ + default: + DBUG_ASSERT(0); + } + } } @@ -96,8 +205,7 @@ void Guardian_thread::request_stop_instances() void Guardian_thread::run() { Instance *instance; - int restart_retry= 100; - LIST *loop; + LIST *elem; struct timespec timeout; thread_registry.register_thread(&thread_info); @@ -105,68 +213,31 @@ void Guardian_thread::run() my_thread_init(); pthread_mutex_lock(&LOCK_guardian); - - while (!shutdown_guardian) + /* loop, until all instances were shut down at the end */ + while (!(shutdown_requested && (guarded_instances == NULL))) { - int status= 0; - loop= guarded_instances; + elem= guarded_instances; - while (loop != NULL) + while (elem != NULL) { - instance= ((GUARD_NODE *) loop->data)->instance; - if (!instance->is_running()) - { - int state= 0; /* state of guardian */ + struct timespec timeout; - if ((((GUARD_NODE *) loop->data)->crash_moment == 0)) - state= 1; /* an instance just crashed */ - else - if (time(NULL) - ((GUARD_NODE *) loop->data)->crash_moment <= 2) - /* try to restart an instance immediately */ - state= 2; - else - state= 3; /* try to restart it */ + GUARD_NODE *current_node= (GUARD_NODE *) elem->data; + instance= ((GUARD_NODE *) elem->data)->instance; + process_instance(instance, current_node, &guarded_instances, elem); - if (state == 1) - ((GUARD_NODE *) loop->data)->crash_moment= time(NULL); - - if ((state == 1) || (state == 2)) - { - instance->start(); - ((GUARD_NODE *) loop->data)->restart_counter++; - log_info("guardian: starting instance %s", - instance->options.instance_name); - } - else - { - if ((status == ETIMEDOUT) && - (((GUARD_NODE *) loop->data)->restart_counter < restart_retry)) - { - instance->start(); - ((GUARD_NODE *) loop->data)->restart_counter++; - log_info("guardian: starting instance %s", - instance->options.instance_name); - } - } - } - else /* clear status fields */ - { - ((GUARD_NODE *) loop->data)->restart_counter= 0; - ((GUARD_NODE *) loop->data)->crash_moment= 0; - } - loop= loop->next; + elem= elem->next; } - move_to_list(&starting_instances, &guarded_instances); timeout.tv_sec= time(NULL) + monitoring_interval; timeout.tv_nsec= 0; - status= pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout); + /* check the loop predicate before sleeping */ + if (!(shutdown_requested && (guarded_instances == NULL))) + pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout); } + stopped= TRUE; pthread_mutex_unlock(&LOCK_guardian); - if (request_stop) - stop_instances(); - is_stopped= TRUE; /* now, when the Guardian is stopped we can stop the IM */ thread_registry.unregister_thread(&thread_info); thread_registry.request_shutdown(); @@ -174,7 +245,29 @@ void Guardian_thread::run() } -int Guardian_thread::start() +int Guardian_thread::is_stopped() +{ + int var; + pthread_mutex_lock(&LOCK_guardian); + var= stopped; + pthread_mutex_unlock(&LOCK_guardian); + return var; +} + + +/* + Initialize the list of guarded instances: loop through the Instance_map and + add all of the instances, which don't have 'nonguarded' option specified. + + SYNOPSYS + Guardian_thread::init() + + RETURN + 0 - ok + 1 - error occured +*/ + +int Guardian_thread::init() { Instance *instance; Instance_map::Iterator iterator(instance_map); @@ -183,7 +276,7 @@ int Guardian_thread::start() while ((instance= iterator.next())) { if ((instance->options.nonguarded == NULL)) - if (add_instance_to_list(instance, &guarded_instances)) + if (guard(instance)) return 1; } instance_map->unlock(); @@ -193,7 +286,7 @@ int Guardian_thread::start() /* - Start instance guarding + Add instance to the Guardian list SYNOPSYS guard() @@ -201,36 +294,15 @@ int Guardian_thread::start() DESCRIPTION - The instance is added to the list of starting instances. Then after one guardian - loop it is moved to the guarded instances list. Usually guard() is called after we - start an instance, so we need to give some time to the instance to start. + The instance is added to the guarded instances list. Usually guard() is + called after we start an instance. RETURN 0 - ok 1 - error occured */ - int Guardian_thread::guard(Instance *instance) -{ - return add_instance_to_list(instance, &starting_instances); -} - - -void Guardian_thread::move_to_list(LIST **from, LIST **to) -{ - LIST *tmp; - - while (*from) - { - tmp= rest(*from); - *to= list_add(*to, *from); - *from= tmp; - } -} - - -int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list) { LIST *node; GUARD_NODE *content; @@ -244,10 +316,11 @@ int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list) content->instance= instance; content->restart_counter= 0; content->crash_moment= 0; + content->state= NOT_STARTED; node->data= (void *) content; pthread_mutex_lock(&LOCK_guardian); - *list= list_add(*list, node); + guarded_instances= list_add(guarded_instances, node); pthread_mutex_unlock(&LOCK_guardian); return 0; @@ -256,7 +329,7 @@ int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list) /* 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 + 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). */ @@ -288,21 +361,61 @@ int Guardian_thread::stop_guard(Instance *instance) return 0; } -int Guardian_thread::stop_instances() -{ - Instance *instance; - Instance_map::Iterator iterator(instance_map); +/* + Start Guardian shutdown. Attempt to start instances if requested. - while ((instance= iterator.next())) + SYNOPSYS + stop_instances() + stop_instances_arg whether we should stop instances at shutdown + + DESCRIPTION + + Loops through the guarded_instances list and prepares them for shutdown. + If stop_instances was requested, we need to issue a stop command and change + the state accordingly. Otherwise we could simply delete an entry. + NOTE: Guardian should be locked by the calling function + + RETURN + 0 - ok + 1 - error occured +*/ + +int Guardian_thread::stop_instances(bool stop_instances_arg) +{ + LIST *node; + node= guarded_instances; + while (node != NULL) { - if ((instance->options.nonguarded == NULL)) + if (!stop_instances_arg) { - if (stop_guard(instance)) - return 1; - /* let us try to stop the server */ - instance->stop(); + /* just forget about an instance */ + guarded_instances= list_delete(guarded_instances, node); + /* + This should still work fine, as we have only removed the + node from the list. The pointer to the next one is still valid + */ + node= node->next; + } + else + { + GUARD_NODE *current_node= (GUARD_NODE *) node->data; + /* + If instance is running or was running (and now probably hanging), + request stop. + */ + if (current_node->instance->is_running() || + (current_node->state == STARTED)) + { + current_node->state= STOPPING; + current_node->last_checked= time(NULL); + } + else + /* otherwise remove it from the list */ + guarded_instances= list_delete(guarded_instances, node); + /* But try to kill it anyway. Just in case */ + current_node->instance->kill_instance(SIGTERM); + node= node->next; } } - return 0; } diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index bf96436a636..0aec00099de 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -19,6 +19,7 @@ #include #include #include +#include "thread_registry.h" #ifdef __GNUC__ #pragma interface @@ -26,9 +27,8 @@ class Instance; class Instance_map; - -#include "thread_registry.h" -#include "instance.h" +class Thread_registry; +struct GUARD_NODE; C_MODE_START @@ -36,19 +36,11 @@ pthread_handler_decl(guardian, arg); C_MODE_END -typedef struct st_guard_node -{ - Instance *instance; - uint restart_counter; - time_t crash_moment; -} GUARD_NODE; - - struct Guardian_thread_args { Thread_registry &thread_registry; Instance_map *instance_map; - uint monitoring_interval; + int monitoring_interval; Guardian_thread_args(Thread_registry &thread_registry_arg, Instance_map *instance_map_arg, @@ -72,36 +64,41 @@ public: Instance_map *instance_map_arg, uint monitoring_interval_arg); ~Guardian_thread(); + /* Main funtion of the thread */ void run(); - int start(); - void shutdown(); - void request_stop_instances(); + /* Initialize list of guarded instances */ + int init(); + /* Request guardian shutdown. Stop instances if needed */ + void request_shutdown(bool stop_instances); + /* Start instance protection */ int guard(Instance *instance); + /* Stop instance protection */ int stop_guard(Instance *instance); - bool is_stopped; + /* Returns true if guardian thread is stopped */ + int is_stopped(); public: pthread_cond_t COND_guardian; private: - int stop_instances(); - int add_instance_to_list(Instance *instance, LIST **list); - void move_to_list(LIST **from, LIST **to); + /* Prepares Guardian shutdown. Stops instances is needed */ + int stop_instances(bool stop_instances_arg); + /* check instance state and act accordingly */ + void process_instance(Instance *instance, GUARD_NODE *current_node, + LIST **guarded_instances, LIST *elem); + int stopped; private: + /* states of an instance */ + enum { NOT_STARTED= 1, STARTING, STARTED, JUST_CRASHED, CRASHED, + CRASHED_AND_ABANDONED, STOPPING }; pthread_mutex_t LOCK_guardian; Thread_info thread_info; LIST *guarded_instances; - LIST *starting_instances; MEM_ROOT alloc; enum { MEM_ROOT_BLOCK_SIZE= 512 }; /* this variable is set to TRUE when we want to stop Guardian thread */ - bool shutdown_guardian; - /* - This var is usually set together with shutdown_guardian. this way we - request guardian to shut down all instances before termination - */ - bool request_stop; + bool shutdown_requested; }; #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */ diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 3ba943c9cd1..e654612de3e 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -27,6 +27,19 @@ #include #include + +C_MODE_START + +pthread_handler_decl(proxy, arg) +{ + Instance *instance= (Instance *) arg; + instance->fork_and_monitor(); + return 0; +} + +C_MODE_END + + /* The method starts an instance. @@ -44,6 +57,12 @@ int Instance::start() { pid_t pid; + /* clear crash flag */ + pthread_mutex_lock(&LOCK_instance); + crashed= 0; + pthread_mutex_unlock(&LOCK_instance); + + if (!is_running()) { if ((pid= options.get_pid()) != 0) /* check the pidfile */ @@ -52,17 +71,26 @@ int Instance::start() since IM lacks permmissions or hasn't found the pidifle", options.instance_name); - log_info("starting instance %s", options.instance_name); - switch (pid= fork()) { - case 0: - execv(options.mysqld_path, options.argv); - /* exec never returns */ - exit(1); - case -1: + /* + No need to monitor this thread in the Thread_registry, as all + instances are to be stopped during shutdown. + */ + pthread_t proxy_thd_id; + pthread_attr_t proxy_thd_attr; + int rc; + + pthread_attr_init(&proxy_thd_attr); + pthread_attr_setdetachstate(&proxy_thd_attr, PTHREAD_CREATE_DETACHED); + rc= pthread_create(&proxy_thd_id, &proxy_thd_attr, proxy, + this); + pthread_attr_destroy(&proxy_thd_attr); + if (rc) + { + log_error("Instance::start(): pthread_create(proxy) failed"); return ER_CANNOT_START_INSTANCE; - default: - return 0; } + + return 0; } /* the instance is started already */ @@ -70,9 +98,62 @@ int Instance::start() } +void Instance::fork_and_monitor() +{ + pid_t pid; + log_info("starting instance %s", options.instance_name); + switch (pid= fork()) { + case 0: + execv(options.mysqld_path, options.argv); + /* exec never returns */ + exit(1); + case -1: + log_info("cannot fork() to start instance %s", options.instance_name); + return; + default: + wait(NULL); + /* set instance state to crashed */ + pthread_mutex_lock(&LOCK_instance); + crashed= 1; + pthread_mutex_unlock(&LOCK_instance); + + /* + Wake connection threads waiting for an instance to stop. This + is needed if a user issued command to stop an instance via + mysql connection. This is not the case if Guardian stop the thread. + */ + pthread_cond_signal(&COND_instance_restarted); + /* wake guardian */ + pthread_cond_signal(&instance_map->guardian->COND_guardian); + /* thread exits */ + return; + } + /* we should never end up here */ + DBUG_ASSERT(0); +} + + +Instance::Instance(): crashed(0) +{ + pthread_mutex_init(&LOCK_instance, 0); + pthread_cond_init(&COND_instance_restarted, 0); +} + + Instance::~Instance() { pthread_mutex_destroy(&LOCK_instance); + pthread_cond_destroy(&COND_instance_restarted); +} + + +int Instance::is_crashed() +{ + int val; + pthread_mutex_lock(&LOCK_instance); + val= crashed; + pthread_mutex_unlock(&LOCK_instance); + return val; } @@ -95,20 +176,19 @@ bool Instance::is_running() pthread_mutex_lock(&LOCK_instance); mysql_init(&mysql); - /* try to connect to a server with the fake username/password pair */ + /* try to connect to a server with a fake username/password pair */ if (mysql_real_connect(&mysql, LOCAL_HOST, username, password, NullS, port, socket, 0)) { /* - Very strange. We have successfully connected to the server using - bullshit as username/password. Write a warning to the logfile. + We have successfully connected to the server using fake + 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_val= TRUE; /* server is alive */ } @@ -151,60 +231,60 @@ int Instance::stop() if (options.shutdown_delay != NULL) waitchild= atoi(options.shutdown_delay); - if ((pid= options.get_pid()) != 0) /* get pid from pidfile */ + kill_instance(SIGTERM); + /* sleep on condition to wait for SIGCHLD */ + + timeout.tv_sec= time(NULL) + waitchild; + timeout.tv_nsec= 0; + if (pthread_mutex_lock(&LOCK_instance)) + goto err; + + while (options.get_pid() != 0) /* while server isn't stopped */ { - /* - If we cannot kill mysqld, then it has propably crashed. - Let us try to remove staled pidfile and return succes as mysqld - is probably 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); + int status; - 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) /* while server isn't stopped */ - { - 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); - } - - return 0; + status= pthread_cond_timedwait(&COND_instance_restarted, + &LOCK_instance, + &timeout); + if (status == ETIMEDOUT) + break; } + pthread_mutex_unlock(&LOCK_instance); + + kill_instance(SIGKILL); + + return 0; + return ER_INSTANCE_IS_NOT_STARTED; err: return ER_STOP_INSTANCE; } +void Instance::kill_instance(int signum) +{ + pid_t pid; + /* if there are no pid, everything seems to be fine */ + if ((pid= options.get_pid()) != 0) /* get pid from pidfile */ + { + /* + If we cannot kill mysqld, then it has propably crashed. + Let us try to remove staled pidfile and return successfully + as mysqld is probably stopped. + */ + if (!kill(pid, signum)) + options.unlink_pidfile(); + else + if (signum == SIGKILL) /* really killed instance with SIGKILL */ + log_error("The instance %s is being stopped forsibly. Normally \ + it should not happed. Probably the instance has been \ + hanging. You should also check your IM setup", + options.instance_name); + } + return; +} + /* We execute this function to initialize instance parameters. Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY. @@ -212,8 +292,6 @@ err: int Instance::init(const char *name_arg) { - pthread_mutex_init(&LOCK_instance, 0); - return options.init(name_arg); } diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 95852542fb4..fbe24e17554 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -30,14 +30,19 @@ class Instance_map; class Instance { public: + Instance(); + ~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(); int start(); int stop(); + /* send a signal to the instance */ + void kill_instance(int signo); + int is_crashed(); + void fork_and_monitor(); public: enum { DEFAULT_SHUTDOWN_DELAY= 35 }; @@ -49,7 +54,9 @@ private: double start of the instance. This happens when the instance is starting and we issue the start command once more. */ + int crashed; pthread_mutex_t LOCK_instance; + pthread_cond_t COND_instance_restarted; Instance_map *instance_map; }; diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index ba373087db4..5044bfb0f43 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -120,9 +120,6 @@ 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,8 +132,6 @@ 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); } @@ -198,21 +193,6 @@ void Instance_map::complete_initialization() } -Instance * -Instance_map::find(uint instance_number) -{ - Instance *instance; - char name[80]; - - 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); - return instance; -} - - /* load options from config files and create appropriate instance structures */ int Instance_map::load() diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index f2121f7141d..b8b5ad0a890 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -27,12 +27,6 @@ #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,8 +77,6 @@ public: public: const char *mysqld_path; Guardian_thread *guardian; - /* structure used for syncronization reasons in the stop command */ - CHILD_COND pid_cond; private: enum { START_HASH_SIZE = 16 }; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index e7e17691330..ab97c86316b 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -27,25 +27,50 @@ #include -/* option_name should be prefixed with "--" */ -int Instance_options::get_default_option(char *result, const char *option_name, - size_t result_len) +/* + Get compiled-in value of default_option + + SYNOPSYS + get_default_option() + result buffer to put found value + result_len buffer size + oprion_name the name of the option, prefixed with "--" + + DESCRIPTION + + Get compile-in value of requested option from server + + RETURN + 0 - ok + 1 - error occured +*/ + +int Instance_options::get_default_option(char *result, size_t result_len, + const char *option_name) { int position= 0; + int rc= 1; 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; + Buffer cmd(strlen(mysqld_path)+sizeof(verbose_option)+1); + if (cmd.get_size()) /* malloc succeeded */ + { + 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); - return 0; + if (cmd.is_error()) + goto err; + /* get the value from "mysqld --help --verbose" */ + rc= parse_output_and_get_value(cmd.buffer, option_name + 2, + result, result_len); + } + + return rc; +err: + return 1; } @@ -56,51 +81,33 @@ void Instance_options::get_pid_filename(char *result) if (mysqld_datadir == NULL) { - get_default_option(datadir, "--datadir", MAX_PATH_LEN); + get_default_option(datadir, sizeof(datadir), "--datadir"); } 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); + DBUG_ASSERT(mysqld_pid_file); + pid_file= strchr(pid_file, '=') + 1; /* 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); + return unlink(pid_file_with_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); + FILE *pid_file_stream; /* get the pid */ - if (FILE *pid_file_stream= my_fopen(pid_file_path, - O_RDONLY | O_BINARY, MYF(0))) + if (pid_file_stream= my_fopen(pid_file_with_path, + O_RDONLY | O_BINARY, MYF(0))) { pid_t pid; @@ -140,6 +147,8 @@ int Instance_options::complete_initialization(const char *default_path) add_option(pidfilename); } + get_pid_filename(pid_file_with_path); + /* 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*)))) @@ -244,6 +253,8 @@ int Instance_options::add_to_argv(const char* option) return 0; } + +/* function for debug purposes */ void Instance_options::print_argv() { int i; diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 1ed11fc9afa..f6d4a6d93c6 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -39,7 +39,7 @@ public: Instance_options() : mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), nonguarded(0), - filled_default_options(0) + shutdown_delay(0), filled_default_options(0) {} ~Instance_options(); /* fills in argv */ @@ -60,6 +60,7 @@ public: enum { MAX_PATH_LEN= 512 }; enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 2 }; enum { MEM_ROOT_BLOCK_SIZE= 512 }; + char pid_file_with_path[MAX_PATH_LEN]; char **argv; /* We need the some options, so we store them as a separate pointers */ const char *mysqld_socket; @@ -72,11 +73,12 @@ public: const char *mysqld_path; const char *nonguarded; const char *shutdown_delay; + /* this value is computed and cashed here */ 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); + int get_default_option(char *result, size_t result_len, + const char *option_name); private: uint filled_default_options; MEM_ROOT alloc; diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 8266c0dac4c..14970989e92 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -85,6 +85,8 @@ void Listener_thread::run() thread_registry.register_thread(&thread_info); + my_thread_init(); + /* I. prepare 'listen' sockets */ int ip_socket= socket(AF_INET, SOCK_STREAM, 0); @@ -263,11 +265,13 @@ void Listener_thread::run() unlink(unix_socket_address.sun_path); thread_registry.unregister_thread(&thread_info); + my_thread_end(); return; err: thread_registry.unregister_thread(&thread_info); thread_registry.request_shutdown(); + my_thread_end(); return; } diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index b48c020786f..af8dac47dca 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -90,7 +90,6 @@ void manager(const Options &options) sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGPIPE); sigaddset(&mask, SIGHUP); /* @@ -128,7 +127,7 @@ void manager(const Options &options) int rc; /* - NOTE: Guardian should be shutdowned first. Only then all other threads + NOTE: Guardian should be shutdown first. Only then all other threads need to be stopped. This should be done, as guardian is responsible for shutting down the instances, and this is a long operation. */ @@ -160,12 +159,8 @@ void manager(const Options &options) more then 10 alarms at the same time. */ init_thr_alarm(10); - /* - Now we can init the list of guarded instances. We have to do it after - alarm structures initialization as we have to use net_* functions while - making the list. And they in their turn need alarms for timeout suppport. - */ - guardian_thread.start(); + /* init list of guarded instances */ + guardian_thread.init(); /* After the list of guarded instances have been initialized, Guardian should start them. @@ -182,18 +177,12 @@ 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_signal(&guardian_thread.COND_guardian); - break; default: - if (!guardian_thread.is_stopped) + { + if (!guardian_thread.is_stopped()) { - guardian_thread.request_stop_instances(); - guardian_thread.shutdown(); + bool stop_instances= true; + guardian_thread.request_shutdown(stop_instances); pthread_cond_signal(&guardian_thread.COND_guardian); } else @@ -201,6 +190,7 @@ void manager(const Options &options) thread_registry.deliver_shutdown(); shutdown_complete= TRUE; } + } break; } } diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index bf6ba1e9163..18d98a3d9a6 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "priv.h" @@ -77,6 +79,9 @@ static struct my_option my_long_options[] = (gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "passwd", 'P', "Prepare entry for passwd file and exit.", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "bind-address", OPT_BIND_ADDRESS, "Bind address to use for connection.", (gptr *) &Options::bind_address, (gptr *) &Options::bind_address, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, @@ -142,6 +147,34 @@ static void usage() my_print_variables(my_long_options); } + +static void passwd() +{ + char user[1024], pw[1024], *p; + char crypted_pw[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1]; + + fprintf(stderr, "Creating record for new user.\n"); + fprintf(stderr, "Enter user name: "); + if (!fgets(user, sizeof(user), stdin)) + { + fprintf(stderr, "Unable to read user.\n"); + return; + } + if ((p= strchr(user, '\n'))) *p= 0; + + fprintf(stderr, "Enter password: "); + if (! fgets(pw, sizeof(pw), stdin)) + { + fprintf(stderr, "Unable to read password.\n"); + return; + } + if ((p= strchr(pw, '\n'))) *p= 0; + + make_scrambled_password(crypted_pw, pw); + printf("%s:%s\n", user, crypted_pw); +} + + C_MODE_START static my_bool @@ -153,7 +186,9 @@ get_one_option(int optid, case 'V': version(); exit(0); - case 'I': + case 'P': + passwd(); + exit(0); case '?': usage(); exit(0); diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc index d029267f9b8..4c897ddf933 100644 --- a/server-tools/instance-manager/parse.cc +++ b/server-tools/instance-manager/parse.cc @@ -49,29 +49,6 @@ static struct tokens_st tokens[]= { }; -/* - 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 diff --git a/server-tools/instance-manager/parse.h b/server-tools/instance-manager/parse.h index 236a9bee53a..92519893302 100644 --- a/server-tools/instance-manager/parse.h +++ b/server-tools/instance-manager/parse.h @@ -20,4 +20,34 @@ Command *parse_command(Command_factory *factory, const char *text); +/* define kinds of the word seek method */ +enum { ALPHANUM= 1, NONSPACE }; + +/* + 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, + int seek_method= ALPHANUM) +{ + const char *word_end; + + /* skip space */ + while (my_isspace(default_charset_info, **text)) + ++(*text); + + word_end= *text; + + if (seek_method == ALPHANUM) + while (my_isalnum(default_charset_info, *word_end)) + ++word_end; + else + while (!my_isspace(default_charset_info, *word_end)) + ++word_end; + + *word_len= word_end - *text; +} + #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H */ diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc index 2bc51922e69..4276062caf2 100644 --- a/server-tools/instance-manager/parse_output.cc +++ b/server-tools/instance-manager/parse_output.cc @@ -14,43 +14,38 @@ 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 #include #include #include -/* 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); +/* + Parse output of the given command - /* skip space */ - while (my_isspace(default_charset_info, (char) currchar) && - currchar != EOF && size > 1) - { - currchar= getc(file); - } + SYNOPSYS + parse_output_and_get_value() - while (!my_isspace(default_charset_info, (char) currchar) && - currchar != EOF && size > 1) - { - *buf++= (char) currchar; - currchar= getc(file); - size--; - } + command the command to execue with popen. + word the word to look for (usually an option name) + result the buffer to store the next word (option value) + result_len self-explanatory - *buf= '\0'; - return 0; -} + DESCRIPTION + Parse output of the "command". Find the "word" and return the next one +*/ int parse_output_and_get_value(const char *command, const char *word, char *result, size_t result_len) { FILE *output; - int wordlen; + uint wordlen; + /* should be enought to store the string from the output */ + enum { MAX_LINE_LEN= 512 }; + char linebuf[MAX_LINE_LEN]; wordlen= strlen(word); @@ -62,19 +57,32 @@ int parse_output_and_get_value(const char *command, const char *word, */ setvbuf(output, NULL, _IOFBF, 0); - get_word(output, result, result_len); - while (strncmp(word, result, wordlen) && *result != '\0') + while (fgets(linebuf, sizeof(linebuf) - 1, output)) { - get_word(output, result, result_len); + uint lineword_len= 0; + char *linep= linebuf; + + linebuf[sizeof(linebuf) - 1]= '\0'; /* safety */ + + /* + Get the word, which might contain non-alphanumeric characters. (Usually + these are '/', '-' and '.' in the path expressions and filenames) + */ + get_word((const char **) &linep, &lineword_len, NONSPACE); + if (!strncmp(word, linep, wordlen) && *result != '\0') + { + /* + If we have found the word, return the next one. This is usually + an option value. + */ + get_word((const char **) &linep, &lineword_len, NONSPACE); + DBUG_ASSERT(result_len > lineword_len); + strncpy(result, linep, lineword_len); + goto pclose; + } } - /* - 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); - +pclose: if (pclose(output)) return 1; diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc index 581157ccd72..60cd0e8714f 100644 --- a/server-tools/instance-manager/protocol.cc +++ b/server-tools/instance-manager/protocol.cc @@ -154,7 +154,8 @@ int send_fields(struct st_net *net, LIST *fields) store_to_string(&send_buff, (char *) "", &position); /* table name alias */ store_to_string(&send_buff, field->name, &position); /* column name */ store_to_string(&send_buff, field->name, &position); /* column name alias */ - if (send_buff.reserve(position, 12)) + send_buff.reserve(position, 12); + if (send_buff.is_error()) goto err; send_buff.buffer[position++]= 12; int2store(send_buff.buffer + position, 1); /* charsetnr */ diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index 21d66912813..7cb2cd67d7f 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -69,7 +69,7 @@ int User::init(const char *line) return 0; err: - log_error("error parsing user and password at line %d", line); + log_error("error parsing user and password at line %s", line); return 1; } From bb8e5b6227ef83b67fd9a786bd32768231d42bdb Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Sun, 13 Feb 2005 15:13:33 +0300 Subject: [PATCH 08/14] various fixes --- server-tools/instance-manager/buffer.cc | 8 ++++---- server-tools/instance-manager/buffer.h | 3 ++- server-tools/instance-manager/commands.cc | 16 ++++++++-------- .../instance-manager/instance_options.cc | 6 ++---- server-tools/instance-manager/manager.cc | 9 ++++++++- server-tools/instance-manager/parse_output.cc | 17 ++++++++++++++--- 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index 3a2a818e478..aaed161838d 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -81,10 +81,10 @@ int Buffer::reserve(uint position, uint len_arg) if (position + len_arg>= buffer_size) { - buffer= (char *) realloc(buffer, - min(MAX_BUFFER_SIZE, - max((uint) (buffer_size*1.5), - position + len_arg))); + buffer= (char *) my_realloc(buffer, + min(MAX_BUFFER_SIZE, + max((uint) (buffer_size*1.5), + position + len_arg)), MYF(0)); if (buffer == NULL) goto err; buffer_size= (uint) (buffer_size*1.5); diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h index 260a9ef92f8..85dd210cac2 100644 --- a/server-tools/instance-manager/buffer.h +++ b/server-tools/instance-manager/buffer.h @@ -17,6 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #ifdef __GNUC__ #pragma interface @@ -45,7 +46,7 @@ public: /* As append() will invokes realloc() anyway, it's ok if malloc returns 0 */ - if (!(buffer= (char*) malloc(buffer_size))) + if (!(buffer= (char*) my_malloc(buffer_size, MYF(0)))) buffer_size= 0; } diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 3f38f21b008..4667e76bfd6 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -184,8 +184,8 @@ int Show_instance_status::do_command(struct st_net *net, } - if (my_net_write(net, send_buff.buffer, (uint) position) || - send_buff.is_error()) + if (send_buff.is_error() || + my_net_write(net, send_buff.buffer, (uint) position)) goto err; } @@ -271,8 +271,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, (char *) instance->options.mysqld_path, &position); - if (my_net_write(net, send_buff.buffer, (uint) position) || - send_buff.is_error()) + if (send_buff.is_error() || + my_net_write(net, send_buff.buffer, (uint) position)) goto err; } @@ -281,8 +281,8 @@ int Show_instance_options::do_command(struct st_net *net, position= 0; store_to_string(&send_buff, (char *) "nonguarded", &position); store_to_string(&send_buff, "", &position); - if (my_net_write(net, send_buff.buffer, (uint) position) || - send_buff.is_error()) + if (send_buff.is_error() || + my_net_write(net, send_buff.buffer, (uint) position)) goto err; } @@ -299,8 +299,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, option_value + 1, &position); /* join name and the value into the same option again */ *option_value= '='; - if (my_net_write(net, send_buff.buffer, (uint) position) || - send_buff.is_error()) + if (send_buff.is_error() || + my_net_write(net, send_buff.buffer, (uint) position)) goto err; } } diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index ab97c86316b..7c41a556cc4 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -80,9 +80,7 @@ void Instance_options::get_pid_filename(char *result) char datadir[MAX_PATH_LEN]; if (mysqld_datadir == NULL) - { get_default_option(datadir, sizeof(datadir), "--datadir"); - } else strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1, "/", NullS); @@ -106,8 +104,8 @@ pid_t Instance_options::get_pid() FILE *pid_file_stream; /* get the pid */ - if (pid_file_stream= my_fopen(pid_file_with_path, - O_RDONLY | O_BINARY, MYF(0))) + if ((pid_file_stream= my_fopen(pid_file_with_path, + O_RDONLY | O_BINARY, MYF(0))) != NULL) { pid_t pid; diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index af8dac47dca..87ad938615f 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -171,7 +171,14 @@ void manager(const Options &options) while (!shutdown_complete) { - sigwait(&mask, &signo); + int status= 0; + + if (status= my_sigwait(&mask, &signo)) + { + log_error("sigwait() failed"); + goto err; + } + switch (signo) { case THR_SERVER_ALARM: diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc index 4276062caf2..317970d2047 100644 --- a/server-tools/instance-manager/parse_output.cc +++ b/server-tools/instance-manager/parse_output.cc @@ -36,6 +36,10 @@ DESCRIPTION Parse output of the "command". Find the "word" and return the next one + + RETURN + 0 - ok + 1 - error occured */ int parse_output_and_get_value(const char *command, const char *word, @@ -49,7 +53,8 @@ int parse_output_and_get_value(const char *command, const char *word, wordlen= strlen(word); - output= popen(command, "r"); + if ((output= popen(command, "r")) == NULL) + goto err; /* We want fully buffered stream. We also want system to @@ -69,15 +74,18 @@ int parse_output_and_get_value(const char *command, const char *word, these are '/', '-' and '.' in the path expressions and filenames) */ get_word((const char **) &linep, &lineword_len, NONSPACE); - if (!strncmp(word, linep, wordlen) && *result != '\0') + if (!strncmp(word, linep, wordlen)) { /* If we have found the word, return the next one. This is usually an option value. */ + linep+= lineword_len; /* swallow the previous one */ get_word((const char **) &linep, &lineword_len, NONSPACE); - DBUG_ASSERT(result_len > lineword_len); + if (result_len <= lineword_len) + goto err; strncpy(result, linep, lineword_len); + result[lineword_len]= '\0'; goto pclose; } } @@ -87,4 +95,7 @@ pclose: return 1; return 0; + +err: + return 1; } From 59d8e51150faa3aeaf489c4454fd6780ad849239 Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Tue, 15 Feb 2005 04:38:33 +0300 Subject: [PATCH 09/14] some more cleanups and fixes --- server-tools/instance-manager/commands.cc | 2 +- server-tools/instance-manager/instance.cc | 5 +- server-tools/instance-manager/instance.h | 3 +- server-tools/instance-manager/instance_map.cc | 47 ++++++++++++++----- server-tools/instance-manager/instance_map.h | 2 +- .../instance-manager/instance_options.cc | 12 +++-- .../instance-manager/instance_options.h | 2 +- server-tools/instance-manager/manager.cc | 16 +++++-- server-tools/instance-manager/mysqlmanager.cc | 6 ++- server-tools/instance-manager/options.cc | 8 ++-- server-tools/instance-manager/options.h | 2 +- server-tools/instance-manager/parse_output.cc | 4 +- 12 files changed, 77 insertions(+), 32 deletions(-) diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 4667e76bfd6..4e88f358148 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -276,7 +276,7 @@ int Show_instance_options::do_command(struct st_net *net, goto err; } - if (instance->options.nonguarded == NULL) + if (instance->options.nonguarded != NULL) { position= 0; store_to_string(&send_buff, (char *) "nonguarded", &position); diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index e654612de3e..f2140ee7200 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -296,8 +296,9 @@ int Instance::init(const char *name_arg) } -int Instance::complete_initialization(Instance_map *instance_map_arg) +int Instance::complete_initialization(Instance_map *instance_map_arg, + const char *mysqld_path) { instance_map= instance_map_arg; - return 0; + return options.complete_initialization(mysqld_path); } diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index fbe24e17554..d2a3eb98410 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -34,7 +34,8 @@ public: ~Instance(); int init(const char *name); - int complete_initialization(Instance_map *instance_map_arg); + int complete_initialization(Instance_map *instance_map_arg, + const char *mysqld_path); bool is_running(); int start(); diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 5044bfb0f43..191677889e9 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -74,7 +74,7 @@ static void delete_instance(void *u) 1 - error occured */ -static int process_option(void * ctx, const char *group, const char *option) +static int process_option(void *ctx, const char *group, const char *option) { Instance_map *map= NULL; Instance *instance= NULL; @@ -178,18 +178,41 @@ Instance_map::find(const char *name, uint name_len) } -void Instance_map::complete_initialization() +int Instance_map::complete_initialization() { Instance *instance; uint i= 0; - while (i < hash.records) + if (hash.records == 0) /* no instances found */ { - instance= (Instance *) hash_element(&hash, i); - instance->complete_initialization(this); - instance->options.complete_initialization(mysqld_path); - i++; + if ((instance= new Instance) == 0) + goto err; + + if (instance->init("mysqld") || add_instance(instance)) + goto err_instance; + + /* + After an instance have been added to the instance_map, + hash_free should handle it's deletion. + */ + if (instance->complete_initialization(this, mysqld_path)) + goto err; } + else + while (i < hash.records) + { + instance= (Instance *) hash_element(&hash, i); + if (instance->complete_initialization(this, mysqld_path)) + goto err; + i++; + } + + return 0; +err: + return 1; +err_instance: + delete instance; + return 1; } @@ -197,13 +220,11 @@ void Instance_map::complete_initialization() int Instance_map::load() { - int error; + if (process_default_option_files("my", process_option, (void *) this) || + complete_initialization()) + return 1; - error= process_default_option_files("my", process_option, (void *) this); - - complete_initialization(); - - return error; + return 0; } diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index b8b5ad0a890..60fde43ce12 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -72,7 +72,7 @@ public: /* adds instance to internal hash */ int add_instance(Instance *instance); /* inits instances argv's after all options have been loaded */ - void complete_initialization(); + int complete_initialization(); public: const char *mysqld_path; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 7c41a556cc4..6b795934d08 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -74,13 +74,17 @@ err: } -void Instance_options::get_pid_filename(char *result) +int Instance_options::get_pid_filename(char *result) { const char *pid_file= mysqld_pid_file; char datadir[MAX_PATH_LEN]; if (mysqld_datadir == NULL) - get_default_option(datadir, sizeof(datadir), "--datadir"); + { + /* we might get an error here if we have wrong path to the mysqld binary */ + if (get_default_option(datadir, sizeof(datadir), "--datadir")) + return 1; + } else strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1, "/", NullS); @@ -90,6 +94,7 @@ void Instance_options::get_pid_filename(char *result) /* get the full path to the pidfile */ my_load_path(result, pid_file, datadir); + return 0; } @@ -145,7 +150,8 @@ int Instance_options::complete_initialization(const char *default_path) add_option(pidfilename); } - get_pid_filename(pid_file_with_path); + if (get_pid_filename(pid_file_with_path)) + goto err; /* we need to reserve space for the final zero + possible default options */ if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1 diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index f6d4a6d93c6..888658969f2 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -48,7 +48,7 @@ public: int add_option(const char* option); int init(const char *instance_name_arg); pid_t get_pid(); - void get_pid_filename(char *result); + int get_pid_filename(char *result); int unlink_pidfile(); void print_argv(); diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 87ad938615f..cf7820d0335 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -77,8 +77,18 @@ void manager(const Options &options) instance_map.guardian= &guardian_thread; - if (instance_map.init() || user_map.init() || instance_map.load() || - user_map.load(options.password_file_name)) + if (instance_map.init() || user_map.init()) + return; + + if (instance_map.load()) + { + log_error("Cannot init instances repository. This might be caused by " + "the wrong config file options. For instance, missing mysqld " + "binary. Aborting."); + return; + } + + if (user_map.load(options.password_file_name)) return; /* write pid file */ @@ -173,7 +183,7 @@ void manager(const Options &options) { int status= 0; - if (status= my_sigwait(&mask, &signo)) + if ((status= my_sigwait(&mask, &signo)) != 0) { log_error("sigwait() failed"); goto err; diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index e37dab911c1..e1d267b492e 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -74,7 +74,8 @@ int main(int argc, char *argv[]) Options options; struct passwd *user_info; - options.load(argc, argv); + if (options.load(argc, argv)) + goto err; if ((user_info= check_user(options.user))) { @@ -96,6 +97,9 @@ int main(int argc, char *argv[]) options.cleanup(); my_end(0); return 0; +err: + my_end(0); + return 1; } /******************* Auxilary functions implementation **********************/ diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 18d98a3d9a6..b136ae3c386 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -207,14 +207,16 @@ C_MODE_END May not return. */ -void Options::load(int argc, char **argv) +int Options::load(int argc, char **argv) { + int rc; /* 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); + if (rc= handle_options(&argc, &argv, my_long_options, get_one_option)) + return rc; Options::saved_argv= argv; + return 0; } void Options::cleanup() diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index bb6da51d1ca..1b8570dd7ba 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -41,7 +41,7 @@ struct Options static char **saved_argv; - static void load(int argc, char **argv); + static int load(int argc, char **argv); void cleanup(); }; diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc index 317970d2047..58e3fbf9fbd 100644 --- a/server-tools/instance-manager/parse_output.cc +++ b/server-tools/instance-manager/parse_output.cc @@ -91,8 +91,8 @@ int parse_output_and_get_value(const char *command, const char *word, } pclose: - if (pclose(output)) - return 1; + /* we are not interested in the termination status */ + pclose(output); return 0; From dbc0937dd058ccf7861aa62caba32b63328bd4f1 Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Fri, 18 Feb 2005 14:58:30 +0300 Subject: [PATCH 10/14] made IM to work with --defaults-file smoothly --- include/my_sys.h | 8 ++-- mysys/default.c | 42 ++----------------- server-tools/instance-manager/instance.cc | 5 ++- server-tools/instance-manager/instance.h | 2 +- server-tools/instance-manager/instance_map.cc | 31 +++++++++++--- server-tools/instance-manager/instance_map.h | 4 +- .../instance-manager/instance_options.cc | 19 +++++++-- .../instance-manager/instance_options.h | 2 +- server-tools/instance-manager/manager.cc | 2 +- server-tools/instance-manager/options.cc | 11 ++++- server-tools/instance-manager/options.h | 2 + support-files/Makefile.am | 1 - support-files/my.cnf | 2 - 13 files changed, 70 insertions(+), 61 deletions(-) delete mode 100644 support-files/my.cnf diff --git a/include/my_sys.h b/include/my_sys.h index 72d4bec74b1..7d3b402bc6f 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -531,7 +531,7 @@ typedef uint32 ha_checksum; /* Define the type of function to be passed to process_default_option_files */ typedef int (*Process_option_func)(void *ctx, const char *group_name, - const char *option); + const char *option); #include @@ -776,9 +776,9 @@ extern void get_defaults_files(int argc, char **argv, char **defaults, char **extra_defaults); extern int load_defaults(const char *conf_file, const char **groups, int *argc, char ***argv); -extern int process_default_option_files(const char *conf_file, - Process_option_func func, - void *func_ctx); +extern int my_search_option_files(const char *conf_file, int *argc, + char ***argv, uint *args_used, + Process_option_func func, void *func_ctx); extern void free_defaults(char **argv); extern void print_defaults(const char *conf_file, const char **groups); extern my_bool my_compress(byte *, ulong *, ulong *); diff --git a/mysys/default.c b/mysys/default.c index 73dca3b6c2f..1c06feec634 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -83,7 +83,7 @@ static char *remove_end_comment(char *ptr); Process config files in default directories. SYNOPSIS - search_files() + my_search_option_files() conf_file Basename for configuration file to search for. If this is a path, then only this file is read. argc Pointer to argc of original program @@ -103,13 +103,13 @@ static char *remove_end_comment(char *ptr); 1 given cinf_file doesn't exist */ -static int search_files(const char *conf_file, int *argc, char ***argv, +int my_search_option_files(const char *conf_file, int *argc, char ***argv, uint *args_used, Process_option_func func, void *func_ctx) { const char **dirs, *forced_default_file; int error= 0; - DBUG_ENTER("search_files"); + DBUG_ENTER("my_search_option_files"); /* Check if we want to force the use a specific default file */ get_defaults_files(*argc, *argv, @@ -180,40 +180,6 @@ err: } -/* - Simplified version of search_files (no argv, argc to process). - - SYNOPSIS - process_default_option_files() - conf_file Basename for configuration file to search for. - If this is a path, then only this file is read. - func Pointer to the function to process options - func_ctx It's context. Usually it is the structure to - store additional options. - - DESCRIPTION - - Often we want only to get options from default config files. In this case we - don't want to provide any argc and argv parameters. This function is a - simplified variant of search_files which allows us to forget about - argc, argv. - - RETURN - 0 ok - 1 given cinf_file doesn't exist -*/ - -int process_default_option_files(const char *conf_file, - Process_option_func func, void *func_ctx) -{ - int argc= 1; - /* this is a dummy variable for search_files() */ - uint args_used; - - return search_files(conf_file, &argc, NULL, &args_used, func, func_ctx); -} - - /* The option handler for load_defaults. @@ -363,7 +329,7 @@ int load_defaults(const char *conf_file, const char **groups, ctx.args= &args; ctx.group= &group; - error= search_files(conf_file, argc, argv, &args_used, + error= my_search_option_files(conf_file, argc, argv, &args_used, handle_default_option, (void *) &ctx); /* Here error contains <> 0 only if we have a fully specified conf_file diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index f2140ee7200..165b06644f4 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -297,8 +297,9 @@ int Instance::init(const char *name_arg) int Instance::complete_initialization(Instance_map *instance_map_arg, - const char *mysqld_path) + const char *mysqld_path, + int only_instance) { instance_map= instance_map_arg; - return options.complete_initialization(mysqld_path); + return options.complete_initialization(mysqld_path, only_instance); } diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index d2a3eb98410..9f3334ba464 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -35,7 +35,7 @@ public: ~Instance(); int init(const char *name); int complete_initialization(Instance_map *instance_map_arg, - const char *mysqld_path); + const char *mysqld_path, int only_instance= 0); bool is_running(); int start(); diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 191677889e9..919f9c955a4 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -110,10 +110,10 @@ err_new_instance: C_MODE_END -Instance_map::Instance_map(const char *default_mysqld_path_arg) +Instance_map::Instance_map(const char *default_mysqld_path_arg, + const char *first_option_arg): +mysqld_path(default_mysqld_path_arg), first_option(first_option_arg) { - mysqld_path= default_mysqld_path_arg; - pthread_mutex_init(&LOCK_instance_map, 0); } @@ -193,9 +193,10 @@ int Instance_map::complete_initialization() /* After an instance have been added to the instance_map, - hash_free should handle it's deletion. + hash_free should handle it's deletion => goto err, not + err_instance. */ - if (instance->complete_initialization(this, mysqld_path)) + if (instance->complete_initialization(this, mysqld_path, 1)) goto err; } else @@ -220,7 +221,25 @@ err_instance: int Instance_map::load() { - if (process_default_option_files("my", process_option, (void *) this) || + int argc= 1; + /* this is a dummy variable for search_option_files() */ + uint args_used= 0; + const char *argv_options[3]; + char **argv= (char **) &argv_options; + + /* the name of the program may be orbitrary here in fact */ + argv_options[0]= "mysqlmanager"; + if (first_option != NULL) + { + argc= 2; + argv_options[1]= first_option; + argv_options[2]= '\0'; + } + else + argv_options[1]= '\0'; + + if (my_search_option_files("my", &argc, (char ***) &argv, &args_used, + process_option, (void *) this) || complete_initialization()) return 1; diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 60fde43ce12..98d98b5c516 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -64,7 +64,8 @@ public: int unlock(); int init(); - Instance_map(const char *default_mysqld_path_arg); + Instance_map(const char *default_mysqld_path_arg, + const char *first_option_arg); ~Instance_map(); /* loads options from config files */ @@ -79,6 +80,7 @@ public: Guardian_thread *guardian; private: + const char *first_option; enum { START_HASH_SIZE = 16 }; pthread_mutex_t LOCK_instance_map; HASH hash; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 6b795934d08..4242b8b6454 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -123,7 +123,8 @@ pid_t Instance_options::get_pid() } -int Instance_options::complete_initialization(const char *default_path) +int Instance_options::complete_initialization(const char *default_path, + int only_instance) { const char *tmp; @@ -140,11 +141,23 @@ int Instance_options::complete_initialization(const char *default_path) { char pidfilename[MAX_PATH_LEN]; char hostname[MAX_PATH_LEN]; + + /* + If we created only one istance [mysqld], because no config. files were + found, we would like to model mysqld pid file values. + */ if (!gethostname(hostname, sizeof(hostname) - 1)) - strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname, "-", - instance_name, ".pid", NullS); + (only_instance == 0) ? + strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name, "-", + hostname, ".pid", NullS): + strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname, + ".pid", NullS); + else + (only_instance == 0) ? strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name, + ".pid", NullS): + strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", "mysql", ".pid", NullS); add_option(pidfilename); diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 888658969f2..5e91ee80f00 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -43,7 +43,7 @@ public: {} ~Instance_options(); /* fills in argv */ - int complete_initialization(const char *default_path); + int complete_initialization(const char *default_path, int only_instance); int add_option(const char* option); int init(const char *instance_name_arg); diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index cf7820d0335..89d49ba9219 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -67,7 +67,7 @@ void manager(const Options &options) */ User_map user_map; - Instance_map instance_map(options.default_mysqld_path); + Instance_map instance_map(options.default_mysqld_path, options.first_option); Guardian_thread guardian_thread(thread_registry, &instance_map, options.monitoring_interval); diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index b136ae3c386..4a9745451a1 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -37,6 +37,7 @@ 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::first_option= 0; /* No default value */ const char *Options::bind_address= 0; /* No default value */ const char *Options::user= 0; /* No default value */ uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL; @@ -210,10 +211,18 @@ C_MODE_END int Options::load(int argc, char **argv) { int rc; + + if (argc >= 2) + { + if (is_prefix(argv[1],"--defaults-file=") || + is_prefix(argv[1],"--defaults-extra-file=")) + Options::first_option= argv[1]; + } + /* config-file options are prepended to command-line ones */ load_defaults("my", default_groups, &argc, &argv); - if (rc= handle_options(&argc, &argv, my_long_options, get_one_option)) + if ((rc= handle_options(&argc, &argv, my_long_options, get_one_option)) != 0) return rc; Options::saved_argv= argv; return 0; diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index 1b8570dd7ba..3df259864be 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -35,6 +35,8 @@ struct Options static const char *password_file_name; static const char *default_mysqld_path; static const char *user; + /* the option which should be passed to process_default_option_files */ + static const char *first_option; static uint monitoring_interval; static uint port_number; static const char *bind_address; diff --git a/support-files/Makefile.am b/support-files/Makefile.am index 4a9a9c60f30..0a6077f0efc 100644 --- a/support-files/Makefile.am +++ b/support-files/Makefile.am @@ -19,7 +19,6 @@ EXTRA_DIST = mysql.spec.sh \ my-small.cnf.sh \ - my.cnf \ my-medium.cnf.sh \ my-large.cnf.sh \ my-huge.cnf.sh \ diff --git a/support-files/my.cnf b/support-files/my.cnf deleted file mode 100644 index 062c78be46a..00000000000 --- a/support-files/my.cnf +++ /dev/null @@ -1,2 +0,0 @@ -[mysqld] -port=3307 From 27e614dc7f25cdc5739c8145d397cb39c8a5c333 Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Fri, 18 Feb 2005 15:23:55 +0300 Subject: [PATCH 11/14] new variant of passwd() by S. Vojtovich --- server-tools/instance-manager/Makefile.am | 1 + server-tools/instance-manager/options.cc | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index aab7db968fd..86effe3b9f0 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -36,6 +36,7 @@ liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc +liboptions_a_LIBADD= $(top_builddir)/libmysql/get_password.$(OBJEXT) # MySQL sometimes uses symlinks to reuse code # All symlinked files are grouped in libnet.a diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 4a9745451a1..9e4205b1407 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -151,27 +151,31 @@ static void usage() static void passwd() { - char user[1024], pw[1024], *p; + char user[1024], *p; + const char *pw1, *pw2; + char pw1msg[]= "Enter password: "; + char pw2msg[]= "Re-type password: "; char crypted_pw[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1]; fprintf(stderr, "Creating record for new user.\n"); fprintf(stderr, "Enter user name: "); - if (!fgets(user, sizeof(user), stdin)) + if (! fgets(user, sizeof(user), stdin)) { fprintf(stderr, "Unable to read user.\n"); return; } if ((p= strchr(user, '\n'))) *p= 0; - fprintf(stderr, "Enter password: "); - if (! fgets(pw, sizeof(pw), stdin)) + pw1= get_tty_password(pw1msg); + pw2= get_tty_password(pw2msg); + + if (strcmp(pw1, pw2)) { - fprintf(stderr, "Unable to read password.\n"); + fprintf(stderr, "Sorry, passwords do not match.\n"); return; } - if ((p= strchr(pw, '\n'))) *p= 0; - make_scrambled_password(crypted_pw, pw); + make_scrambled_password(crypted_pw, pw1); printf("%s:%s\n", user, crypted_pw); } From a007751b03ca28b8c43e93829c3a500f6bdbafad Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Sat, 19 Feb 2005 02:25:40 +0300 Subject: [PATCH 12/14] revert my changes in the spec.file --- support-files/mysql.spec.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index a4cd156a0a4..a7ee5fa8a07 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -417,9 +417,8 @@ install -s -m755 $MBD/sql/mysqld-max $RBR%{_sbindir}/mysqld-max install -m644 $MBD/sql/mysqld-max.sym $RBR%{_libdir}/mysql/mysqld-max.sym install -m644 $MBD/sql/mysqld.sym $RBR%{_libdir}/mysql/mysqld.sym -# Install logrotate, autostart and config file +# Install logrotate and autostart install -m644 $MBD/support-files/mysql-log-rotate $RBR%{_sysconfdir}/logrotate.d/mysql -install -m644 $MBD/support-files/my.cnf $RBR%{_sysconfdir}/my.cnf install -m755 $MBD/support-files/mysql.server $RBR%{_sysconfdir}/init.d/mysql # Create a symlink "rcmysql", pointing to the init.script. SuSE users @@ -430,6 +429,10 @@ ln -s %{_sysconfdir}/init.d/mysql $RPM_BUILD_ROOT%{_sbindir}/rcmysql # (safe_mysqld will be gone in MySQL 4.1) ln -sf ./mysqld_safe $RBR%{_bindir}/safe_mysqld +# Touch the place where the my.cnf config file might be located +# Just to make sure it's in the file list and marked as a config file +touch $RBR%{_sysconfdir}/my.cnf + %pre server # Shut down a previously installed server first if test -x %{_sysconfdir}/init.d/mysql @@ -547,6 +550,8 @@ fi %doc %attr(644, root, man) %{_mandir}/man1/perror.1* %doc %attr(644, root, man) %{_mandir}/man1/replace.1* +%ghost %config(noreplace,missingok) %{_sysconfdir}/my.cnf + %attr(755, root, root) %{_bindir}/my_print_defaults %attr(755, root, root) %{_bindir}/myisamchk %attr(755, root, root) %{_bindir}/myisam_ftdump @@ -574,12 +579,10 @@ fi %attr(755, root, root) %{_bindir}/safe_mysqld %attr(755, root, root) %{_sbindir}/mysqld -%attr(755, root, root) %{_sbindir}/mysqlmanager %attr(755, root, root) %{_sbindir}/rcmysql %attr(644, root, root) %{_libdir}/mysql/mysqld.sym %attr(644, root, root) %config(noreplace,missingok) %{_sysconfdir}/logrotate.d/mysql -%attr(644, root, root) %config(noreplace,missingok) %{_sysconfdir}/my.cnf %attr(755, root, root) %{_sysconfdir}/init.d/mysql %attr(755, root, root) %{_datadir}/mysql/ From 912dfdfa3421fc232d1863a6b0c9a207590b4b78 Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Sun, 27 Feb 2005 18:41:34 +0300 Subject: [PATCH 13/14] post-review fixes + cleanup + some minor fixes --- server-tools/instance-manager/buffer.cc | 4 +- server-tools/instance-manager/buffer.h | 2 +- server-tools/instance-manager/commands.cc | 19 ++- server-tools/instance-manager/guardian.cc | 117 ++++++++++-------- server-tools/instance-manager/guardian.h | 32 ++++- server-tools/instance-manager/instance.cc | 46 +++++-- server-tools/instance-manager/instance.h | 6 +- server-tools/instance-manager/instance_map.cc | 6 + server-tools/instance-manager/instance_map.h | 1 - .../instance-manager/instance_options.cc | 14 ++- .../instance-manager/instance_options.h | 11 +- server-tools/instance-manager/listener.cc | 7 ++ server-tools/instance-manager/manager.cc | 8 ++ server-tools/instance-manager/parse_output.cc | 2 +- server-tools/instance-manager/priv.cc | 9 ++ server-tools/instance-manager/priv.h | 12 ++ 16 files changed, 206 insertions(+), 90 deletions(-) diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index aaed161838d..b000a48d5ae 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -79,13 +79,13 @@ int Buffer::reserve(uint position, uint len_arg) if (position + len_arg >= MAX_BUFFER_SIZE) goto err; - if (position + len_arg>= buffer_size) + if (position + len_arg >= buffer_size) { buffer= (char *) my_realloc(buffer, min(MAX_BUFFER_SIZE, max((uint) (buffer_size*1.5), position + len_arg)), MYF(0)); - if (buffer == NULL) + if (!(buffer)) goto err; buffer_size= (uint) (buffer_size*1.5); } diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h index 85dd210cac2..a94047b11a5 100644 --- a/server-tools/instance-manager/buffer.h +++ b/server-tools/instance-manager/buffer.h @@ -41,7 +41,7 @@ private: int error; public: Buffer(size_t buffer_size_arg= BUFFER_INITIAL_SIZE) - :buffer_size(BUFFER_INITIAL_SIZE), error(0) + :buffer_size(buffer_size_arg), error(0) { /* As append() will invokes realloc() anyway, it's ok if malloc returns 0 diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 4e88f358148..9831ad3a891 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -170,7 +170,7 @@ int Show_instance_status::do_command(struct st_net *net, Instance *instance; store_to_string(&send_buff, (char *) instance_name, &position); - if ((instance= instance_map->find(instance_name, strlen(instance_name))) == NULL) + if (!(instance= instance_map->find(instance_name, strlen(instance_name)))) goto err; if (instance->is_running()) { @@ -201,7 +201,7 @@ err: int Show_instance_status::execute(struct st_net *net, ulong connection_id) { - if (instance_name != NULL) + if ((instance_name)) { if (do_command(net, instance_name)) return ER_OUT_OF_RESOURCES; @@ -257,14 +257,13 @@ int Show_instance_options::do_command(struct st_net *net, { Instance *instance; - if ((instance= instance_map-> - find(instance_name, strlen(instance_name))) == NULL) + if (!(instance= instance_map->find(instance_name, strlen(instance_name)))) goto err; store_to_string(&send_buff, (char *) "instance_name", &position); store_to_string(&send_buff, (char *) instance_name, &position); if (my_net_write(net, send_buff.buffer, (uint) position)) goto err; - if (instance->options.mysqld_path != NULL) + if ((instance->options.mysqld_path)) { position= 0; store_to_string(&send_buff, (char *) "mysqld-path", &position); @@ -276,7 +275,7 @@ int Show_instance_options::do_command(struct st_net *net, goto err; } - if (instance->options.nonguarded != NULL) + if ((instance->options.nonguarded)) { position= 0; store_to_string(&send_buff, (char *) "nonguarded", &position); @@ -317,7 +316,7 @@ err: int Show_instance_options::execute(struct st_net *net, ulong connection_id) { - if (instance_name != NULL) + if ((instance_name)) { if (do_command(net, instance_name)) return ER_OUT_OF_RESOURCES; @@ -351,10 +350,10 @@ int Start_instance::execute(struct st_net *net, ulong connection_id) } else { - if (err_code= instance->start()) + if ((err_code= instance->start())) return err_code; - if (instance->options.nonguarded == NULL) + if (!(instance->options.nonguarded)) instance_map->guardian->guard(instance); net_send_ok(net, connection_id); @@ -385,7 +384,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id) } else { - if (instance->options.nonguarded == NULL) + if (!(instance->options.nonguarded)) instance_map->guardian-> stop_guard(instance); if ((err_code= instance->stop())) diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index 750c3495870..4fbfaf2672d 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -29,23 +29,6 @@ #include -/* - The Guardian list node structure. Guardian utilizes it to store - guarded instances plus some additional info. -*/ - -struct GUARD_NODE -{ - Instance *instance; - /* state of an instance (i.e. STARTED, CRASHED, etc.) */ - int state; - /* the amount of attemts to restart instance (cleaned up at success) */ - int restart_counter; - /* triggered at a crash */ - time_t crash_moment; - /* General time field. Used to provide timeouts (at shutdown and restart) */ - time_t last_checked; -}; C_MODE_START @@ -99,9 +82,9 @@ void Guardian_thread::request_shutdown(bool stop_instances_arg) void Guardian_thread::process_instance(Instance *instance, GUARD_NODE *current_node, LIST **guarded_instances, - LIST *elem) + LIST *node) { - int waitchild= Instance::DEFAULT_SHUTDOWN_DELAY; + uint waitchild= (uint) Instance::DEFAULT_SHUTDOWN_DELAY; /* The amount of times, Guardian attempts to restart an instance */ int restart_retry= 100; time_t current_time= time(NULL); @@ -109,21 +92,21 @@ void Guardian_thread::process_instance(Instance *instance, if (current_node->state == STOPPING) { /* this brach is executed during shutdown */ - if (instance->options.shutdown_delay != NULL) - waitchild= atoi(instance->options.shutdown_delay); + if (instance->options.shutdown_delay_val) + waitchild= instance->options.shutdown_delay_val; - /* this returns true if and only if an instance was stopped for shure */ + /* this returns true if and only if an instance was stopped for sure */ if (instance->is_crashed()) - *guarded_instances= list_delete(*guarded_instances, elem); - else if (current_time - current_node->last_checked > waitchild) - { - instance->kill_instance(SIGKILL); - /* - Later we do elem= elem->next. This is ok, as we are only removing - the node from the list. The pointer to the next one is still valid. - */ - *guarded_instances= list_delete(*guarded_instances, elem); - } + *guarded_instances= list_delete(*guarded_instances, node); + else if ( (uint) (current_time - current_node->last_checked) > waitchild) + { + instance->kill_instance(SIGKILL); + /* + Later we do node= node->next. This is ok, as we are only removing + the node from the list. The pointer to the next one is still valid. + */ + *guarded_instances= list_delete(*guarded_instances, node); + } return; } @@ -174,11 +157,12 @@ void Guardian_thread::process_instance(Instance *instance, { instance->start(); current_node->last_checked= current_time; - ((GUARD_NODE *) elem->data)->restart_counter++; - log_info("guardian: starting instance %s", + current_node->restart_counter++; + log_info("guardian: restarting instance %s", instance->options.instance_name); } - else current_node->state= CRASHED_AND_ABANDONED; + else + current_node->state= CRASHED_AND_ABANDONED; } break; case CRASHED_AND_ABANDONED: @@ -205,7 +189,7 @@ void Guardian_thread::process_instance(Instance *instance, void Guardian_thread::run() { Instance *instance; - LIST *elem; + LIST *node; struct timespec timeout; thread_registry.register_thread(&thread_info); @@ -216,23 +200,23 @@ void Guardian_thread::run() /* loop, until all instances were shut down at the end */ while (!(shutdown_requested && (guarded_instances == NULL))) { - elem= guarded_instances; + node= guarded_instances; - while (elem != NULL) + while (node != NULL) { struct timespec timeout; - GUARD_NODE *current_node= (GUARD_NODE *) elem->data; - instance= ((GUARD_NODE *) elem->data)->instance; - process_instance(instance, current_node, &guarded_instances, elem); + GUARD_NODE *current_node= (GUARD_NODE *) node->data; + instance= ((GUARD_NODE *) node->data)->instance; + process_instance(instance, current_node, &guarded_instances, node); - elem= elem->next; + node= node->next; } timeout.tv_sec= time(NULL) + monitoring_interval; timeout.tv_nsec= 0; /* check the loop predicate before sleeping */ - if (!(shutdown_requested && (guarded_instances == NULL))) + if (!(shutdown_requested && (!(guarded_instances)))) pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout); } @@ -262,6 +246,8 @@ int Guardian_thread::is_stopped() SYNOPSYS Guardian_thread::init() + NOTE: One should always lock guardian before calling this routine. + RETURN 0 - ok 1 - error occured @@ -273,14 +259,22 @@ int Guardian_thread::init() Instance_map::Iterator iterator(instance_map); instance_map->lock(); + /* clear the list of guarded instances */ + free_root(&alloc, MYF(0)); + init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); + guarded_instances= NULL; + while ((instance= iterator.next())) { - if ((instance->options.nonguarded == NULL)) - if (guard(instance)) + if (!(instance->options.nonguarded)) + if (guard(instance, TRUE)) /* do not lock guardian */ + { + instance_map->unlock(); return 1; + } } - instance_map->unlock(); + instance_map->unlock(); return 0; } @@ -291,6 +285,8 @@ int Guardian_thread::init() SYNOPSYS guard() instance the instance to be guarded + nolock whether we prefer do not lock Guardian here, + but use external locking instead DESCRIPTION @@ -302,7 +298,7 @@ int Guardian_thread::init() 1 - error occured */ -int Guardian_thread::guard(Instance *instance) +int Guardian_thread::guard(Instance *instance, bool nolock) { LIST *node; GUARD_NODE *content; @@ -310,7 +306,7 @@ int Guardian_thread::guard(Instance *instance) node= (LIST *) alloc_root(&alloc, sizeof(LIST)); content= (GUARD_NODE *) alloc_root(&alloc, sizeof(GUARD_NODE)); - if ((node == NULL) || (content == NULL)) + if ((!(node)) || (!(content))) return 1; /* we store the pointers to instances from the instance_map's MEM_ROOT */ content->instance= instance; @@ -319,16 +315,21 @@ int Guardian_thread::guard(Instance *instance) content->state= NOT_STARTED; node->data= (void *) content; - pthread_mutex_lock(&LOCK_guardian); - guarded_instances= list_add(guarded_instances, node); - pthread_mutex_unlock(&LOCK_guardian); + if (nolock) + guarded_instances= list_add(guarded_instances, node); + else + { + pthread_mutex_lock(&LOCK_guardian); + guarded_instances= list_add(guarded_instances, node); + pthread_mutex_unlock(&LOCK_guardian); + } return 0; } /* - TODO: perhaps it would make sense to create a pool of the LIST elements + TODO: perhaps it would make sense to create a pool of the LIST nodeents 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). @@ -419,3 +420,15 @@ int Guardian_thread::stop_instances(bool stop_instances_arg) } return 0; } + + +int Guardian_thread::lock() +{ + return pthread_mutex_lock(&LOCK_guardian); +} + + +int Guardian_thread::unlock() +{ + return pthread_mutex_unlock(&LOCK_guardian); +} diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index 0aec00099de..e43b5d565d7 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -60,22 +60,47 @@ struct Guardian_thread_args class Guardian_thread: public Guardian_thread_args { public: + /* states of an instance */ + enum INSTANCE_STATE { NOT_STARTED= 1, STARTING, STARTED, JUST_CRASHED, + CRASHED, CRASHED_AND_ABANDONED, STOPPING }; + + /* + The Guardian list node structure. Guardian utilizes it to store + guarded instances plus some additional info. + */ + + struct GUARD_NODE + { + Instance *instance; + /* state of an instance (i.e. STARTED, CRASHED, etc.) */ + INSTANCE_STATE state; + /* the amount of attemts to restart instance (cleaned up at success) */ + int restart_counter; + /* triggered at a crash */ + time_t crash_moment; + /* General time field. Used to provide timeouts (at shutdown and restart) */ + time_t last_checked; + }; + + Guardian_thread(Thread_registry &thread_registry_arg, Instance_map *instance_map_arg, uint monitoring_interval_arg); ~Guardian_thread(); /* Main funtion of the thread */ void run(); - /* Initialize list of guarded instances */ + /* Initialize or refresh the list of guarded instances */ int init(); /* Request guardian shutdown. Stop instances if needed */ void request_shutdown(bool stop_instances); /* Start instance protection */ - int guard(Instance *instance); + int guard(Instance *instance, bool nolock= FALSE); /* Stop instance protection */ int stop_guard(Instance *instance); /* Returns true if guardian thread is stopped */ int is_stopped(); + int lock(); + int unlock(); public: pthread_cond_t COND_guardian; @@ -89,9 +114,6 @@ private: int stopped; private: - /* states of an instance */ - enum { NOT_STARTED= 1, STARTING, STARTED, JUST_CRASHED, CRASHED, - CRASHED_AND_ABANDONED, STOPPING }; pthread_mutex_t LOCK_guardian; Thread_info thread_info; LIST *guarded_instances; diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 165b06644f4..97282f3f800 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -22,6 +22,8 @@ #include "mysql_manager_error.h" #include "log.h" #include "instance_map.h" +#include "priv.h" + #include #include #include @@ -30,6 +32,13 @@ C_MODE_START +/* + Proxy thread is a simple way to avoid all pitfalls of the threads + implementation in the OS (e.g. LinuxThreads). With such a thread we + don't have to process SIGCHLD, which is a tricky business if we want + to do it in a portable way. +*/ + pthread_handler_decl(proxy, arg) { Instance *instance= (Instance *) arg; @@ -111,7 +120,26 @@ void Instance::fork_and_monitor() log_info("cannot fork() to start instance %s", options.instance_name); return; default: - wait(NULL); + /* + Here we wait for the child created. This process differs for systems + running LinuxThreads and POSIX Threads compliant systems. This is because + according to POSIX we could wait() for a child in any thread of the + process. While LinuxThreads require that wait() is called by the thread, + which created the child. + On the other hand we could not expect mysqld to return the pid, we + got in from fork(), to wait4() fucntion when running on LinuxThreads. + This is because MySQL shutdown thread is not the one, which was created + by our fork() call. + So basically we have two options: whether the wait() call returns only in + the creator thread, but we cannot use waitpid() since we have no idea + which pid we should wait for (in fact it should be the pid of shutdown + thread, but we don't know this one). Or we could use waitpid(), but + couldn't use wait(), because it could return in any wait() in the program. + */ + if (linuxthreads) + wait(NULL); /* LinuxThreads were detected */ + else + waitpid(pid, NULL, 0); /* set instance state to crashed */ pthread_mutex_lock(&LOCK_instance); crashed= 1; @@ -122,7 +150,7 @@ void Instance::fork_and_monitor() is needed if a user issued command to stop an instance via mysql connection. This is not the case if Guardian stop the thread. */ - pthread_cond_signal(&COND_instance_restarted); + pthread_cond_signal(&COND_instance_stopped); /* wake guardian */ pthread_cond_signal(&instance_map->guardian->COND_guardian); /* thread exits */ @@ -136,14 +164,14 @@ void Instance::fork_and_monitor() Instance::Instance(): crashed(0) { pthread_mutex_init(&LOCK_instance, 0); - pthread_cond_init(&COND_instance_restarted, 0); + pthread_cond_init(&COND_instance_stopped, 0); } Instance::~Instance() { + pthread_cond_destroy(&COND_instance_stopped); pthread_mutex_destroy(&LOCK_instance); - pthread_cond_destroy(&COND_instance_restarted); } @@ -168,7 +196,7 @@ bool Instance::is_running() bool return_val; if (options.mysqld_port) - port= atoi(strchr(options.mysqld_port, '=') + 1); + port= options.mysqld_port_val; if (options.mysqld_socket) socket= strchr(options.mysqld_socket, '=') + 1; @@ -226,10 +254,10 @@ int Instance::stop() { pid_t pid; struct timespec timeout; - int waitchild= DEFAULT_SHUTDOWN_DELAY; + uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY; - if (options.shutdown_delay != NULL) - waitchild= atoi(options.shutdown_delay); + if (options.shutdown_delay_val) + waitchild= options.shutdown_delay_val; kill_instance(SIGTERM); /* sleep on condition to wait for SIGCHLD */ @@ -243,7 +271,7 @@ int Instance::stop() { int status; - status= pthread_cond_timedwait(&COND_instance_restarted, + status= pthread_cond_timedwait(&COND_instance_stopped, &LOCK_instance, &timeout); if (status == ETIMEDOUT) diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 9f3334ba464..c141ff7b30d 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -57,7 +57,11 @@ private: */ int crashed; pthread_mutex_t LOCK_instance; - pthread_cond_t COND_instance_restarted; + /* + This condition variable is used to wake threads waiting for instance to + stop in Instance::stop() + */ + pthread_cond_t COND_instance_stopped; Instance_map *instance_map; }; diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 919f9c955a4..d19d42662e5 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -151,12 +151,15 @@ int Instance_map::flush_instances() { int rc; + guardian->lock(); 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); pthread_mutex_unlock(&LOCK_instance_map); rc= load(); + guardian->init(); + guardian->unlock(); return rc; } @@ -183,6 +186,7 @@ int Instance_map::complete_initialization() Instance *instance; uint i= 0; + if (hash.records == 0) /* no instances found */ { if ((instance= new Instance) == 0) @@ -191,6 +195,7 @@ int Instance_map::complete_initialization() if (instance->init("mysqld") || add_instance(instance)) goto err_instance; + /* After an instance have been added to the instance_map, hash_free should handle it's deletion => goto err, not @@ -227,6 +232,7 @@ int Instance_map::load() const char *argv_options[3]; char **argv= (char **) &argv_options; + /* the name of the program may be orbitrary here in fact */ argv_options[0]= "mysqlmanager"; if (first_option != NULL) diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 98d98b5c516..8d8ea33cf5d 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -57,7 +57,6 @@ public: public: /* returns a pointer to the instance or NULL, if there is no such instance */ Instance *find(const char *name, uint name_len); - Instance *find(uint instance_number); int flush_instances(); int lock(); diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 4242b8b6454..e1b81bc93b1 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -79,7 +79,7 @@ int Instance_options::get_pid_filename(char *result) const char *pid_file= mysqld_pid_file; char datadir[MAX_PATH_LEN]; - if (mysqld_datadir == NULL) + if (!(mysqld_datadir)) { /* we might get an error here if we have wrong path to the mysqld binary */ if (get_default_option(datadir, sizeof(datadir), "--datadir")) @@ -128,16 +128,22 @@ int Instance_options::complete_initialization(const char *default_path, { const char *tmp; - if (mysqld_path == NULL) + if (!(mysqld_path)) { if (!(mysqld_path= strdup_root(&alloc, default_path))) goto err; } + if (mysqld_port) + mysqld_port_val= atoi(strchr(mysqld_port, '=') + 1); + + if (shutdown_delay) + shutdown_delay_val= atoi(shutdown_delay); + if (!(tmp= strdup_root(&alloc, "--no-defaults"))) goto err; - if (mysqld_pid_file == NULL) + if (!(mysqld_pid_file)) { char pidfilename[MAX_PATH_LEN]; char hostname[MAX_PATH_LEN]; @@ -265,7 +271,7 @@ int Instance_options::add_to_argv(const char* option) { DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS); - if (option != NULL) + if ((option)) argv[filled_default_options++]= (char *) option; return 0; } diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 5e91ee80f00..06ad0156bc0 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -37,9 +37,10 @@ class Instance_options { public: Instance_options() : - mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0), - mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), nonguarded(0), - shutdown_delay(0), filled_default_options(0) + mysqld_socket(0), mysqld_datadir(0), + mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0), + mysqld_port_val(0), mysqld_path(0), nonguarded(0), shutdown_delay(0), + shutdown_delay_val(0), filled_default_options(0) {} ~Instance_options(); /* fills in argv */ @@ -68,11 +69,13 @@ public: const char *mysqld_bind_address; const char *mysqld_pid_file; const char *mysqld_port; - uint instance_name_len; + uint mysqld_port_val; const char *instance_name; + uint instance_name_len; const char *mysqld_path; const char *nonguarded; const char *shutdown_delay; + uint shutdown_delay_val; /* this value is computed and cashed here */ DYNAMIC_ARRAY options_array; private: diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 14970989e92..946bab11a2e 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -31,6 +31,7 @@ #include "instance_map.h" #include "log.h" #include "mysql_connection.h" +#include "priv.h" /* @@ -82,6 +83,12 @@ void Listener_thread::run() int arg= 1; /* value to be set by setsockopt */ int unix_socket; uint im_port; + /* we use this var to check whether we are running on LinuxThreads */ + pid_t thread_pid; + + thread_pid= getpid(); + /* set global variable */ + linuxthreads= (thread_pid != manager_pid); thread_registry.register_thread(&thread_info); diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 89d49ba9219..8629c5e9d9c 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -16,6 +16,7 @@ #include "manager.h" +#include "priv.h" #include "thread_registry.h" #include "listener.h" #include "instance_map.h" @@ -75,11 +76,13 @@ void manager(const Options &options) Listener_thread_args listener_args(thread_registry, options, user_map, instance_map); + manager_pid= getpid(); instance_map.guardian= &guardian_thread; if (instance_map.init() || user_map.init()) return; + if (instance_map.load()) { log_error("Cannot init instances repository. This might be caused by " @@ -170,7 +173,12 @@ void manager(const Options &options) */ init_thr_alarm(10); /* init list of guarded instances */ + guardian_thread.lock(); + guardian_thread.init(); + + guardian_thread.unlock(); + /* After the list of guarded instances have been initialized, Guardian should start them. diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc index 58e3fbf9fbd..6fee0797395 100644 --- a/server-tools/instance-manager/parse_output.cc +++ b/server-tools/instance-manager/parse_output.cc @@ -53,7 +53,7 @@ int parse_output_and_get_value(const char *command, const char *word, wordlen= strlen(word); - if ((output= popen(command, "r")) == NULL) + if (!(output= popen(command, "r"))) goto err; /* diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc index 4b47fe5b593..dd192370aaf 100644 --- a/server-tools/instance-manager/priv.cc +++ b/server-tools/instance-manager/priv.cc @@ -16,6 +16,15 @@ #include "priv.h" +/* the pid of the manager process (of the signal thread on the LinuxThreads) */ +pid_t manager_pid; + +/* + This flag is set if mysqlmanager has detected that it is running on the + system using LinuxThreads +*/ +bool linuxthreads; + /* The following string must be less then 80 characters, as mysql_connection.cc relies on it diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h index 8014df7260c..5bee8aa1463 100644 --- a/server-tools/instance-manager/priv.h +++ b/server-tools/instance-manager/priv.h @@ -16,6 +16,18 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include + + +/* the pid of the manager process (of the signal thread on the LinuxThreads) */ +extern pid_t manager_pid; + +/* + This flag is set if mysqlmanager has detected that it is running on the + system using LinuxThreads +*/ +extern bool linuxthreads; + extern const char mysqlmanager_version[]; extern const int mysqlmanager_version_length; From 8bed4a14e7db732da1658f144f42bb889d525e1e Mon Sep 17 00:00:00 2001 From: "petr@mysql.com" <> Date: Mon, 28 Feb 2005 16:05:18 +0300 Subject: [PATCH 14/14] portability fix --- server-tools/instance-manager/priv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h index 5bee8aa1463..decc3605dff 100644 --- a/server-tools/instance-manager/priv.h +++ b/server-tools/instance-manager/priv.h @@ -16,6 +16,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include