Commit 83f9ee07 authored by petr@mysql.com's avatar petr@mysql.com

WL#2246 "IM: Add ability to change instance options, add server logs handling" ported to the

current version of the IM
parent eeeedd31
......@@ -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;
......@@ -294,13 +307,17 @@ int Show_instance_options::do_command(struct st_net *net,
char *tmp_option, *option_value;
get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i);
option_value= strchr(tmp_option, '=');
/* split the option string into two parts */
*option_value= 0;
/* split the option string into two parts if it has a value */
position= 0;
store_to_string(&send_buff, tmp_option + 2, &position);
store_to_string(&send_buff, option_value + 1, &position);
/* join name and the value into the same option again */
*option_value= '=';
if (option_value != NULL)
{
*option_value= 0;
store_to_string(&send_buff, tmp_option + 2, &position);
store_to_string(&send_buff, option_value + 1, &position);
/* join name and the value into the same option again */
*option_value= '=';
}
else store_to_string(&send_buff, tmp_option + 2, &position);
if (send_buff.is_error() ||
my_net_write(net, send_buff.buffer, (uint) position))
goto err;
......@@ -338,7 +355,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;
}
......@@ -359,9 +376,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);
}
......@@ -371,7 +839,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;
}
......@@ -392,7 +860,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;
}
}
......
......@@ -18,6 +18,7 @@
#include "command.h"
#include "instance.h"
#include "parse.h"
/*
Print all instances of this instance manager.
......@@ -115,6 +116,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
......@@ -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 */
......@@ -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);
}
......@@ -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;
};
......
......@@ -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:
......
......@@ -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);
......
......@@ -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;
......
......@@ -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:
{
......
......@@ -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 */
......@@ -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;
}
......@@ -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 */
......@@ -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;
}
#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 */
......@@ -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 */
......
......@@ -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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment