instance_map.cc 15.9 KB
Newer Older
1 2 3 4
/* Copyright (C) 2004 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
6 7 8 9 10 11 12 13 14 15

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

16
#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
17 18 19 20
#pragma implementation
#endif

#include "instance_map.h"
21

22 23 24 25
#include <my_global.h>
#include <m_ctype.h>
#include <mysql_com.h>

26 27
#include "buffer.h"
#include "instance.h"
28
#include "log.h"
29 30
#include "mysqld_error.h"
#include "mysql_manager_error.h"
31
#include "options.h"
32
#include "priv.h"
33 34 35

C_MODE_START

unknown's avatar
unknown committed
36 37 38
/**
  HASH-routines: get key of instance for storing in hash.
*/
39

40 41
static uchar* get_instance_key(const uchar* u, size_t* len,
                               my_bool __attribute__((unused)) t)
42 43
{
  const Instance *instance= (const Instance *) u;
44
  *len= instance->options.instance_name.length;
45
  return (uchar *) instance->options.instance_name.str;
46 47
}

unknown's avatar
unknown committed
48 49 50 51
/**
  HASH-routines: cleanup handler.
*/

52 53 54 55 56 57
static void delete_instance(void *u)
{
  Instance *instance= (Instance *) u;
  delete instance;
}

unknown's avatar
unknown committed
58 59
/**
  The option handler to pass to the process_default_option_files function.
60

61
  SYNOPSIS
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
    process_option()
    ctx               Handler context. Here it is an instance_map structure.
    group_name        The name of the group the option belongs to.
    option            The very option to be processed. It is already
                      prepared to be used in argv (has -- prefix)

  DESCRIPTION

    This handler checks whether a group is an instance group and adds
    an option to the appropriate instance class. If this is the first
    occurence of an instance name, we'll also create the instance
    with such name and add it to the instance map.

  RETURN
    0 - ok
    1 - error occured
*/

unknown's avatar
unknown committed
80
static int process_option(void *ctx, const char *group, const char *option)
81
{
82 83 84 85 86
  Instance_map *map= (Instance_map*) ctx;
  LEX_STRING group_str;

  group_str.str= (char *) group;
  group_str.length= strlen(group);
87

88
  return map->process_one_option(&group_str, option);
89 90 91 92 93
}

C_MODE_END


unknown's avatar
unknown committed
94 95
/**
  Parse option string.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113

  SYNOPSIS
    parse_option()
      option_str        [IN] option string (e.g. "--name=value")
      option_name_buf   [OUT] parsed name of the option.
                        Must be of (MAX_OPTION_LEN + 1) size.
      option_value_buf  [OUT] parsed value of the option.
                        Must be of (MAX_OPTION_LEN + 1) size.

  DESCRIPTION
    This is an auxiliary function and should not be used externally. It is
    intended to parse whole option string into option name and option value.
*/

static void parse_option(const char *option_str,
                         char *option_name_buf,
                         char *option_value_buf)
{
114
  const char *eq_pos;
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
  const char *ptr= option_str;

  while (*ptr == '-')
    ++ptr;

  strmake(option_name_buf, ptr, MAX_OPTION_LEN + 1);

  eq_pos= strchr(ptr, '=');
  if (eq_pos)
  {
    option_name_buf[eq_pos - ptr]= 0;
    strmake(option_value_buf, eq_pos + 1, MAX_OPTION_LEN + 1);
  }
  else
  {
    option_value_buf[0]= 0;
  }
}


unknown's avatar
unknown committed
135
/**
136 137 138 139 140 141 142 143 144 145 146 147 148
  Process one option from the configuration file.

  SYNOPSIS
    Instance_map::process_one_option()
      group         group name
      option        option string (e.g. "--name=value")

  DESCRIPTION
    This is an auxiliary function and should not be used externally.
    It is used only by flush_instances(), which pass it to
    process_option(). The caller ensures proper locking
    of the instance map object.
*/
unknown's avatar
unknown committed
149 150 151 152
  /*
    Process a given option and assign it to appropricate instance. This is
    required for the option handler, passed to my_search_option_files().
  */
