diff options
Diffstat (limited to 'server-tools/instance-manager')
-rw-r--r-- | server-tools/instance-manager/commands.cc | 478 | ||||
-rw-r--r-- | server-tools/instance-manager/commands.h | 86 | ||||
-rw-r--r-- | server-tools/instance-manager/factory.cc | 45 | ||||
-rw-r--r-- | server-tools/instance-manager/factory.h | 20 | ||||
-rw-r--r-- | server-tools/instance-manager/instance_options.cc | 135 | ||||
-rw-r--r-- | server-tools/instance-manager/instance_options.h | 8 | ||||
-rw-r--r-- | server-tools/instance-manager/messages.cc | 9 | ||||
-rw-r--r-- | server-tools/instance-manager/mysql_connection.cc | 4 | ||||
-rw-r--r-- | server-tools/instance-manager/mysql_manager_error.h | 4 | ||||
-rw-r--r-- | server-tools/instance-manager/parse.cc | 209 | ||||
-rw-r--r-- | server-tools/instance-manager/parse.h | 20 | ||||
-rw-r--r-- | server-tools/instance-manager/parse_output.cc | 3 | ||||
-rw-r--r-- | server-tools/instance-manager/parse_output.h | 3 | ||||
-rw-r--r-- | server-tools/instance-manager/protocol.cc | 53 | ||||
-rw-r--r-- | server-tools/instance-manager/protocol.h | 6 |
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 */ |