Commit 1ad379af authored by unknown's avatar unknown
Browse files

merge

parents 241bedad 76164b89
Loading
Loading
Loading
Loading
+471 −7
Original line number Diff line number Diff line
@@ -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,19 +378,470 @@ 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);
}


/* Implementation for Stop_instance: */

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;
  }
}
+86 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include "command.h"
#include "instance.h"
#include "parse.h"

/*
  Print all instances of this instance manager.
@@ -115,6 +116,45 @@ class Stop_instance : public Command
};


/*
  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 @@ class Syntax_error : public Command
  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 */
+45 −0
Original line number Diff line number Diff line
@@ -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);
}
+18 −2
Original line number Diff line number Diff line
@@ -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 @@ class Command_factory
  {}

  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;
};
+133 −2
Original line number Diff line number Diff line
@@ -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>

@@ -76,6 +76,135 @@ int Instance_options::get_default_option(char *result, size_t result_len,
}


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:
Loading