153

154 155
int Instance_map::process_one_option(const LEX_STRING *group,
                                     const char *option)
156
{
157 158
  Instance *instance= NULL;

159 160 161 162 163 164 165 166 167
  if (!Instance::is_name_valid(group))
  {
    /*
      Current section name is not a valid instance name.
      We should skip it w/o error.
    */
    return 0;
  }

168
  if (!(instance= (Instance *) hash_search(&hash, (uchar *) group->str,
169 170
                                           group->length)))
  {
171
    if (!(instance= new Instance()))
172 173 174
      return 1;

    if (instance->init(group) || add_instance(instance))
175
    {
176 177
      delete instance;
      return 1;
178 179
    }

180 181 182
    if (instance->is_mysqld_compatible())
      log_info("Warning: instance name '%s' is mysqld-compatible.",
               (const char *) group->str);
183

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
    log_info("mysqld instance '%s' has been added successfully.",
             (const char *) group->str);
  }

  if (option)
  {
    char option_name[MAX_OPTION_LEN + 1];
    char option_value[MAX_OPTION_LEN + 1];

    parse_option(option, option_name, option_value);

    if (instance->is_mysqld_compatible() &&
        Instance_options::is_option_im_specific(option_name))
    {
      log_info("Warning: configuration of mysqld-compatible instance '%s' "
               "contains IM-specific option '%s'. "
               "This breaks backward compatibility for the configuration file.",
               (const char *) group->str,
               (const char *) option_name);
    }

    Named_value option(option_name, option_value);

    if (instance->options.set_option(&option))
      return 1;   /* the instance'll be deleted when we destroy the map */
  }

  return 0;
212 213 214
}


unknown's avatar
unknown committed
215 216 217 218
/**
  Instance_map constructor.
*/

219
Instance_map::Instance_map()
220 221 222 223 224
{
  pthread_mutex_init(&LOCK_instance_map, 0);
}


unknown's avatar
unknown committed
225 226 227 228
/**
  Initialize Instance_map internals.
*/

229
bool Instance_map::init()
unknown's avatar
unknown committed
230
{
unknown's avatar
unknown committed
231 232
  return hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
                   get_instance_key, delete_instance, 0);
unknown's avatar
unknown committed
233 234
}

unknown's avatar
unknown committed
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250

/**
  Reset Instance_map data.
*/

bool Instance_map::reset()
{
  hash_free(&hash);
  return init();
}


/**
  Instance_map destructor.
*/

251 252
Instance_map::~Instance_map()
{
unknown's avatar
unknown committed
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
  lock();

  /*
    NOTE: it's necessary to synchronize on each instance before removal,
    because Instance-monitoring thread can be still alive an hold the mutex
    (because it is detached and we have no control over it).
  */

  while (true)
  {
    Iterator it(this);
    Instance *instance= it.next();

    if (!instance)
      break;

    instance->lock();
    instance->unlock();

    remove_instance(instance);
  }

275
  hash_free(&hash);
unknown's avatar
unknown committed
276 277
  unlock();

278 279 280 281
  pthread_mutex_destroy(&LOCK_instance_map);
}


unknown's avatar
unknown committed
282 283 284 285
/**
  Lock Instance_map.
*/

286
void Instance_map::lock()
287
{
288
  pthread_mutex_lock(&LOCK_instance_map);
289 290 291
}


unknown's avatar
unknown committed
292 293 294 295
/**
  Unlock Instance_map.
*/

296
void Instance_map::unlock()
297
{
298
  pthread_mutex_unlock(&LOCK_instance_map);
299 300
}

301

unknown's avatar
unknown committed
302 303
/**
  Check if there is an active instance or not.
304
*/
305

