summaryrefslogtreecommitdiff
path: root/server-tools
diff options
context:
space:
mode:
Diffstat (limited to 'server-tools')
-rw-r--r--server-tools/instance-manager/commands.cc478
-rw-r--r--server-tools/instance-manager/commands.h86
-rw-r--r--server-tools/instance-manager/factory.cc45
-rw-r--r--server-tools/instance-manager/factory.h20
-rw-r--r--server-tools/instance-manager/instance_options.cc135
-rw-r--r--server-tools/instance-manager/instance_options.h8
-rw-r--r--server-tools/instance-manager/messages.cc9
-rw-r--r--server-tools/instance-manager/mysql_connection.cc4
-rw-r--r--server-tools/instance-manager/mysql_manager_error.h4
-rw-r--r--server-tools/instance-manager/parse.cc209
-rw-r--r--server-tools/instance-manager/parse.h20
-rw-r--r--server-tools/instance-manager/parse_output.cc3
-rw-r--r--server-tools/instance-manager/parse_output.h3
-rw-r--r--server-tools/instance-manager/protocol.cc53
-rw-r--r--server-tools/instance-manager/protocol.h6
15 files changed, 1042 insertions, 41 deletions
diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc
index e1f811ef57d..3b4c43d289c 100644
--- a/server-tools/instance-manager/commands.cc
+++ b/server-tools/instance-manager/commands.cc
@@ -27,6 +27,19 @@
#include <mysql.h>
+/* some useful functions */
+
+static int put_to_buff(Buffer *buff, const char *str, uint *position)
+{
+ uint len= strlen(str);
+ if (buff->append(*position, str, len))
+ return 1;
+
+ *position+= len;
+ return 0;
+}
+
+
/* implementation for Show_instances: */
@@ -106,7 +119,7 @@ int Flush_instances::execute(struct st_net *net, ulong connection_id)
if (instance_map->flush_instances())
return ER_OUT_OF_RESOURCES;
- net_send_ok(net, connection_id);
+ net_send_ok(net, connection_id, NULL);
return 0;
}
@@ -119,7 +132,7 @@ Show_instance_status::Show_instance_status(Instance_map *instance_map_arg,
{
Instance *instance;
- /* we make a search here, since we don't want t store the name */
+ /* we make a search here, since we don't want to store the name */
if ((instance= instance_map->find(name, len)))
{
instance_name= instance->options.instance_name;
@@ -225,7 +238,7 @@ Show_instance_options::Show_instance_options(Instance_map *instance_map_arg,
{
Instance *instance;
- /* we make a search here, since we don't want t store the name */
+ /* we make a search here, since we don't want to store the name */
if ((instance= instance_map->find(name, len)))
{
instance_name= instance->options.instance_name;
@@ -344,7 +357,7 @@ Start_instance::Start_instance(Instance_map *instance_map_arg,
const char *name, uint len)
:Command(instance_map_arg)
{
- /* we make a search here, since we don't want t store the name */
+ /* we make a search here, since we don't want to store the name */
if ((instance= instance_map->find(name, len)))
instance_name= instance->options.instance_name;
}
@@ -365,9 +378,460 @@ int Start_instance::execute(struct st_net *net, ulong connection_id)
if (!(instance->options.nonguarded))
instance_map->guardian->guard(instance);
- net_send_ok(net, connection_id);
+ net_send_ok(net, connection_id, "Instance started");
+ return 0;
+ }
+}
+
+
+/* implementation for Show_instance_log: */
+
+Show_instance_log::Show_instance_log(Instance_map *instance_map_arg,
+ const char *name, uint len,
+ Log_type log_type_arg,
+ const char *size_arg,
+ const char *offset_arg)
+ :Command(instance_map_arg)
+{
+ Instance *instance;
+
+ if (offset_arg != NULL)
+ offset= atoi(offset_arg);
+ else
+ offset= 0;
+ size= atoi(size_arg);
+ log_type= log_type_arg;
+
+ /* we make a search here, since we don't want to store the name */
+ if ((instance= instance_map->find(name, len)))
+ {
+ instance_name= instance->options.instance_name;
+ }
+ else
+ instance_name= NULL;
+}
+
+
+int Show_instance_log::do_command(struct st_net *net,
+ const char *instance_name)
+{
+ enum { MAX_VERSION_LENGTH= 40 };
+ Buffer send_buff; /* buffer for packets */
+ LIST name;
+ LIST *field_list;
+ NAME_WITH_LENGTH name_field;
+ uint position=0;
+
+ /* create list of the fileds to be passed to send_fields */
+ name_field.name= (char *) "Log";
+ name_field.length= 20;
+ name.data= &name_field;
+ field_list= list_add(NULL, &name);
+
+ /* cannot read negative number of bytes */
+ if (offset > size)
+ return ER_SYNTAX_ERROR;
+
+ send_fields(net, field_list);
+
+ {
+ Instance *instance;
+ const char *logpath;
+ File fd;
+
+ if ((instance= instance_map->find(instance_name, strlen(instance_name))) == NULL)
+ goto err;
+
+ switch (log_type)
+ {
+ case LOG_ERROR:
+ logpath= instance->options.error_log;
+ break;
+ case LOG_GENERAL:
+ logpath= instance->options.query_log;
+ break;
+ case LOG_SLOW:
+ logpath= instance->options.slow_log;
+ break;
+ default:
+ logpath= NULL;
+ }
+
+ /* Instance has no such log */
+ if (logpath == NULL)
+ {
+ return ER_NO_SUCH_LOG;
+ }
+ else if (*logpath == '\0')
+ {
+ return ER_GUESS_LOGFILE;
+ }
+
+
+ if ((fd= open(logpath, O_RDONLY)))
+ {
+ size_t buff_size;
+ int read_len;
+ /* calculate buffer size */
+ struct stat file_stat;
+
+ if(fstat(fd, &file_stat))
+ goto err;
+
+ buff_size= (size - offset);
+
+ /* read in one chunk */
+ read_len= my_seek(fd, file_stat.st_size - size, MY_SEEK_SET, MYF(0));
+
+ char *bf= (char *) malloc(sizeof(char)*buff_size);
+ read_len= my_read(fd, bf, buff_size, MYF(0));
+ store_to_string(&send_buff, (char *) bf, &position, read_len);
+ close(fd);
+ }
+ else
+ {
+ return ER_OPEN_LOGFILE;
+ }
+
+ if (my_net_write(net, send_buff.buffer, (uint) position))
+ goto err;
+ }
+
+ send_eof(net);
+ net_flush(net);
+
+ return 0;
+
+err:
+ return ER_OUT_OF_RESOURCES;
+}
+
+
+int Show_instance_log::execute(struct st_net *net, ulong connection_id)
+{
+ if (instance_name != NULL)
+ {
+ return do_command(net, instance_name);
+ }
+ else
+ {
+ return ER_BAD_INSTANCE_NAME;
+ }
+}
+
+
+
+/* implementation for Show_instance_log_files: */
+
+Show_instance_log_files::Show_instance_log_files
+ (Instance_map *instance_map_arg, const char *name, uint len)
+ :Command(instance_map_arg)
+{
+ Instance *instance;
+
+ /* we make a search here, since we don't want to store the name */
+ if ((instance= instance_map->find(name, len)))
+ {
+ instance_name= instance->options.instance_name;
+ }
+ else
+ instance_name= NULL;
+}
+
+
+/*
+ The method sends a table with a the list of the log files
+ used by the instance.
+
+ SYNOPSYS
+ Show_instance_log_files::do_command()
+ net The network connection to the client.
+ instance_name The name of the instance.
+
+ RETURN
+ 0 - ok
+ 1 - error occured
+*/
+
+
+int Show_instance_log_files::do_command(struct st_net *net,
+ const char *instance_name)
+{
+ enum { MAX_VERSION_LENGTH= 40 };
+ Buffer send_buff; /* buffer for packets */
+ LIST name, path, size;
+ LIST *field_list;
+ NAME_WITH_LENGTH name_field, path_field, size_field;
+ uint position=0;
+
+ /* create list of the fileds to be passed to send_fields */
+ name_field.name= (char *) "Logfile";
+ name_field.length= 20;
+ name.data= &name_field;
+ path_field.name= (char *) "Path";
+ path_field.length= 20;
+ path.data= &path_field;
+ size_field.name= (char *) "Filesize";
+ size_field.length= 20;
+ size.data= &size_field;
+ field_list= list_add(NULL, &size);
+ field_list= list_add(field_list, &path);
+ field_list= list_add(field_list, &name);
+
+ send_fields(net, field_list);
+
+ Instance *instance;
+
+ if ((instance= instance_map->
+ find(instance_name, strlen(instance_name))) == NULL)
+ goto err;
+ {
+ /*
+ We have alike structure in instance_options.cc. We use such to be able
+ to loop througt the options, which we need to handle in some common way.
+ */
+ struct log_files_st
+ {
+ const char *name;
+ const char *value;
+ } logs[]=
+ {
+ {"ERROR LOG", instance->options.error_log},
+ {"GENERAL LOG", instance->options.query_log},
+ {"SLOW LOG", instance->options.slow_log},
+ {NULL, NULL}
+ };
+ struct log_files_st *log_files;
+
+
+ instance->options.print_argv();
+ for (log_files= logs; log_files->name; log_files++)
+ {
+ if (log_files->value != NULL)
+ {
+ struct stat file_stat;
+ char buff[20];
+
+ position= 0;
+ /* store the type of the log in the send buffer */
+ store_to_string(&send_buff, log_files->name, &position);
+ switch (stat(log_files->value, &file_stat)) {
+ case 0:
+ if (S_ISREG(file_stat.st_mode))
+ {
+ store_to_string(&send_buff,
+ (char *) log_files->value,
+ &position);
+ int10_to_str(file_stat.st_size, buff, 10);
+ store_to_string(&send_buff, (char *) buff, &position);
+ break;
+ }
+ default:
+ store_to_string(&send_buff,
+ "",
+ &position);
+ store_to_string(&send_buff, (char *) "0", &position);
+ }
+ if (my_net_write(net, send_buff.buffer, (uint) position))
+ goto err;
+ }
+ }
+ }
+
+ send_eof(net);
+ net_flush(net);
+
+ return 0;
+
+err:
+ return 1;
+}
+int Show_instance_log_files::execute(struct st_net *net, ulong connection_id)
+{
+ if (instance_name != NULL)
+ {
+ if (do_command(net, instance_name))
+ return ER_OUT_OF_RESOURCES;
return 0;
}
+ else
+ {
+ return ER_BAD_INSTANCE_NAME;
+ }
+}
+
+
+/* implementation for SET nstance_name.option=option_value: */
+
+Set_option::Set_option(Instance_map *instance_map_arg,
+ const char *name, uint len,
+ const char *option_arg, uint option_len_arg,
+ const char *option_value_arg, uint option_value_len_arg)
+ :Command(instance_map_arg)
+{
+ Instance *instance;
+
+ /* we make a search here, since we don't want to store the name */
+ if ((instance= instance_map->find(name, len)))
+ {
+ instance_name= instance->options.instance_name;
+ /* add prefix for add_option */
+ if ((option_len_arg < MAX_OPTION_LEN - 1) ||
+ (option_value_len_arg < MAX_OPTION_LEN - 1))
+ {
+ strncpy(option, option_arg, option_len_arg);
+ option[option_len_arg]= 0;
+ strncpy(option_value, option_value_arg, option_value_len_arg);
+ option_value[option_value_len_arg]= 0;
+ }
+ else
+ {
+ option[0]= 0;
+ option_value[0]= 0;
+ }
+ instance_name_len= len;
+ }
+ else
+ {
+ instance_name= NULL;
+ instance_name_len= 0;
+ }
+}
+
+
+/*
+ Correct the file. skip option could be used in future if we don't want to
+ let user change the options file (E.g. he lacks permissions to do that)
+*/
+int Set_option::correct_file(bool skip)
+{
+ FILE *cnf_file;
+ const char *default_location="/etc/my.cnf";
+ char linebuff[4096], *ptr;
+ uint optlen;
+ Buffer file_buffer;
+ uint position= 0;
+ bool isfound= false;
+
+ optlen= strlen(option);
+
+ if (!(cnf_file= my_fopen(default_location, O_RDONLY, MYF(0))))
+ goto err_fopen;
+
+ while (fgets(linebuff, sizeof(linebuff), cnf_file))
+ {
+ /* if the section is found traverse it */
+ if (isfound)
+ {
+ /* skip the old value of the option we are changing */
+ if (strncmp(linebuff, option, optlen))
+ {
+ /* copy all other lines line */
+ put_to_buff(&file_buffer, linebuff, &position);
+ }
+ }
+ else
+ put_to_buff(&file_buffer, linebuff, &position);
+
+ /* looking for appropriate instance section */
+ for (ptr= linebuff ; my_isspace(&my_charset_latin1,*ptr) ; ptr++);
+ if (*ptr == '[')
+ {
+ /* copy the line to the buffer */
+ if (!strncmp(++ptr, instance_name, instance_name_len))
+ {
+ isfound= true;
+ /* add option */
+ if (!skip)
+ {
+ put_to_buff(&file_buffer, option, &position);
+ if (option_value[0] != 0)
+ {
+ put_to_buff(&file_buffer, "=", &position);
+ put_to_buff(&file_buffer, option_value, &position);
+ }
+ /* add a newline */
+ put_to_buff(&file_buffer, "\n", &position);
+ }
+ }
+ else
+ isfound= false; /* mark that this section is of no interest to us */
+ }
+
+ }
+
+ if (my_fclose(cnf_file, MYF(0)))
+ goto err;
+
+ /* we must hold an instance_map mutex while changing config file */
+ instance_map->lock();
+
+ if (!(cnf_file= my_fopen(default_location, O_WRONLY|O_TRUNC, MYF(0))))
+ goto err;
+ if (my_fwrite(cnf_file, file_buffer.buffer, position, MYF(MY_NABP)))
+ goto err;
+
+ if (my_fclose(cnf_file, MYF(0)))
+ goto err;
+
+ instance_map->unlock();
+
+ return 0;
+
+err:
+ my_fclose(cnf_file, MYF(0));
+ return ER_OUT_OF_RESOURCES;
+err_fopen:
+ return ER_ACCESS_OPTION_FILE;
+}
+
+
+/*
+ The method sets an option in the the default config file (/etc/my.cnf).
+
+ SYNOPSYS
+ Set_option::do_command()
+ net The network connection to the client.
+
+ RETURN
+ 0 - ok
+ 1 - error occured
+*/
+
+
+int Set_option::do_command(struct st_net *net)
+{
+ return correct_file(false);
+}
+
+
+int Set_option::execute(struct st_net *net, ulong connection_id)
+{
+ if (instance_name != NULL)
+ {
+ int val;
+
+ val= do_command(net);
+ if (val == 0)
+ {
+ net_send_ok(net, connection_id, NULL);
+ return 0;
+ }
+
+ return val;
+ }
+ else
+ {
+ return ER_BAD_INSTANCE_NAME;
+ }
+}
+
+
+/* the only function from Unset_option we need to Implement */
+
+int Unset_option::do_command(struct st_net *net)
+{
+ return correct_file(true);
}
@@ -377,7 +841,7 @@ Stop_instance::Stop_instance(Instance_map *instance_map_arg,
const char *name, uint len)
:Command(instance_map_arg)
{
- /* we make a search here, since we don't want t store the name */
+ /* we make a search here, since we don't want to store the name */
if ((instance= instance_map->find(name, len)))
instance_name= instance->options.instance_name;
}
@@ -398,7 +862,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id)
stop_guard(instance);
if ((err_code= instance->stop()))
return err_code;
- net_send_ok(net, connection_id);
+ net_send_ok(net, connection_id, NULL);
return 0;
}
}
diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h
index bab67f9c6b4..46cd0893759 100644
--- a/server-tools/instance-manager/commands.h
+++ b/server-tools/instance-manager/commands.h
@@ -18,6 +18,7 @@
#include "command.h"
#include "instance.h"
+#include "parse.h"
/*
Print all instances of this instance manager.
@@ -116,6 +117,45 @@ public:
/*
+ Print requested part of the log
+ Grammar:
+ SHOW <instance_name> log {ERROR | SLOW | GENERAL} size[, offset_from_end]
+*/
+
+class Show_instance_log : public Command
+{
+public:
+
+ Show_instance_log(Instance_map *instance_map_arg, const char *name,
+ uint len, Log_type log_type_arg, const char *size_arg,
+ const char *offset_arg);
+ int do_command(struct st_net *net, const char *instance_name);
+ int execute(struct st_net *net, ulong connection_id);
+ Log_type log_type;
+ const char *instance_name;
+ uint size;
+ uint offset;
+};
+
+
+/*
+ Shows the list of the log files, used by an instance.
+ Grammar: SHOW <instance_name> LOG FILES
+*/
+
+class Show_instance_log_files : public Command
+{
+public:
+
+ Show_instance_log_files(Instance_map *instance_map_arg, const char *name, uint len);
+ int do_command(struct st_net *net, const char *instance_name);
+ int execute(struct st_net *net, ulong connection_id);
+ const char *instance_name;
+ const char *option;
+};
+
+
+/*
Syntax error command. This command is issued if parser reported a syntax error.
We need it to distinguish the parse error and the situation when parser internal
error occured. E.g. parsing failed because we hadn't had enought memory. In the
@@ -128,4 +168,50 @@ public:
int execute(struct st_net *net, ulong connection_id);
};
+/*
+ Set an option for the instance.
+ Grammar: SET instance_name.option=option_value
+*/
+
+class Set_option : public Command
+{
+public:
+ Set_option(Instance_map *instance_map_arg, const char *name, uint len,
+ const char *option_arg, uint option_len,
+ const char *option_value_arg, uint option_value_len);
+ /*
+ the following function is virtual to let Unset_option to use
+ */
+ virtual int do_command(struct st_net *net);
+ int execute(struct st_net *net, ulong connection_id);
+protected:
+ int correct_file(bool skip);
+public:
+ const char *instance_name;
+ uint instance_name_len;
+ /* buffer for the option */
+ enum { MAX_OPTION_LEN= 1024 };
+ char option[MAX_OPTION_LEN];
+ char option_value[MAX_OPTION_LEN];
+};
+
+
+/*
+ Remove option of the instance from config file
+ Grammar: UNSET instance_name.option
+*/
+
+class Unset_option: public Set_option
+{
+public:
+ Unset_option(Instance_map *instance_map_arg, const char *name, uint len,
+ const char *option_arg, uint option_len,
+ const char *option_value_arg, uint option_value_len):
+ Set_option(instance_map_arg, name, len, option_arg, option_len,
+ option_value_arg, option_value_len)
+ {}
+ int do_command(struct st_net *net);
+};
+
+
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H */
diff --git a/server-tools/instance-manager/factory.cc b/server-tools/instance-manager/factory.cc
index 538d9353983..58ac32a9feb 100644
--- a/server-tools/instance-manager/factory.cc
+++ b/server-tools/instance-manager/factory.cc
@@ -16,40 +16,85 @@
#include "factory.h"
+
Show_instances *Command_factory::new_Show_instances()
{
return new Show_instances(&instance_map);
}
+
Flush_instances *Command_factory::new_Flush_instances()
{
return new Flush_instances(&instance_map);
}
+
Show_instance_status *Command_factory::
new_Show_instance_status(const char *name, uint len)
{
return new Show_instance_status(&instance_map, name, len);
}
+
Show_instance_options *Command_factory::
new_Show_instance_options(const char *name, uint len)
{
return new Show_instance_options(&instance_map, name, len);
}
+
Start_instance *Command_factory::
new_Start_instance(const char *name, uint len)
{
return new Start_instance(&instance_map, name, len);
}
+
Stop_instance *Command_factory::new_Stop_instance(const char *name, uint len)
{
return new Stop_instance(&instance_map, name, len);
}
+
Syntax_error *Command_factory::new_Syntax_error()
{
return new Syntax_error();
}
+
+
+Set_option *Command_factory::
+ new_Set_option(const char* name, uint len,
+ const char *option_arg, uint option_len,
+ const char *option_value_arg, uint option_value_len)
+{
+ return new Set_option(&instance_map, name, len, option_arg,
+ option_len, option_value_arg, option_value_len);
+}
+
+
+Unset_option *Command_factory::
+ new_Unset_option(const char* name, uint len,
+ const char *option_arg, uint option_len,
+ const char *option_value_arg, uint option_value_len)
+{
+ return new Unset_option(&instance_map, name, len, option_arg,
+ option_len, option_value_arg, option_value_len);
+}
+
+
+Show_instance_log *Command_factory::
+ new_Show_instance_log(const char *name, uint len,
+ Log_type log_type_arg,
+ const char *size, const char *offset)
+{
+ return new Show_instance_log(&instance_map, name, len,
+ log_type_arg, size, offset);
+}
+
+
+Show_instance_log_files *Command_factory::
+ new_Show_instance_log_files(const char *name, uint len)
+{
+ return new Show_instance_log_files(&instance_map, name, len);
+}
+
diff --git a/server-tools/instance-manager/factory.h b/server-tools/instance-manager/factory.h
index 0a1b955d156..14073eb5007 100644
--- a/server-tools/instance-manager/factory.h
+++ b/server-tools/instance-manager/factory.h
@@ -26,6 +26,8 @@
Http_command_factory e.t.c. Also see comment in the instance_map.cc
*/
+class Show_instances;
+
class Command_factory
{
public:
@@ -33,12 +35,26 @@ public:
{}
Show_instances *new_Show_instances ();
+ Flush_instances *new_Flush_instances ();
+ Syntax_error *new_Syntax_error ();
Show_instance_status *new_Show_instance_status (const char *name, uint len);
Show_instance_options *new_Show_instance_options (const char *name, uint len);
Start_instance *new_Start_instance (const char *name, uint len);
Stop_instance *new_Stop_instance (const char *name, uint len);
- Flush_instances *new_Flush_instances ();
- Syntax_error *new_Syntax_error ();
+ Show_instance_log *new_Show_instance_log (const char *name, uint len,
+ Log_type log_type_arg,
+ const char *size,
+ const char *offset);
+ Set_option *new_Set_option (const char *name, uint len,
+ const char *option_arg, uint option_len,
+ const char *option_value_arg,
+ uint option_value_len);
+ Unset_option *new_Unset_option (const char *name, uint len,
+ const char *option_arg, uint option_len,
+ const char *option_value_arg,
+ uint option_value_len);
+ Show_instance_log_files *new_Show_instance_log_files (const char *name,
+ uint len);
Instance_map &instance_map;
};
diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc
index 0d602f88ad2..ce7d43db74a 100644
--- a/server-tools/instance-manager/instance_options.cc
+++ b/server-tools/instance-manager/instance_options.cc
@@ -21,10 +21,10 @@
#include "instance_options.h"
#include "parse_output.h"
+#include "parse.h"
#include "buffer.h"
#include <my_sys.h>
-#include <mysql.h>
#include <signal.h>
#include <m_string.h>
@@ -54,7 +54,7 @@ int Instance_options::get_default_option(char *result, size_t result_len,
int rc= 1;
char verbose_option[]= " --no-defaults --verbose --help";
- Buffer cmd(strlen(mysqld_path)+sizeof(verbose_option)+1);
+ Buffer cmd(strlen(mysqld_path) + sizeof(verbose_option) + 1);
if (cmd.get_size()) /* malloc succeeded */
{
cmd.append(position, mysqld_path, strlen(mysqld_path));
@@ -76,6 +76,135 @@ err:
}
+int Instance_options::fill_log_options()
+{
+ /* array for the log option for mysqld */
+ enum { MAX_LOG_OPTIONS= 8 };
+ enum { MAX_LOG_OPTION_LENGTH= 256 };
+ /* the last option must be '\0', so we reserve space for it */
+ char log_options[MAX_LOG_OPTIONS + 1][MAX_LOG_OPTION_LENGTH];
+ Buffer buff;
+ uint position= 0;
+ char **tmp_argv= argv;
+ char datadir[MAX_LOG_OPTION_LENGTH];
+ char hostname[MAX_LOG_OPTION_LENGTH];
+ uint hostname_length;
+ struct log_files_st
+ {
+ const char *name;
+ uint length;
+ const char **value;
+ const char *default_suffix;
+ } logs[]=
+ {
+ {"--log-error", 11, &error_log, ".err"},
+ {"--log", 5, &query_log, ".log"},
+ {"--log-slow-queries", 18, &slow_log, "-slow.log"},
+ {NULL, 0, NULL, NULL}
+ };
+ struct log_files_st *log_files;
+
+ /* clean the buffer before usage */
+ bzero(log_options, sizeof(log_options));
+
+ /* create a "mysqld <argv_options>" command in the buffer */
+ buff.append(position, mysqld_path, strlen(mysqld_path));
+ position= strlen(mysqld_path);
+
+ /* skip the first option */
+ tmp_argv++;
+
+ while (*tmp_argv != 0)
+ {
+ buff.append(position, " ", 1);
+ position++;
+ buff.append(position, *tmp_argv, strlen(*tmp_argv));
+ position+= strlen(*tmp_argv);
+ tmp_argv++;
+ }
+
+ buff.append(position, "\0", 1);
+ position++;
+
+ /* get options and parse them */
+ if (parse_arguments(buff.buffer, "--log", (char *) log_options,
+ MAX_LOG_OPTIONS + 1, MAX_LOG_OPTION_LENGTH))
+ goto err;
+ /* compute hostname and datadir for the instance */
+ if (mysqld_datadir == NULL)
+ {
+ if (get_default_option(datadir,
+ MAX_LOG_OPTION_LENGTH, "--datadir"))
+ goto err;
+ }
+ else /* below is safe, as --datadir always has a value */
+ strncpy(datadir, strchr(mysqld_datadir, '=') + 1,
+ MAX_LOG_OPTION_LENGTH);
+
+ if (gethostname(hostname,sizeof(hostname)-1) < 0)
+ strmov(hostname, "mysql");
+
+ hostname[MAX_LOG_OPTION_LENGTH - 1]= 0; /* Safety */
+ hostname_length= strlen(hostname);
+
+
+ for (log_files= logs; log_files->name; log_files++)
+ {
+ for (int i=0; (i < MAX_LOG_OPTIONS) && (log_options[i][0] != '\0'); i++)
+ {
+ if (!strncmp(log_options[i], log_files->name, log_files->length))
+ {
+ /*
+ This is really log_files->name option if and only if it is followed
+ by '=', '\0' or space character. This way we can distinguish such
+ options as '--log' and '--log-bin'. This is checked in the following
+ two statements.
+ */
+ if (log_options[i][log_files->length] == '\0' ||
+ my_isspace(default_charset_info, log_options[i][log_files->length]))
+ {
+ char full_name[MAX_LOG_OPTION_LENGTH];
+
+ fn_format(full_name, hostname, datadir, "",
+ MY_UNPACK_FILENAME | MY_SAFE_PATH);
+
+
+ if ((MAX_LOG_OPTION_LENGTH - strlen(full_name)) >
+ strlen(log_files->default_suffix))
+ {
+ strcpy(full_name + strlen(full_name),
+ log_files->default_suffix);
+ }
+ else
+ goto err;
+
+ *(log_files->value)= strdup_root(&alloc, datadir);
+ }
+
+ if (log_options[i][log_files->length] == '=')
+ {
+ char full_name[MAX_LOG_OPTION_LENGTH];
+
+ fn_format(full_name, log_options[i] +log_files->length + 1,
+ datadir, "", MY_UNPACK_FILENAME | MY_SAFE_PATH);
+
+ if (!(*(log_files->value)=
+ strdup_root(&alloc, full_name)))
+ goto err;
+ }
+
+ }
+ }
+ }
+
+ return 0;
+
+err:
+ return 1;
+
+}
+
+
int Instance_options::get_pid_filename(char *result)
{
const char *pid_file= mysqld_pid_file;
@@ -190,6 +319,8 @@ int Instance_options::complete_initialization(const char *default_path,
options_array.elements*sizeof(char*));
argv[filled_default_options + options_array.elements]= 0;
+ fill_log_options();
+
return 0;
err:
diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h
index 06ad0156bc0..4def59b75a2 100644
--- a/server-tools/instance-manager/instance_options.h
+++ b/server-tools/instance-manager/instance_options.h
@@ -40,7 +40,8 @@ public:
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)
+ shutdown_delay_val(0), error_log(0), query_log(0), slow_log(0),
+ filled_default_options(0)
{}
~Instance_options();
/* fills in argv */
@@ -76,9 +77,14 @@ public:
const char *nonguarded;
const char *shutdown_delay;
uint shutdown_delay_val;
+ const char *error_log;
+ const char *query_log;
+ const char *slow_log;
+
/* this value is computed and cashed here */
DYNAMIC_ARRAY options_array;
private:
+ int fill_log_options();
int add_to_argv(const char *option);
int get_default_option(char *result, size_t result_len,
const char *option_name);
diff --git a/server-tools/instance-manager/messages.cc b/server-tools/instance-manager/messages.cc
index d044c0f65db..69bc8507c2e 100644
--- a/server-tools/instance-manager/messages.cc
+++ b/server-tools/instance-manager/messages.cc
@@ -57,6 +57,15 @@ static const char *mysqld_error_message(unsigned sql_errno)
" or resources shortage";
case ER_STOP_INSTANCE:
return "Cannot stop instance";
+ case ER_NO_SUCH_LOG:
+ return "The instance has no such log enabled";
+ case ER_OPEN_LOGFILE:
+ return "Cannot open log file";
+ case ER_GUESS_LOGFILE:
+ return "Cannot guess the log filename. Try specifying full log name"
+ "in the instance options";
+ case ER_ACCESS_OPTION_FILE:
+ return "Cannot open the option file to edit. Check permissions";
default:
DBUG_ASSERT(0);
return 0;
diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc
index 215dbe51b58..036d0887d18 100644
--- a/server-tools/instance-manager/mysql_connection.cc
+++ b/server-tools/instance-manager/mysql_connection.cc
@@ -284,7 +284,7 @@ int Mysql_connection_thread::check_connection()
net_send_error(&net, ER_ACCESS_DENIED_ERROR);
return 1;
}
- net_send_ok(&net, connection_id);
+ net_send_ok(&net, connection_id, NULL);
return 0;
}
@@ -332,7 +332,7 @@ int Mysql_connection_thread::dispatch_command(enum enum_server_command command,
return 1;
case COM_PING:
log_info("query for connection %d received ping command", connection_id);
- net_send_ok(&net, connection_id);
+ net_send_ok(&net, connection_id, NULL);
break;
case COM_QUERY:
{
diff --git a/server-tools/instance-manager/mysql_manager_error.h b/server-tools/instance-manager/mysql_manager_error.h
index 2862e01649d..afb9affe621 100644
--- a/server-tools/instance-manager/mysql_manager_error.h
+++ b/server-tools/instance-manager/mysql_manager_error.h
@@ -23,5 +23,9 @@
#define ER_INSTANCE_ALREADY_STARTED 3002
#define ER_CANNOT_START_INSTANCE 3003
#define ER_STOP_INSTANCE 3004
+#define ER_NO_SUCH_LOG 3005
+#define ER_OPEN_LOGFILE 3006
+#define ER_GUESS_LOGFILE 3007
+#define ER_ACCESS_OPTION_FILE 3008
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H */
diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc
index d3fb10b7c94..8af7c88ece2 100644
--- a/server-tools/instance-manager/parse.cc
+++ b/server-tools/instance-manager/parse.cc
@@ -15,38 +15,56 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "parse.h"
+#include "factory.h"
#include <string.h>
+
enum Token
{
- TOK_FLUSH = 0,
+ TOK_ERROR= 0, /* Encodes the "ERROR" word, it doesn't indicate error. */
+ TOK_FILES,
+ TOK_FLUSH,
+ TOK_GENERAL,
TOK_INSTANCE,
TOK_INSTANCES,
+ TOK_LOG,
TOK_OPTIONS,
+ TOK_SET,
+ TOK_SLOW,
TOK_START,
TOK_STATUS,
TOK_STOP,
TOK_SHOW,
+ TOK_UNSET,
TOK_NOT_FOUND, // must be after all tokens
TOK_END
};
+
struct tokens_st
{
uint length;
const char *tok_name;
};
+
static struct tokens_st tokens[]= {
+ {5, "ERROR"},
+ {5, "FILES"},
{5, "FLUSH"},
+ {7, "GENERAL"},
{8, "INSTANCE"},
{9, "INSTANCES"},
+ {3, "LOG"},
{7, "OPTIONS"},
+ {3, "SET"},
+ {4, "SLOW"},
{5, "START"},
{6, "STATUS"},
{4, "STOP"},
- {4, "SHOW"}
+ {4, "SHOW"},
+ {5, "UNSET"}
};
@@ -86,13 +104,6 @@ Token shift_token(const char **text, uint *word_len)
}
-void print_token(const char *token, uint tok_len)
-{
- for (uint i= 0; i < tok_len; ++i)
- printf("%c", token[i]);
-}
-
-
int get_text_id(const char **text, uint *word_len, const char **id)
{
get_word(text, word_len);
@@ -108,7 +119,15 @@ Command *parse_command(Command_factory *factory, const char *text)
uint word_len;
const char *instance_name;
uint instance_name_len;
+ const char *option;
+ uint option_len;
+ const char *option_value;
+ uint option_value_len;
+ const char *log_size;
Command *command;
+ const char *saved_text= text;
+ bool skip= false;
+ const char *tmp;
Token tok1= shift_token(&text, &word_len);
@@ -143,6 +162,55 @@ Command *parse_command(Command_factory *factory, const char *text)
command= factory->new_Flush_instances();
break;
+ case TOK_UNSET:
+ skip= true;
+ case TOK_SET:
+
+ get_text_id(&text, &instance_name_len, &instance_name);
+ text+= instance_name_len;
+
+ /* the next token should be a dot */
+ get_word(&text, &word_len);
+ if (*text != '.')
+ goto syntax_error;
+ text++;
+
+ get_word(&text, &option_len, NONSPACE);
+ option= text;
+ if ((tmp= strchr(text, '=')) != NULL)
+ option_len= tmp - text;
+ text+= option_len;
+
+ get_word(&text, &word_len);
+ if (*text == '=')
+ {
+ text++; /* skip '=' */
+ get_word(&text, &option_value_len, NONSPACE);
+ option_value= text;
+ text+= option_value_len;
+ }
+ else
+ {
+ option_value= "";
+ option_value_len= 0;
+ }
+
+ /* should be empty */
+ get_word(&text, &word_len);
+ if (word_len)
+ {
+ goto syntax_error;
+ }
+
+ if (skip)
+ command= factory->new_Unset_option(instance_name, instance_name_len,
+ option, option_len, option_value,
+ option_value_len);
+ else
+ command= factory->new_Set_option(instance_name, instance_name_len,
+ option, option_len, option_value,
+ option_value_len);
+ break;
case TOK_SHOW:
switch (shift_token(&text, &word_len)) {
case TOK_INSTANCES:
@@ -157,6 +225,7 @@ Command *parse_command(Command_factory *factory, const char *text)
case TOK_STATUS:
get_text_id(&text, &instance_name_len, &instance_name);
text+= instance_name_len;
+ /* check that this is the end of the command */
get_word(&text, &word_len);
if (word_len)
goto syntax_error;
@@ -172,7 +241,87 @@ Command *parse_command(Command_factory *factory, const char *text)
}
break;
default:
- goto syntax_error;
+ instance_name= text - word_len;
+ instance_name_len= word_len;
+ if (instance_name_len)
+ {
+ Log_type log_type;
+ switch (shift_token(&text, &word_len)) {
+ case TOK_LOG:
+ switch (Token tok3= shift_token(&text, &word_len)) {
+ case TOK_FILES:
+ get_word(&text, &word_len);
+ /* check that this is the end of the command */
+ if (word_len)
+ goto syntax_error;
+ command= (Command *)
+ factory->new_Show_instance_log_files(instance_name,
+ instance_name_len);
+ break;
+ case TOK_ERROR:
+ case TOK_GENERAL:
+ case TOK_SLOW:
+ /* define a log type */
+ switch (tok3) {
+ case TOK_ERROR:
+ log_type= LOG_ERROR;
+ break;
+ case TOK_GENERAL:
+ log_type= LOG_GENERAL;
+ break;
+ case TOK_SLOW:
+ log_type= LOG_SLOW;
+ break;
+ default:
+ goto syntax_error;
+ }
+ /* get the size of the log we want to retrieve */
+ get_text_id(&text, &word_len, &log_size);
+ text+= word_len;
+ /* this parameter is required */
+ if (!word_len)
+ goto syntax_error;
+ /* the next token should be comma, or nothing */
+ get_word(&text, &word_len);
+ switch (*text) {
+ case ',':
+ text++; /* swallow the comma */
+ /* read the next word */
+ get_word(&text, &word_len);
+ if (!word_len)
+ goto syntax_error;
+ command= (Command *)
+ factory->new_Show_instance_log(instance_name,
+ instance_name_len,
+ log_type,
+ log_size,
+ text);
+
+ //get_text_id(&text, &log_size_len, &log_size);
+ break;
+ case '\0':
+ command= (Command *)
+ factory->new_Show_instance_log(instance_name,
+ instance_name_len,
+ log_type,
+ log_size,
+ NULL);
+ break; /* this is ok */
+ default:
+ goto syntax_error;
+ }
+ break;
+ default:
+ goto syntax_error;
+ }
+ break;
+ default:
+ goto syntax_error;
+ }
+ }
+ else
+ goto syntax_error;
+ break;
}
break;
default:
@@ -182,3 +331,43 @@ syntax_error:
return command;
}
+/* additional parse function, needed to parse */
+
+/* create an array of strings from the output, starting from "word" */
+int parse_arguments(const char *command, const char *word, char *result,
+ int max_result_cardinality, size_t option_len)
+{
+ int wordlen;
+ int i= 0; /* result array index */
+ /* should be enough to store the string from the output */
+ enum { MAX_LINE_LEN= 4096 };
+ char linebuf[MAX_LINE_LEN];
+
+ wordlen= strlen(word);
+
+ uint lineword_len= 0;
+ const char *linep= command;
+ get_word((const char **) &linep, &lineword_len, NONSPACE);
+ while ((*linep != '\0') && (i < max_result_cardinality))
+ {
+ if (!strncmp(word, linep, wordlen))
+ {
+ strncpy(result + i*option_len, linep, lineword_len);
+ *(result + i*option_len + lineword_len)= '\0';
+ linep+= lineword_len;
+ i++;
+ }
+ else
+ linep+= lineword_len;
+ get_word((const char **) &linep, &lineword_len, NONSPACE);
+
+ /* stop if we've filled the array */
+ if (i >= max_result_cardinality)
+ break;
+ }
+
+
+ return 0;
+}
+
+
diff --git a/server-tools/instance-manager/parse.h b/server-tools/instance-manager/parse.h
index 92519893302..f03ef4c8a00 100644
--- a/server-tools/instance-manager/parse.h
+++ b/server-tools/instance-manager/parse.h
@@ -16,10 +16,24 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include "factory.h"
+#include <my_global.h>
+#include <my_sys.h>
+
+class Command;
+class Command_factory;
+
+enum Log_type
+{
+ LOG_ERROR= 0,
+ LOG_GENERAL,
+ LOG_SLOW
+};
Command *parse_command(Command_factory *factory, const char *text);
+int parse_arguments(const char *command, const char *word, char *result,
+ int max_result_cardinality, size_t option_len);
+
/* define kinds of the word seek method */
enum { ALPHANUM= 1, NONSPACE };
@@ -44,10 +58,12 @@ inline void get_word(const char **text, uint *word_len,
while (my_isalnum(default_charset_info, *word_end))
++word_end;
else
- while (!my_isspace(default_charset_info, *word_end))
+ while (!my_isspace(default_charset_info, *word_end) &&
+ (*word_end != '\0'))
++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 77fa40ca352..d6adb8079ce 100644
--- a/server-tools/instance-manager/parse_output.cc
+++ b/server-tools/instance-manager/parse_output.cc
@@ -47,7 +47,7 @@ int parse_output_and_get_value(const char *command, const char *word,
{
FILE *output;
uint wordlen;
- /* should be enought to store the string from the output */
+ /* should be enough to store the string from the output */
enum { MAX_LINE_LEN= 512 };
char linebuf[MAX_LINE_LEN];
@@ -99,3 +99,4 @@ pclose:
err:
return 1;
}
+
diff --git a/server-tools/instance-manager/parse_output.h b/server-tools/instance-manager/parse_output.h
index 20503a74629..48fd2ae4068 100644
--- a/server-tools/instance-manager/parse_output.h
+++ b/server-tools/instance-manager/parse_output.h
@@ -1,3 +1,5 @@
+#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_OUTPUT_H
+#define INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_OUTPUT_H
/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
@@ -17,3 +19,4 @@
int parse_output_and_get_value(const char *command, const char *word,
char *result, size_t result_len);
+#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_OUTPUT_H */
diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc
index 9c8975a78be..f0928743716 100644
--- a/server-tools/instance-manager/protocol.cc
+++ b/server-tools/instance-manager/protocol.cc
@@ -24,15 +24,25 @@
static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */
-int net_send_ok(struct st_net *net, unsigned long connection_id)
+
+int net_send_ok(struct st_net *net, unsigned long connection_id,
+ const char *message)
{
- char buff[1 + // packet type code
- 9 + // affected rows count
- 9 + // connection id
- 2 + // thread return status
- 2]; // warning count
+ /*
+ The format of a packet
+ 1 packet type code
+ 1-9 affected rows count
+ 1-9 connection id
+ 2 thread return status
+ 2 warning count
+ 1-9 + message length message to send (isn't stored if no message)
+ */
+ Buffer buff;
+ char *pos= buff.buffer;
+
+ /* check that we have space to hold mandatory fields */
+ buff.reserve(0, 23);
- char *pos= buff;
enum { OK_PACKET_CODE= 0 };
*pos++= OK_PACKET_CODE;
pos= net_store_length(pos, (ulonglong) 0);
@@ -43,7 +53,15 @@ int net_send_ok(struct st_net *net, unsigned long connection_id)
int2store(pos, 0);
pos+= 2;
- return my_net_write(net, buff, pos - buff) || net_flush(net);
+ uint position= pos - buff.buffer; /* we might need it for message */
+
+ if (message != NULL)
+ {
+ buff.reserve(position, 9 + strlen(message));
+ store_to_string(&buff, message, &position);
+ }
+
+ return my_net_write(net, buff.buffer, position) || net_flush(net);
}
@@ -99,15 +117,15 @@ char *net_store_length(char *pkg, uint length)
}
-int store_to_string(Buffer *buf, const char *string, uint *position)
+int store_to_string(Buffer *buf, const char *string, uint *position,
+ uint string_len)
{
uint currpos;
- uint string_len;
- string_len= strlen(string);
- if (buf->reserve(*position, 2))
+ if (buf->reserve(*position, 9))
goto err;
- currpos= (net_store_length(buf->buffer + *position, string_len) - buf->buffer);
+ currpos= (net_store_length(buf->buffer + *position,
+ (ulonglong) string_len) - buf->buffer);
if (buf->append(currpos, string, string_len))
goto err;
*position= *position + string_len + (currpos - *position);
@@ -118,6 +136,15 @@ err:
}
+int store_to_string(Buffer *buf, const char *string, uint *position)
+{
+ uint string_len;
+
+ string_len= strlen(string);
+ return store_to_string(buf, string, position, string_len);
+}
+
+
int send_eof(struct st_net *net)
{
char buff[1 + /* eof packet code */
diff --git a/server-tools/instance-manager/protocol.h b/server-tools/instance-manager/protocol.h
index b7b18b4b76c..c853fa10483 100644
--- a/server-tools/instance-manager/protocol.h
+++ b/server-tools/instance-manager/protocol.h
@@ -27,7 +27,8 @@ typedef struct field {
struct st_net;
-int net_send_ok(struct st_net *net, unsigned long connection_id);
+int net_send_ok(struct st_net *net, unsigned long connection_id,
+ const char *message);
int net_send_error(struct st_net *net, unsigned sql_errno);
@@ -39,6 +40,9 @@ char *net_store_length(char *pkg, uint length);
int store_to_string(Buffer *buf, const char *string, uint *position);
+int store_to_string(Buffer *buf, const char *string, uint *position,
+ uint string_len);
+
int send_eof(struct st_net *net);
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PROTOCOL_H */