Commit 76164b89 authored by unknown's avatar unknown

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

current version of the IM


server-tools/instance-manager/commands.cc:
  Log and set options commands added
server-tools/instance-manager/commands.h:
  Log and set options commands added
server-tools/instance-manager/factory.cc:
  Log and set options factory entries added
server-tools/instance-manager/factory.h:
  prototypes added
server-tools/instance-manager/instance_options.cc:
  fill_log_options() added
server-tools/instance-manager/instance_options.h:
  log processing options and funcctions added
server-tools/instance-manager/messages.cc:
  new error messages added (log and option processing-related)
server-tools/instance-manager/mysql_connection.cc:
  minor fixes
server-tools/instance-manager/mysql_manager_error.h:
  new error codes added
server-tools/instance-manager/parse.cc:
  parser fixed to recognize new commands. function to parse command-line options added
server-tools/instance-manager/parse.h:
  header fixed in line with .cc changes
server-tools/instance-manager/parse_output.cc:
  cleanup
server-tools/instance-manager/parse_output.h:
  header guards added
server-tools/instance-manager/protocol.cc:
  Protocol support extended to provide messages in ok packet
server-tools/instance-manager/protocol.h:
  protocol support extended: ok packet could contain messages
parent 7ff83a3f
...@@ -27,6 +27,19 @@ ...@@ -27,6 +27,19 @@
#include <mysql.h> #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: */ /* implementation for Show_instances: */
...@@ -106,7 +119,7 @@ int Flush_instances::execute(struct st_net *net, ulong connection_id) ...@@ -106,7 +119,7 @@ int Flush_instances::execute(struct st_net *net, ulong connection_id)
if (instance_map->flush_instances()) if (instance_map->flush_instances())
return ER_OUT_OF_RESOURCES; return ER_OUT_OF_RESOURCES;
net_send_ok(net, connection_id); net_send_ok(net, connection_id, NULL);
return 0; return 0;
} }
...@@ -119,7 +132,7 @@ Show_instance_status::Show_instance_status(Instance_map *instance_map_arg, ...@@ -119,7 +132,7 @@ Show_instance_status::Show_instance_status(Instance_map *instance_map_arg,
{ {
Instance *instance; 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))) if ((instance= instance_map->find(name, len)))
{ {
instance_name= instance->options.instance_name; instance_name= instance->options.instance_name;
...@@ -225,7 +238,7 @@ Show_instance_options::Show_instance_options(Instance_map *instance_map_arg, ...@@ -225,7 +238,7 @@ Show_instance_options::Show_instance_options(Instance_map *instance_map_arg,
{ {
Instance *instance; 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))) if ((instance= instance_map->find(name, len)))
{ {
instance_name= instance->options.instance_name; instance_name= instance->options.instance_name;
...@@ -294,13 +307,17 @@ int Show_instance_options::do_command(struct st_net *net, ...@@ -294,13 +307,17 @@ int Show_instance_options::do_command(struct st_net *net,
char *tmp_option, *option_value; char *tmp_option, *option_value;
get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i); get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i);
option_value= strchr(tmp_option, '='); option_value= strchr(tmp_option, '=');
/* split the option string into two parts */ /* split the option string into two parts if it has a value */
*option_value= 0;
position= 0; position= 0;
if (option_value != NULL)
{
*option_value= 0;
store_to_string(&send_buff, tmp_option + 2, &position); store_to_string(&send_buff, tmp_option + 2, &position);
store_to_string(&send_buff, option_value + 1, &position); store_to_string(&send_buff, option_value + 1, &position);
/* join name and the value into the same option again */ /* join name and the value into the same option again */
*option_value= '='; *option_value= '=';
}
else store_to_string(&send_buff, tmp_option + 2, &position);
if (send_buff.is_error() || if (send_buff.is_error() ||
my_net_write(net, send_buff.buffer, (uint) position)) my_net_write(net, send_buff.buffer, (uint) position))
goto err; goto err;
...@@ -338,7 +355,7 @@ Start_instance::Start_instance(Instance_map *instance_map_arg, ...@@ -338,7 +355,7 @@ Start_instance::Start_instance(Instance_map *instance_map_arg,
const char *name, uint len) const char *name, uint len)
:Command(instance_map_arg) :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))) if ((instance= instance_map->find(name, len)))
instance_name= instance->options.instance_name; instance_name= instance->options.instance_name;
} }
...@@ -359,9 +376,460 @@ int Start_instance::execute(struct st_net *net, ulong connection_id) ...@@ -359,9 +376,460 @@ int Start_instance::execute(struct st_net *net, ulong connection_id)
if (!(instance->options.nonguarded)) if (!(instance->options.nonguarded))
instance_map->guardian->guard(instance); 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; 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, ...@@ -371,7 +839,7 @@ Stop_instance::Stop_instance(Instance_map *instance_map_arg,
const char *name, uint len) const char *name, uint len)
:Command(instance_map_arg) :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))) if ((instance= instance_map->find(name, len)))
instance_name= instance->options.instance_name; instance_name= instance->options.instance_name;
} }
...@@ -392,7 +860,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id) ...@@ -392,7 +860,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id)
stop_guard(instance); stop_guard(instance);
if ((err_code= instance->stop())) if ((err_code= instance->stop()))
return err_code; return err_code;
net_send_ok(net, connection_id); net_send_ok(net, connection_id, NULL);
return 0; return 0;
} }
} }
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "command.h" #include "command.h"
#include "instance.h" #include "instance.h"
#include "parse.h"
/* /*
Print all instances of this instance manager. Print all instances of this instance manager.
...@@ -115,6 +116,45 @@ public: ...@@ -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. 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 We need it to distinguish the parse error and the situation when parser internal
...@@ -128,4 +168,50 @@ public: ...@@ -128,4 +168,50 @@ public:
int execute(struct st_net *net, ulong connection_id); 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 */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H */
...@@ -16,40 +16,85 @@ ...@@ -16,40 +16,85 @@
#include "factory.h" #include "factory.h"
Show_instances *Command_factory::new_Show_instances() Show_instances *Command_factory::new_Show_instances()
{ {
return new Show_instances(&instance_map); return new Show_instances(&instance_map);
} }
Flush_instances *Command_factory::new_Flush_instances() Flush_instances *Command_factory::new_Flush_instances()
{ {
return new Flush_instances(&instance_map); return new Flush_instances(&instance_map);
} }
Show_instance_status *Command_factory:: Show_instance_status *Command_factory::
new_Show_instance_status(const char *name, uint len) new_Show_instance_status(const char *name, uint len)
{ {
return new Show_instance_status(&instance_map, name, len); return new Show_instance_status(&instance_map, name, len);
} }
Show_instance_options *Command_factory:: Show_instance_options *Command_factory::
new_Show_instance_options(const char *name, uint len) new_Show_instance_options(const char *name, uint len)
{ {
return new Show_instance_options(&instance_map, name, len); return new Show_instance_options(&instance_map, name, len);
} }
Start_instance *Command_factory:: Start_instance *Command_factory::
new_Start_instance(const char *name, uint len) new_Start_instance(const char *name, uint len)
{ {
return new Start_instance(&instance_map, name, len); return new Start_instance(&instance_map, name, len);
} }
Stop_instance *Command_factory::new_Stop_instance(const char *name, uint len) Stop_instance *Command_factory::new_Stop_instance(const char *name, uint len)
{ {
return new Stop_instance(&instance_map, name, len); return new Stop_instance(&instance_map, name, len);
} }
Syntax_error *Command_factory::new_Syntax_error() Syntax_error *Command_factory::new_Syntax_error()
{ {
return 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 @@ ...@@ -26,6 +26,8 @@
Http_command_factory e.t.c. Also see comment in the instance_map.cc Http_command_factory e.t.c. Also see comment in the instance_map.cc
*/ */
class Show_instances;
class Command_factory class Command_factory
{ {
public: public:
...@@ -33,12 +35,26 @@ public: ...@@ -33,12 +35,26 @@ public:
{} {}
Show_instances *new_Show_instances (); 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_status *new_Show_instance_status (const char *name, uint len);
Show_instance_options *new_Show_instance_options (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); Start_instance *new_Start_instance (const char *name, uint len);
Stop_instance *new_Stop_instance (const char *name, uint len); Stop_instance *new_Stop_instance (const char *name, uint len);
Flush_instances *new_Flush_instances (); Show_instance_log *new_Show_instance_log (const char *name, uint len,
Syntax_error *new_Syntax_error (); 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; Instance_map &instance_map;
}; };
......
...@@ -21,10 +21,10 @@ ...@@ -21,10 +21,10 @@
#include "instance_options.h" #include "instance_options.h"
#include "parse_output.h" #include "parse_output.h"
#include "parse.h"
#include "buffer.h" #include "buffer.h"
#include <my_sys.h> #include <my_sys.h>
#include <mysql.h>
#include <signal.h> #include <signal.h>
#include <m_string.h> #include <m_string.h>
...@@ -54,7 +54,7 @@ int Instance_options::get_default_option(char *result, size_t result_len, ...@@ -54,7 +54,7 @@ int Instance_options::get_default_option(char *result, size_t result_len,
int rc= 1; int rc= 1;
char verbose_option[]= " --no-defaults --verbose --help"; 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 */ if (cmd.get_size()) /* malloc succeeded */
{ {
cmd.append(position, mysqld_path, strlen(mysqld_path)); cmd.append(position, mysqld_path, strlen(mysqld_path));
...@@ -76,6 +76,135 @@ err: ...@@ -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) int Instance_options::get_pid_filename(char *result)
{ {
const char *pid_file= mysqld_pid_file; const char *pid_file= mysqld_pid_file;
...@@ -190,6 +319,8 @@ int Instance_options::complete_initialization(const char *default_path, ...@@ -190,6 +319,8 @@ int Instance_options::complete_initialization(const char *default_path,
options_array.elements*sizeof(char*)); options_array.elements*sizeof(char*));
argv[filled_default_options + options_array.elements]= 0; argv[filled_default_options + options_array.elements]= 0;
fill_log_options();
return 0; return 0;
err: err:
......
...@@ -40,7 +40,8 @@ public: ...@@ -40,7 +40,8 @@ public:
mysqld_socket(0), mysqld_datadir(0), mysqld_socket(0), mysqld_datadir(0),
mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0), mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0),
mysqld_port_val(0), mysqld_path(0), nonguarded(0), shutdown_delay(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(); ~Instance_options();
/* fills in argv */ /* fills in argv */
...@@ -76,9 +77,14 @@ public: ...@@ -76,9 +77,14 @@ public:
const char *nonguarded; const char *nonguarded;
const char *shutdown_delay; const char *shutdown_delay;
uint shutdown_delay_val; uint shutdown_delay_val;
const char *error_log;
const char *query_log;
const char *slow_log;
/* this value is computed and cashed here */ /* this value is computed and cashed here */
DYNAMIC_ARRAY options_array; DYNAMIC_ARRAY options_array;
private: private:
int fill_log_options();
int add_to_argv(const char *option); int add_to_argv(const char *option);
int get_default_option(char *result, size_t result_len, int get_default_option(char *result, size_t result_len,
const char *option_name); const char *option_name);
......
...@@ -57,6 +57,15 @@ static const char *mysqld_error_message(unsigned sql_errno) ...@@ -57,6 +57,15 @@ static const char *mysqld_error_message(unsigned sql_errno)
" or resources shortage"; " or resources shortage";
case ER_STOP_INSTANCE: case ER_STOP_INSTANCE:
return "Cannot 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: default:
DBUG_ASSERT(0); DBUG_ASSERT(0);
return 0; return 0;
......
...@@ -284,7 +284,7 @@ int Mysql_connection_thread::check_connection() ...@@ -284,7 +284,7 @@ int Mysql_connection_thread::check_connection()
net_send_error(&net, ER_ACCESS_DENIED_ERROR); net_send_error(&net, ER_ACCESS_DENIED_ERROR);
return 1; return 1;
} }
net_send_ok(&net, connection_id); net_send_ok(&net, connection_id, NULL);
return 0; return 0;
} }
...@@ -332,7 +332,7 @@ int Mysql_connection_thread::dispatch_command(enum enum_server_command command, ...@@ -332,7 +332,7 @@ int Mysql_connection_thread::dispatch_command(enum enum_server_command command,
return 1; return 1;
case COM_PING: case COM_PING:
log_info("query for connection %d received ping command", connection_id); 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; break;
case COM_QUERY: case COM_QUERY:
{ {
......
...@@ -23,5 +23,9 @@ ...@@ -23,5 +23,9 @@
#define ER_INSTANCE_ALREADY_STARTED 3002 #define ER_INSTANCE_ALREADY_STARTED 3002
#define ER_CANNOT_START_INSTANCE 3003 #define ER_CANNOT_START_INSTANCE 3003
#define ER_STOP_INSTANCE 3004 #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 */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H */
...@@ -15,38 +15,56 @@ ...@@ -15,38 +15,56 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "parse.h" #include "parse.h"
#include "factory.h"
#include <string.h> #include <string.h>
enum Token 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_INSTANCE,
TOK_INSTANCES, TOK_INSTANCES,
TOK_LOG,
TOK_OPTIONS, TOK_OPTIONS,
TOK_SET,
TOK_SLOW,
TOK_START, TOK_START,
TOK_STATUS, TOK_STATUS,
TOK_STOP, TOK_STOP,
TOK_SHOW, TOK_SHOW,
TOK_UNSET,
TOK_NOT_FOUND, // must be after all tokens TOK_NOT_FOUND, // must be after all tokens
TOK_END TOK_END
}; };
struct tokens_st struct tokens_st
{ {
uint length; uint length;
const char *tok_name; const char *tok_name;
}; };
static struct tokens_st tokens[]= { static struct tokens_st tokens[]= {
{5, "ERROR"},
{5, "FILES"},
{5, "FLUSH"}, {5, "FLUSH"},
{7, "GENERAL"},
{8, "INSTANCE"}, {8, "INSTANCE"},
{9, "INSTANCES"}, {9, "INSTANCES"},
{3, "LOG"},
{7, "OPTIONS"}, {7, "OPTIONS"},
{3, "SET"},
{4, "SLOW"},
{5, "START"}, {5, "START"},
{6, "STATUS"}, {6, "STATUS"},
{4, "STOP"}, {4, "STOP"},
{4, "SHOW"} {4, "SHOW"},
{5, "UNSET"}
}; };
...@@ -86,13 +104,6 @@ Token shift_token(const char **text, uint *word_len) ...@@ -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) int get_text_id(const char **text, uint *word_len, const char **id)
{ {
get_word(text, word_len); get_word(text, word_len);
...@@ -108,7 +119,15 @@ Command *parse_command(Command_factory *factory, const char *text) ...@@ -108,7 +119,15 @@ Command *parse_command(Command_factory *factory, const char *text)
uint word_len; uint word_len;
const char *instance_name; const char *instance_name;
uint instance_name_len; uint instance_name_len;
const char *option;
uint option_len;
const char *option_value;
uint option_value_len;
const char *log_size;
Command *command; Command *command;
const char *saved_text= text;
bool skip= false;
const char *tmp;
Token tok1= shift_token(&text, &word_len); Token tok1= shift_token(&text, &word_len);
...@@ -143,6 +162,55 @@ Command *parse_command(Command_factory *factory, const char *text) ...@@ -143,6 +162,55 @@ Command *parse_command(Command_factory *factory, const char *text)
command= factory->new_Flush_instances(); command= factory->new_Flush_instances();
break; 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: case TOK_SHOW:
switch (shift_token(&text, &word_len)) { switch (shift_token(&text, &word_len)) {
case TOK_INSTANCES: case TOK_INSTANCES:
...@@ -157,6 +225,7 @@ Command *parse_command(Command_factory *factory, const char *text) ...@@ -157,6 +225,7 @@ Command *parse_command(Command_factory *factory, const char *text)
case TOK_STATUS: case TOK_STATUS:
get_text_id(&text, &instance_name_len, &instance_name); get_text_id(&text, &instance_name_len, &instance_name);
text+= instance_name_len; text+= instance_name_len;
/* check that this is the end of the command */
get_word(&text, &word_len); get_word(&text, &word_len);
if (word_len) if (word_len)
goto syntax_error; goto syntax_error;
...@@ -171,14 +240,134 @@ Command *parse_command(Command_factory *factory, const char *text) ...@@ -171,14 +240,134 @@ Command *parse_command(Command_factory *factory, const char *text)
goto syntax_error; goto syntax_error;
} }
break; break;
default:
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: default:
goto syntax_error; goto syntax_error;
} }
break; break;
default: default:
goto syntax_error;
}
}
else
goto syntax_error;
break;
}
break;
default:
syntax_error: syntax_error:
command= factory->new_Syntax_error(); command= factory->new_Syntax_error();
} }
return command; 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 @@ ...@@ -16,10 +16,24 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 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); 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 */ /* define kinds of the word seek method */
enum { ALPHANUM= 1, NONSPACE }; enum { ALPHANUM= 1, NONSPACE };
...@@ -44,10 +58,12 @@ inline void get_word(const char **text, uint *word_len, ...@@ -44,10 +58,12 @@ inline void get_word(const char **text, uint *word_len,
while (my_isalnum(default_charset_info, *word_end)) while (my_isalnum(default_charset_info, *word_end))
++word_end; ++word_end;
else else
while (!my_isspace(default_charset_info, *word_end)) while (!my_isspace(default_charset_info, *word_end) &&
(*word_end != '\0'))
++word_end; ++word_end;
*word_len= word_end - *text; *word_len= word_end - *text;
} }
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H */
...@@ -47,7 +47,7 @@ int parse_output_and_get_value(const char *command, const char *word, ...@@ -47,7 +47,7 @@ int parse_output_and_get_value(const char *command, const char *word,
{ {
FILE *output; FILE *output;
uint wordlen; 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 }; enum { MAX_LINE_LEN= 512 };
char linebuf[MAX_LINE_LEN]; char linebuf[MAX_LINE_LEN];
...@@ -99,3 +99,4 @@ pclose: ...@@ -99,3 +99,4 @@ pclose:
err: err:
return 1; return 1;
} }
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_OUTPUT_H
#define INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_OUTPUT_H
/* Copyright (C) 2004 MySQL AB /* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
...@@ -17,3 +19,4 @@ ...@@ -17,3 +19,4 @@
int parse_output_and_get_value(const char *command, const char *word, int parse_output_and_get_value(const char *command, const char *word,
char *result, size_t result_len); char *result, size_t result_len);
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_OUTPUT_H */
...@@ -24,15 +24,25 @@ ...@@ -24,15 +24,25 @@
static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */ 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 The format of a packet
9 + // connection id 1 packet type code
2 + // thread return status 1-9 affected rows count
2]; // warning 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 }; enum { OK_PACKET_CODE= 0 };
*pos++= OK_PACKET_CODE; *pos++= OK_PACKET_CODE;
pos= net_store_length(pos, (ulonglong) 0); pos= net_store_length(pos, (ulonglong) 0);
...@@ -43,7 +53,15 @@ int net_send_ok(struct st_net *net, unsigned long connection_id) ...@@ -43,7 +53,15 @@ int net_send_ok(struct st_net *net, unsigned long connection_id)
int2store(pos, 0); int2store(pos, 0);
pos+= 2; 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) ...@@ -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 currpos;
uint string_len;
string_len= strlen(string); if (buf->reserve(*position, 9))
if (buf->reserve(*position, 2))
goto err; 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)) if (buf->append(currpos, string, string_len))
goto err; goto err;
*position= *position + string_len + (currpos - *position); *position= *position + string_len + (currpos - *position);
...@@ -118,6 +136,15 @@ err: ...@@ -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) int send_eof(struct st_net *net)
{ {
char buff[1 + /* eof packet code */ char buff[1 + /* eof packet code */
......
...@@ -27,7 +27,8 @@ typedef struct field { ...@@ -27,7 +27,8 @@ typedef struct field {
struct st_net; 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); int net_send_error(struct st_net *net, unsigned sql_errno);
...@@ -39,6 +40,9 @@ char *net_store_length(char *pkg, uint length); ...@@ -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);
int store_to_string(Buffer *buf, const char *string, uint *position,
uint string_len);
int send_eof(struct st_net *net); int send_eof(struct st_net *net);
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PROTOCOL_H */ #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