306
bool Instance_map::is_there_active_instance()
307 308
{
  Instance *instance;
309 310 311 312
  Iterator iterator(this);

  while ((instance= iterator.next()))
  {
unknown's avatar
unknown committed
313 314 315 316 317 318 319
    bool active_instance_found;

    instance->lock();
    active_instance_found= instance->is_active();
    instance->unlock();

    if (active_instance_found)
320 321 322 323
      return TRUE;
  }

  return FALSE;
324 325 326
}


unknown's avatar
unknown committed
327 328 329 330 331 332
/**
  Add an instance into the internal hash.

  MT-NOTE: Instance Map must be locked before calling the operation.
*/

333
int Instance_map::add_instance(Instance *instance)
334
{
335
  return my_hash_insert(&hash, (uchar *) instance);
336
}
337

338

unknown's avatar
unknown committed
339 340 341 342 343 344
/**
  Remove instance from the internal hash.

  MT-NOTE: Instance Map must be locked before calling the operation.
*/

345 346
int Instance_map::remove_instance(Instance *instance)
{
347
  return hash_delete(&hash, (uchar *) instance);
348
}
unknown's avatar
unknown committed
349 350


unknown's avatar
unknown committed
351 352 353 354 355 356
/**
  Create a new instance and register it in the internal hash.

  MT-NOTE: Instance Map must be locked before calling the operation.
*/

357 358 359
int Instance_map::create_instance(const LEX_STRING *instance_name,
                                  const Named_value_arr *options)
{
360
  Instance *instance= new Instance();
361 362 363

  if (!instance)
  {
unknown's avatar
unknown committed
364
    log_error("Can not allocate instance (name: '%s').",
365 366
              (const char *) instance_name->str);
    return ER_OUT_OF_RESOURCES;
367
  }
368 369 370

  if (instance->init(instance_name))
  {
unknown's avatar
unknown committed
371
    log_error("Can not initialize instance (name: '%s').",
372 373 374 375 376 377 378 379 380 381 382
              (const char *) instance_name->str);
    delete instance;
    return ER_OUT_OF_RESOURCES;
  }

  for (int i= 0; options && i < options->get_size(); ++i)
  {
    Named_value option= options->get_element(i);

    if (instance->is_mysqld_compatible() &&
        Instance_options::is_option_im_specific(option.get_name()))
unknown's avatar
unknown committed
383
    {
unknown's avatar
unknown committed
384
      log_error("IM-option (%s) can not be used "
385 386 387 388 389
                "in configuration of mysqld-compatible instance (%s).",
                (const char *) option.get_name(),
                (const char *) instance_name->str);
      delete instance;
      return ER_INCOMPATIBLE_OPTION;
unknown's avatar
unknown committed
390 391
    }

392 393 394 395 396 397 398
    instance->options.set_option(&option);
  }

  if (instance->is_mysqld_compatible())
    log_info("Warning: instance name '%s' is mysqld-compatible.",
             (const char *) instance_name->str);

399
  if (instance->complete_initialization())
400
  {
unknown's avatar
unknown committed
401
    log_error("Can not complete initialization of instance (name: '%s').",
402 403 404 405 406 407 408 409
              (const char *) instance_name->str);
    delete instance;
    return ER_OUT_OF_RESOURCES;
    /* TODO: return more appropriate error code in this case. */
  }

  if (add_instance(instance))
  {
unknown's avatar
unknown committed
410
    log_error("Can not register instance (name: '%s').",
411 412 413 414 415
              (const char *) instance_name->str);
    delete instance;
    return ER_OUT_OF_RESOURCES;
  }

unknown's avatar
unknown committed
416
  return 0;
417 418 419
}


unknown's avatar
unknown committed
420 421 422 423 424 425
/**
  Return a pointer to the instance or NULL, if there is no such instance.

  MT-NOTE: Instance Map must be locked before calling the operation.
*/

426 427
Instance * Instance_map::find(const LEX_STRING *name)
{
428
  return (Instance *) hash_search(&hash, (uchar *) name->str, name->length);
429 430 431
}


unknown's avatar
unknown committed
432 433 434 435
/**
  Init instances command line arguments after all options have been loaded.
*/

436 437 438 439 440 441 442 443 444 445
bool Instance_map::complete_initialization()
{
  bool mysqld_found;

  /* Complete initialization of all registered instances. */

  for (uint i= 0; i < hash.records; ++i)
  {
    Instance *instance= (Instance *) hash_element(&hash, i);

446
    if (instance->complete_initialization())
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
      return TRUE;
  }

  /* That's all if we are runnning in an ordinary mode. */

  if (!Options::Main::mysqld_safe_compatible)
    return FALSE;

  /* In mysqld-compatible mode we must ensure that there 'mysqld' instance. */

  mysqld_found= find(&Instance::DFLT_INSTANCE_NAME) != NULL;

  if (mysqld_found)
    return FALSE;

  if (create_instance(&Instance::DFLT_INSTANCE_NAME, NULL))
  {
unknown's avatar
unknown committed
464
    log_error("Can not create default instance.");
465 466 467 468 469 470 471 472 473 474 475 476 477 478
    return TRUE;
  }

  switch (create_instance_in_file(&Instance::DFLT_INSTANCE_NAME, NULL))
  {
  case 0:
  case ER_CONF_FILE_DOES_NOT_EXIST:
    /*
      Continue if the instance has been added to the config file
      successfully, or the config file just does not exist.
    */
    break;

  default:
unknown's avatar
unknown committed
479
    log_error("Can not add default instance to the config file.");
480 481 482 483 484 485 486 487 488 489

    Instance *instance= find(&Instance::DFLT_INSTANCE_NAME);

    if (instance)
      remove_instance(instance); /* instance is deleted here. */

    return TRUE;
  }

  return FALSE;
490 491 492
}


unknown's avatar
unknown committed
493 494 495 496
/**
  Load options from config files and create appropriate instance
  structures.
*/
497 498 499

int Instance_map::load()
{
500 501 502 503 504
  int argc= 1;
  /* this is a dummy variable for search_option_files() */
  uint args_used= 0;
  const char *argv_options[3];
  char **argv= (char **) &argv_options;
505
  char defaults_file_arg[FN_REFLEN];
506

507 508
  /* the name of the program may be orbitrary here in fact */
  argv_options[0]= "mysqlmanager";
509 510 511 512 513 514 515 516 517 518 519

  /*
    If the option file was forced by the user when starting
    the IM with --defaults-file=xxxx, make sure it is also
    passed as --defaults-file, not only as Options::config_file.
    This is important for option files given with relative path:
    e.g. --defaults-file=my.cnf.
    Otherwise my_search_option_files will treat "my.cnf" as a group
    name and start looking for files named "my.cnf.cnf" in all
    default dirs. Which is not what we want.
  */
520
  if (Options::Main::is_forced_default_file)
521 522
  {
    snprintf(defaults_file_arg, FN_REFLEN, "--defaults-file=%s",
523
             Options::Main::config_file);
524 525 526 527 528 529 530 531

    argv_options[1]= defaults_file_arg;
    argv_options[2]= '\0';

    argc= 2;
  }
  else
    argv_options[1]= '\0';
532

533 534 535 536
  /*
    If the routine failed, we'll simply fallback to defaults in
    complete_initialization().
  */
537
  if (my_search_option_files(Options::Main::config_file, &argc,
538
                             (char ***) &argv, &args_used,
539 540
                             process_option, (void*) this,
                             Options::default_directories))
unknown's avatar
unknown committed
541
    log_info("Falling back to compiled-in defaults.");
542

543
  return complete_initialization();
544
}
unknown's avatar
unknown committed
545 546


unknown's avatar
unknown committed
547 548 549
/*************************************************************************
  {{{ Instance_map::Iterator implementation.
*************************************************************************/
unknown's avatar
unknown committed
550

unknown's avatar
unknown committed
551
void Instance_map::Iterator::go_to_first()
unknown's avatar
unknown committed
552 553 554 555 556
{
  current_instance=0;
}


unknown's avatar
unknown committed
557
Instance *Instance_map::Iterator::next()
unknown's avatar
unknown committed
558
{
unknown's avatar
unknown committed
559 560
  if (current_instance < instance_map->hash.records)
    return (Instance *) hash_element(&instance_map->hash, current_instance++);
unknown's avatar
unknown committed
561 562

  return NULL;
unknown's avatar
unknown committed
563 564
}

unknown's avatar
unknown committed
565 566 567
/*************************************************************************
  }}}
*************************************************************************/
568 569


unknown's avatar
unknown committed
570
/**
571 572
  Create a new configuration section for mysqld-instance in the config file.

573
  SYNOPSIS
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
    create_instance_in_file()
    instance_name       mysqld-instance name
    options             options for the new mysqld-instance

  RETURN
    0     On success
    ER_CONF_FILE_DOES_NOT_EXIST     If config file does not exist
    ER_ACCESS_OPTION_FILE           If config file is not writable or some I/O
                                    error ocurred during writing configuration
*/

int create_instance_in_file(const LEX_STRING *instance_name,
                            const Named_value_arr *options)
{
  File cnf_file;

  if (my_access(Options::Main::config_file, W_OK))
  {
unknown's avatar
unknown committed
592
    log_error("Configuration file (%s) does not exist.",
593 594 595 596 597 598 599 600
              (const char *) Options::Main::config_file);
    return ER_CONF_FILE_DOES_NOT_EXIST;
  }

  cnf_file= my_open(Options::Main::config_file, O_WRONLY | O_APPEND, MYF(0));

  if (cnf_file <= 0)
  {
unknown's avatar
unknown committed
601
    log_error("Can not open configuration file (%s): %s.",
602 603 604 605 606
              (const char *) Options::Main::config_file,
              (const char *) strerror(errno));
    return ER_ACCESS_OPTION_FILE;
  }

607 608 609
  if (my_write(cnf_file, (uchar*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP)) ||
      my_write(cnf_file, (uchar*)"[", 1, MYF(MY_NABP)) ||
      my_write(cnf_file, (uchar*)instance_name->str, instance_name->length,
610
               MYF(MY_NABP)) ||
611 612
      my_write(cnf_file, (uchar*)"]", 1,   MYF(MY_NABP)) ||
      my_write(cnf_file, (uchar*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP)))
613
  {
unknown's avatar
unknown committed
614
    log_error("Can not write to configuration file (%s): %s.",
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
              (const char *) Options::Main::config_file,
              (const char *) strerror(errno));
    my_close(cnf_file, MYF(0));
    return ER_ACCESS_OPTION_FILE;
  }

  for (int i= 0; options && i < options->get_size(); ++i)
  {
    char option_str[MAX_OPTION_STR_LEN];
    char *ptr;
    int option_str_len;
    Named_value option= options->get_element(i);

    ptr= strxnmov(option_str, MAX_OPTION_LEN + 1, option.get_name(), NullS);

    if (option.get_value()[0])
      ptr= strxnmov(ptr, MAX_OPTION_LEN + 2, "=", option.get_value(), NullS);

    option_str_len= ptr - option_str;

635 636
    if (my_write(cnf_file, (uchar*)option_str, option_str_len, MYF(MY_NABP)) ||
        my_write(cnf_file, (uchar*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP)))
637
    {
unknown's avatar
unknown committed
638
      log_error("Can not write to configuration file (%s): %s.",
639 640 641 642 643 644 645 646 647 648 649
                (const char *) Options::Main::config_file,
                (const char *) strerror(errno));
      my_close(cnf_file, MYF(0));
      return ER_ACCESS_OPTION_FILE;
    }
  }

  my_close(cnf_file, MYF(0));

  return 0;
}