Skip to content

File databases_file.c

File List > base > databases_file.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */


/*
 * databases_file.c - Parsing the database directory file
 *
 */

#ident "$Id$"

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>

#if defined(WINDOWS)
#include <io.h>
#else /* WINDOWS */
#include <unistd.h>
#endif /* WINDOWS */
#include <assert.h>

#include "porting.h"

#include "chartype.h"
#include "error_manager.h"
#include "databases_file.h"
#include "boot.h"
#include "connection_defs.h"
#include "memory_alloc.h"
#include "environment_variable.h"
#include "system_parameter.h"

#if defined(WINDOWS)
#include "misc_string.h"
#include "wintcp.h"
#endif /* WINDOWS */
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"


/* conservative upper bound of a line in databases.txt */
#define CFG_MAX_LINE 4096

static const char CFG_HOST_SEPARATOR = ':';

static char *cfg_next_char (char *str_p);
static char *cfg_next_line (char *str_p);
static char *cfg_pop_token (char *str_p, char **token_p);
static char *cfg_pop_linetoken (char *str_p, char **token_p);
static void cfg_get_directory_filename (char *buffer, int *local);

static int cfg_ensure_directory_write (void);
static FILE *cfg_open_directory_file (bool write_flag);

static char **cfg_copy_hosts (const char **host_array, int *num_hosts);
static const char *cfg_pop_host (const char *host_list, char *buffer, int *length);
static bool cfg_host_exists (char *host_list, char *hostname, int num_items);

/* PARSING UTILITIES */
/*
 * cfg_next_char() - Advances the given pointer until a non-whitespace character
 *               or the end of the string are encountered.
 *    return: char *
 *    str_p(in): buffer pointer
 */
static char *
cfg_next_char (char *str_p)
{
  char *p;

  p = str_p;
  while (char_isspace ((int) *p) && *p != '\0')
    {
      p++;
    }

  return (p);
}

/*
 * cfg_next_line()
 *    return: char *
 *    str_p(in): buffer pointer
 */
static char *
cfg_next_line (char *str_p)
{
  char *p;

  p = str_p;
  while (!char_iseol ((int) *p) && *p != '\0')
    {
      p++;
    }
  while (char_iseol ((int) *p) && *p != '\0')
    {
      p++;
    }

  return (p);
}

/*
 * cfg_pop_token() - This looks in the buffer for the next token which is define as
 *               a string of characters surrounded by whitespace
 *    return: char
 *    str_p(in): buffer with tokens
 *    token_p(in/out): returned next token string
 *
 *    Note : When found the token characters are copied into a new string
 *           and returned.
 *           The pointer to the first character following the new token in
 *           the buffer is returned.
 */
static char *
cfg_pop_token (char *str_p, char **token_p)
{
  char *p, *end, *token = NULL;
  int length;

  token = NULL;
  p = str_p;
  while (char_isspace ((int) *p) && *p != '\0')
    {
      p++;
    }
  end = p;
  while (!char_isspace ((int) *end) && *end != '\0')
    {
      end++;
    }

  length = (int) (end - p);
  if (length > 0)
    {
      token = (char *) malloc (length + 1);
      if (token != NULL)
    {
      strncpy (token, p, length);
      token[length] = '\0';
    }
    }

  *token_p = token;
  return (end);
}

/*
 * cfg_pop_linetoken()
 *    return: char *
 *    str_p(in):
 *    token_p(in/out):
 */
static char *
cfg_pop_linetoken (char *str_p, char **token_p)
{
  char *p, *end, *token = NULL;
  int length;

  if (str_p == NULL || char_iseol ((int) *str_p))
    {
      *token_p = NULL;
      return str_p;
    }
  token = NULL;
  p = str_p;
  while (char_isspace ((int) *p) && !char_iseol ((int) *p) && *p != '\0')
    {
      p++;
    }
  end = p;

  while (!char_isspace ((int) *end) && *end != '\0')
    {
      end++;
    }

  length = (int) (end - p);
  if (length > 0)
    {
      token = (char *) malloc (length + 1);
      if (token != NULL)
    {
      strncpy (token, p, length);
      token[length] = '\0';
    }
    }

  *token_p = token;
  return (end);
}

/*
 * cfg_get_directory_filename() - Finds the full pathname of the database
 *                                directory file.
 *    return: none
 *    buffer(in): character buffer to hold the full path name
 *    local(out): flag set if the file was assumed to be local
 */
static void
cfg_get_directory_filename (char *buffer, int *local)
{
  const char *env_name;

  *local = 0;
  env_name = envvar_get (DATABASES_ENVNAME);
  if (env_name == NULL || strlen (env_name) == 0)
    {
      sprintf (buffer, DATABASES_FILENAME);
      *local = 1;
    }
  else
    {
      if (env_name[strlen (env_name) - 1] == '/')
    {
      sprintf (buffer, "%s%s", env_name, DATABASES_FILENAME);
    }
      else
    {
      sprintf (buffer, "%s/%s", env_name, DATABASES_FILENAME);
    }
    }
}

/*
 * cfg_os_working_directory() - Returns the current working directory
 *                              in a buffer.
 *                              Buffer returned is static and must be
 *                              copied immediately.
 *    return: char *
 */
char *
cfg_os_working_directory (void)
{
  char *return_str = NULL;

#if defined(WINDOWS)
  static char working_dir[PATH_MAX];
  return_str = _fullpath (working_dir, ".", PATH_MAX);
#else /* WINDOWS */
  return_str = getenv ("PWD");
#endif /* WINDOWS */

  return return_str;
}

/*
 * cfg_maycreate_get_directory_filename()
 *    return: char *
 *    buffer(in):
 */
char *
cfg_maycreate_get_directory_filename (char *buffer)
{
  int local_ignore;
  FILE *file_p = NULL;

  cfg_get_directory_filename (buffer, &local_ignore);
  if ((file_p = fopen (buffer, "a+")) == NULL)
    {
#if !defined(CS_MODE)
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CFG_NO_WRITE_ACCESS, 1, buffer);
#else /* !CS_MODE */
      er_set_with_oserror (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_CFG_NO_WRITE_ACCESS, 1, buffer);
#endif /* !CS_MODE */
      return NULL;
    }
  fclose (file_p);
  return buffer;
}

/*
 * cfg_ensure_directory_write() - Make sure that we can get write access to
 *                                the directory file, if not abort
 *                                the operation.
 *    return: non-zero if directory file is writable
 */
static int
cfg_ensure_directory_write (void)
{
  char filename[PATH_MAX];
  FILE *file_p = NULL;
  int local, status;

  status = 0;

  cfg_get_directory_filename (filename, &local);
  file_p = fopen (filename, "a+");
  if (file_p == NULL)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CFG_NO_WRITE_ACCESS, 1, filename);
    }
  else
    {
      status = 1;
      fclose (file_p);
    }

  return (status);
}

/*
 * cfg_open_directory_file()
 *    return: file pointer
 *    write_flag(in): set to open for write
 */
static FILE *
cfg_open_directory_file (bool write_flag)
{
  char filename[PATH_MAX];
  FILE *file_p = NULL;
  int local;

  cfg_get_directory_filename (filename, &local);

  if (write_flag)
    {
#if defined(WINDOWS)
      file_p = fopen (filename, "wbc"); /* write binary commit */
#else /* WINDOWS */
      file_p = fopen (filename, "w");
#endif /* WINDOWS */
    }
  else
    {
      file_p = fopen (filename, "r");
    }
  if (file_p == NULL)
    {
      if (write_flag)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CFG_NO_WRITE_ACCESS, 1, filename);
    }
      else
    {
      /* Only standalone and server will update the database location file */
#if !defined(CS_MODE)
      /* no readable file, try to create one */
      file_p = fopen (filename, "r+");
      if (file_p == NULL)
        {
          er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CFG_NO_WRITE_ACCESS, 1, filename);
        }
#else /* !CS_MODE */
      file_p = NULL;
#endif /* !CS_MODE */
    }
    }
  return (file_p);
}

/*
 * cfg_read_directory() - This reads the database directory file and returns
 *                        a list of descriptors
 *    return: non-zero for success
 *    info_p(out): pointer to returned list of descriptors
 *    write_flag(in): flag indicating write intention
 *
 * Note: The write_flag flag should be set if the file is to be
 *    modified.  This will ensure that write access is available before some
 *    potentially expensive operation like database creation is performed.
 *    A list of hosts where the database could exist is obtained from
 *    cfg_get_hosts() and attached to the DB_INFO structure.
 *
 *    However, cfg_read_directory() has a potential problem that
 *    the file lock is released. The file lock acquired through io_mount()
 *    is released when cfg_read_directory() opens and closes the file.
 */
int
cfg_read_directory (DB_INFO ** info_p, bool write_flag)
{
  char line[CFG_MAX_LINE];
  FILE *file_p = NULL;
  DB_INFO *databases, *last, *db;
  char *str = NULL;
  char *primary_host = NULL;
  int error_code = ER_FAILED;
  char *ha_node_list = NULL;

  databases = last = NULL;

#if defined(SERVER_MODE)
  if (!HA_DISABLED () && prm_get_string_value (PRM_ID_HA_NODE_LIST))
    {
      str = strchr (prm_get_string_value (PRM_ID_HA_NODE_LIST), '@');
      ha_node_list = (str) ? str + 1 : NULL;
    }
#endif

  if (!write_flag || cfg_ensure_directory_write ())
    {
      file_p = cfg_open_directory_file (false);
      if (file_p != NULL)
    {
      while (fgets (line, CFG_MAX_LINE - 1, file_p) != NULL)
        {
          str = cfg_next_char (line);
          if (*str != '\0' && *str != '#')
        {
          db = (DB_INFO *) malloc (sizeof (DB_INFO));
          if (db == NULL)
            {
              if (databases != NULL)
            {
              cfg_free_directory (databases);
            }
              *info_p = NULL;
              er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (DB_INFO));
              return ER_OUT_OF_VIRTUAL_MEMORY;
            }
          db->next = NULL;
          str = cfg_pop_token (str, &db->name);
          str = cfg_pop_token (str, &db->pathname);
          str = cfg_pop_token (str, &primary_host);
          if (ha_node_list)
            {
              db->hosts = cfg_get_hosts (ha_node_list, &db->num_hosts, false);
            }
          else
            {
              db->hosts = cfg_get_hosts (primary_host, &db->num_hosts, false);
            }
          if (primary_host != NULL)
            {
              free_and_init (primary_host);
            }

          str = cfg_pop_token (str, &db->logpath);
          str = cfg_pop_token (str, &db->lobpath);

          if (databases == NULL)
            {
              databases = db;
            }
          else
            {
              last->next = db;
            }
          last = db;
          if (db->name == NULL || db->pathname == NULL || db->hosts == NULL || db->logpath == NULL
              /* skip to check above to support backward compatibility || db->lobpath == NULL */ )
            {
              er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CFG_INVALID_DATABASES, 1, DATABASES_FILENAME);
              if (databases != NULL)
            {
              cfg_free_directory (databases);
            }
              *info_p = NULL;
              return ER_CFG_INVALID_DATABASES;
            }
        }
        }
      fclose (file_p);
      error_code = NO_ERROR;
    }
    }
  *info_p = databases;
  return (error_code);
}

/*
 * cfg_read_directory_ex() - This provides same functionality of
 *                           cfg_read_directory().
 *    return: non-zero for success
 *    vdes(in): file descriptor
 *    info_p(out): pointer to returned list of descriptors
 *    write_flag(in): flag indicating write intention
 *
 *    Note: However it does not open/close the file, the file lock is
 *          preserved.
 */
int
cfg_read_directory_ex (int vdes, DB_INFO ** info_p, bool write_flag)
{
  char *line = NULL;
  DB_INFO *databases, *last, *db;
  char *str = NULL;
  char *primary_host = NULL;
  struct stat stat_buffer;
  int error_code = ER_FAILED;

#if defined(DONT_USE_MANDATORY_LOCK_IN_WINDOWS)
  return cfg_read_directory (info_p, write_flag);
#endif /* DONT_USE_MANDATORY_LOCK_IN_WINDOWS */

  databases = last = NULL;

  if (lseek (vdes, 0L, SEEK_SET) == 0L)
    {
      fstat (vdes, &stat_buffer);
      line = (char *) malloc (stat_buffer.st_size + 1);
      if (line == NULL)
    {
      *info_p = NULL;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) (stat_buffer.st_size + 1));
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }
      read (vdes, line, (unsigned int) stat_buffer.st_size);
      line[stat_buffer.st_size] = '\0';
      str = cfg_next_char (line);
      while (*str != '\0')
    {
      if (*str != '#')
        {
          if ((db = (DB_INFO *) malloc (sizeof (DB_INFO))) == NULL)
        {
          if (databases != NULL)
            {
              cfg_free_directory (databases);
            }
          *info_p = NULL;
          free_and_init (line);

          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (DB_INFO));
          return ER_OUT_OF_VIRTUAL_MEMORY;
        }

          db->next = NULL;
          str = cfg_pop_linetoken (str, &db->name);
          str = cfg_pop_linetoken (str, &db->pathname);
          str = cfg_pop_linetoken (str, &primary_host);
          db->hosts = cfg_get_hosts (primary_host, &db->num_hosts, false);
          if (primary_host != NULL)
        {
          free_and_init (primary_host);
        }
          str = cfg_pop_linetoken (str, &db->logpath);
          str = cfg_pop_linetoken (str, &db->lobpath);

          if (databases == NULL)
        {
          databases = db;
        }
          else
        {
          last->next = db;
        }
          last = db;
          if (db->name == NULL || db->pathname == NULL || db->hosts == NULL || db->logpath == NULL
          /* skip to check above to support backward compatibility || db->lobpath == NULL */ )

        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CFG_INVALID_DATABASES, 1, DATABASES_FILENAME);
          if (databases != NULL)
            {
              cfg_free_directory (databases);
            }
          *info_p = NULL;
          free_and_init (line);
          return ER_CFG_INVALID_DATABASES;
        }
        }
      str = cfg_next_line (str);
      str = cfg_next_char (str);
    }
      error_code = NO_ERROR;
      free_and_init (line);
    }
  *info_p = databases;
  return (error_code);
}

/*
 * cfg_write_directory() - This writes a list of database descriptors to
 *                         the accessible config file. only the first host,
 *                         (primary host), is written to file.
 *    return: none
 *    databases(in): list of database descriptors
 *
 * Note: However, cfg_write_directory() has a potential problem that
 *       the file lock is released. The file lock acquired through io_mount()
 *       is released when cfg_write_directory() opens and closes the file.
 */
void
cfg_write_directory (const DB_INFO * databases)
{
  FILE *file_p;
  const DB_INFO *db_info_p;
#if !defined(WINDOWS)
  sigset_t new_mask, old_mask;
#endif /* !WINDOWS */

  file_p = cfg_open_directory_file (true);
  if (file_p != NULL)
    {

#if !defined(WINDOWS)
      sigfillset (&new_mask);
      sigdelset (&new_mask, SIGINT);
      sigdelset (&new_mask, SIGQUIT);
      sigdelset (&new_mask, SIGTERM);
      sigdelset (&new_mask, SIGHUP);
      sigdelset (&new_mask, SIGABRT);
      sigprocmask (SIG_SETMASK, &new_mask, &old_mask);
#endif /* !WINDOWS */

      fprintf (file_p, "#db-name\tvol-path\t\tdb-host\t\tlog-path\t\tlob-base-path\n");
      for (db_info_p = databases; db_info_p != NULL; db_info_p = db_info_p->next)
    {
      bool t = (strlen (db_info_p->name) < 8);
#if defined(WINDOWS)
      char short_path[256];
      GetShortPathName (db_info_p->pathname, short_path, 256);
      fprintf (file_p, "%s%s\t%s\t", db_info_p->name, (t ? "\t" : ""), short_path);
#else /* WINDOWS */
      fprintf (file_p, "%s%s\t%s\t", db_info_p->name, (t ? "\t" : ""), db_info_p->pathname);
#endif /* WINDOWS */

      if (db_info_p->hosts != NULL && *(db_info_p->hosts) != NULL)
        {
          char **array = db_info_p->hosts;
          fprintf (file_p, "%s", *array++);
          while (*array != NULL)
        {
          fprintf (file_p, ":%s", *array++);
        }
        }
      else
        {
          fprintf (file_p, "localhost");
        }

      if (db_info_p->logpath != NULL)
        {
#if defined(WINDOWS)
          GetShortPathName (db_info_p->logpath, short_path, 256);
          fprintf (file_p, "\t%s ", short_path);
#else /* WINDOWS */
          fprintf (file_p, "\t%s ", db_info_p->logpath);
#endif /* WINDOWS */
        }

      if (db_info_p->lobpath != NULL)
        {
          fprintf (file_p, "\t%s ", db_info_p->lobpath);
        }

      fprintf (file_p, "\n");
    }
      fflush (file_p);
      fclose (file_p);

#if !defined(WINDOWS)
      sigprocmask (SIG_SETMASK, &old_mask, NULL);
#endif /* !WINDOWS */
    }
}

/*
 * cfg_write_directory_ex() - This writes a list of database descriptors
 *                            to the accessible config file.
 *                            only the first host, (primary host),
 *                            is written to file.
 *    return: none
 *    vdes(in): file descriptor
 *    databases(in): list of database descriptors
 *
 * Note : However, cfg_write_directory() has a potential problem that
 *        the file lock is released. The file lock acquired through io_mount()
 *        is released when cfg_write_directory() opens and closes the file.
 */
void
cfg_write_directory_ex (int vdes, const DB_INFO * databases)
{
  char line[LINE_MAX], *s;
  const DB_INFO *db_info_p;
  int n;
#if !defined(WINDOWS)
  sigset_t new_mask, old_mask;
#endif /* !WINDOWS */

#if defined(DONT_USE_MANDATORY_LOCK_IN_WINDOWS)
  cfg_read_directory (info_p, true);
  return;
#endif /* DONT_USE_MANDATORY_LOCK_IN_WINDOWS */

#if !defined(WINDOWS)
  sigfillset (&new_mask);
  sigdelset (&new_mask, SIGINT);
  sigdelset (&new_mask, SIGQUIT);
  sigdelset (&new_mask, SIGTERM);
  sigdelset (&new_mask, SIGHUP);
  sigdelset (&new_mask, SIGABRT);
  sigprocmask (SIG_SETMASK, &new_mask, &old_mask);
#endif /* !WINDOWS */

  lseek (vdes, 0L, SEEK_SET);
  n = sprintf (line, "#db-name\tvol-path\t\tdb-host\t\tlog-path\t\tlob-base-path\n");
  write (vdes, line, n);
  for (db_info_p = databases; db_info_p != NULL; db_info_p = db_info_p->next)
    {
      bool t = (strlen (db_info_p->name) < 8);
      s = line;
      s += sprintf (s, "%s%s\t%s\t", db_info_p->name, (t ? "\t" : ""), db_info_p->pathname);

      if (db_info_p->hosts != NULL && *(db_info_p->hosts) != NULL)
    {
      char **array = db_info_p->hosts;
      s += sprintf (s, "%s", *array++);
      while (*array != NULL)
        {
          s += sprintf (s, ":%s", *array++);
        }
    }
      else
    {
      s += sprintf (s, "localhost");
    }
      if (db_info_p->logpath)
    {
      s += sprintf (s, "\t%s", db_info_p->logpath);
    }
      if (db_info_p->lobpath)
    {
      s += sprintf (s, "\t%s", db_info_p->lobpath);
    }
      s += sprintf (s, "\n");
      n = (int) (s - line);
      write (vdes, line, n);
    }

  ftruncate (vdes, lseek (vdes, 0L, SEEK_CUR));

#if !defined(WINDOWS)
  sigprocmask (SIG_SETMASK, &old_mask, NULL);
#endif /* !WINDOWS */
}

/*
 * cfg_free_directory() - Frees a list of database descriptors.
 *    return: none
 *    databases(in): list of databases
 */
void
cfg_free_directory (DB_INFO * databases)
{
  DB_INFO *db_info_p, *next_info_p;

  for (db_info_p = databases, next_info_p = NULL; db_info_p != NULL; db_info_p = next_info_p)
    {

      next_info_p = db_info_p->next;

      if (db_info_p->name != NULL)
    {
      free_and_init (db_info_p->name);
    }
      if (db_info_p->pathname != NULL)
    {
      free_and_init (db_info_p->pathname);
    }
      if (db_info_p->hosts != NULL)
    {
      cfg_free_hosts (db_info_p->hosts);
    }
      if (db_info_p->logpath != NULL)
    {
      free_and_init (db_info_p->logpath);
    }
      if (db_info_p->lobpath != NULL)
    {
      free_and_init (db_info_p->lobpath);
    }
      free_and_init (db_info_p);
    }
}

#if defined(CUBRID_DEBUG)
/*
 * cfg_dump_directory() - debug function.
 *    return: none
 *    databases(in): list of database descriptors
 */
void
cfg_dump_directory (const DB_INFO * databases)
{
  const DB_INFO *db_info_p;
  int i = 0;

  for (db_info_p = databases; db_info_p != NULL; db_info_p = db_info_p->next)
    {
      fprintf (stdout, "%s %s ", db_info_p->name, db_info_p->pathname);
      if (db_info_p->hosts != NULL)
    {
      i = 0;
      while ((*((db_info_p->hosts) + i)) != NULL)
        {
          fprintf (stdout, "%s", (*(db_info_p->hosts + i)));
          i++;
          if ((*((db_info_p->hosts) + i)) != NULL)
        {
          fprintf (stdout, ",");
        }
          else
        {
          fprintf (stdout, " ");
        }
        }
    }
      if (db_info_p->logpath != NULL)
    {
      fprintf (stdout, "%s ", db_info_p->logpath);
    }
      if (db_info_p->lobpath != NULL)
    {
      fprintf (stdout, "%s", db_info_p->lobpath);
    }
      fprintf (stdout, "\n");
    }
}
#endif

/*
 * cfg_update_db() - Updates pathname, logpath, and creates a new host list
 *                   with the hostname sent in used as the primary host.
 *    return: none
 *    db_info_p(in): database descriptor
 *    path(in): directory path name
 *    logpath(in): log path name
 *    host(in): server host name
 */
void
cfg_update_db (DB_INFO * db_info_p, const char *path, const char *logpath, const char *lobpath, const char *host)
{
  char **ptr_p;

  if (db_info_p != NULL)
    {
      if (path != NULL)
    {
      if (db_info_p->pathname != NULL)
        {
          free_and_init (db_info_p->pathname);
        }
      db_info_p->pathname = strdup (path);
    }

      if (logpath != NULL)
    {
      if (db_info_p->logpath != NULL)
        {
          free_and_init (db_info_p->logpath);
        }
      db_info_p->logpath = strdup (logpath);
    }

      if (lobpath != NULL)
    {
      if (db_info_p->lobpath != NULL)
        {
          free_and_init (db_info_p->lobpath);
        }
      db_info_p->lobpath = strdup (lobpath);
    }

      if (host != NULL)
    {
      ptr_p = cfg_get_hosts (host, &db_info_p->num_hosts, false);
      if (db_info_p->hosts != NULL)
        {
          cfg_free_hosts (db_info_p->hosts);
        }
      db_info_p->hosts = ptr_p;
    }
    }
}

/*
 * cfg_new_db() - creates a new DB_INFO structure. If the hosts array sent
 *                in is NULL, an array with the local host as primary host
 *                is created.
 *    return: new database descriptor
 *    name(in): database name
 *    path(in):
 *    logpath(in): log path
 *    lobpath(in): lob path
 *    hosts(in):
 */
DB_INFO *
cfg_new_db (const char *name, const char *path, const char *logpath, const char *lobpath, const char **hosts)
{
  DB_INFO *db_info_p;

  db_info_p = (DB_INFO *) malloc (DB_SIZEOF (DB_INFO));
  if (db_info_p == NULL)
    {
      goto error;
    }

  db_info_p->pathname = NULL;
  db_info_p->logpath = NULL;
  db_info_p->lobpath = NULL;
  db_info_p->hosts = NULL;
  db_info_p->num_hosts = 0;

  db_info_p->name = strdup (name);
  if (db_info_p->name == NULL)
    goto error;

  if (path == NULL)
    {
      path = cfg_os_working_directory ();
    }

  /*
   * if NULL hosts is passed in, then create a new host list, with the
   * local host as the primary.
   */
  if (hosts == NULL)
    {
      db_info_p->hosts = cfg_get_hosts (NULL, &db_info_p->num_hosts, true);
    }
  else
    {
      db_info_p->hosts = cfg_copy_hosts (hosts, &db_info_p->num_hosts);
    }

  db_info_p->pathname = (path != NULL) ? strdup (path) : NULL;
  if (db_info_p->pathname == NULL)
    {
      goto error;
    }

  if (logpath == NULL)
    {
      db_info_p->logpath = strdup (db_info_p->pathname);
    }
  else
    {
      db_info_p->logpath = strdup (logpath);
    }

  if (db_info_p->logpath == NULL)
    {
      goto error;
    }

  if (lobpath != NULL)
    {
      db_info_p->lobpath = strdup (lobpath);
    }

  db_info_p->next = NULL;

  return (db_info_p);

error:
  if (db_info_p != NULL)
    {
      if (db_info_p->name != NULL)
    {
      free_and_init (db_info_p->name);
    }
      if (db_info_p->pathname != NULL)
    {
      free_and_init (db_info_p->pathname);
    }
      if (db_info_p->logpath != NULL)
    {
      free_and_init (db_info_p->logpath);
    }
      if (db_info_p->lobpath != NULL)
    {
      free_and_init (db_info_p->lobpath);
    }
      if (db_info_p->hosts != NULL)
    {
      free_and_init (db_info_p->hosts);
    }

      free_and_init (db_info_p);
    }

  return NULL;
}

/*
 * cfg_find_db_list()
 *    return: database descriptor
 *    dir(in): descriptor list
 *    name(in): database name
 */
DB_INFO *
cfg_find_db_list (DB_INFO * db_info_list_p, const char *name)
{
  DB_INFO *db_info_p, *found_info_p;

  found_info_p = NULL;
  for (db_info_p = db_info_list_p; db_info_p != NULL && found_info_p == NULL; db_info_p = db_info_p->next)
    {
      if (strcmp (db_info_p->name, name) == 0)
    {
      found_info_p = db_info_p;
    }
    }

  return (found_info_p);
}

/*
 * cfg_add_db() - Creates a new hosts array and DB_INFO structure and pops
 *                the structure into the dir linked-list.
 *    return: new database descriptor
 *    dir(in/out): pointer to directory list
 *    name(in): database name
 *    path(in): directory path
 *    logpath(in): log path
 *    lobpath(in): lob path
 */
DB_INFO *
cfg_add_db (DB_INFO ** dir, const char *name, const char *path, const char *logpath, const char *lobpath,
        const char *host)
{
  DB_INFO *db_info_p;

  if (host != NULL)
    {
      const char *hosts[2];
      hosts[0] = host;
      hosts[1] = NULL;
      db_info_p = cfg_new_db (name, path, logpath, lobpath, hosts);
    }
  else
    {
      db_info_p = cfg_new_db (name, path, logpath, lobpath, NULL);
    }

  if (db_info_p != NULL)
    {
      db_info_p->next = *dir;
      *dir = db_info_p;
    }

  return (db_info_p);
}

/*
 * cfg_find_db()
 *    return: database descriptor
 *    db_name(in): database name
 */
DB_INFO *
cfg_find_db (const char *db_name)
{
  DB_INFO *dir_info_p, *db_info_p;

  db_info_p = NULL;

  if (cfg_read_directory (&dir_info_p, false) == NO_ERROR)
    {
      if (dir_info_p == NULL)
    {
#if !defined(CS_MODE)
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CFG_FIND_DATABASE, 2, db_name, DATABASES_FILENAME);
#else /* !CS_MODE */
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_CFG_FIND_DATABASE, 2, db_name, DATABASES_FILENAME);
#endif /* !CS_MODE */
    }
      else
    {
      db_info_p = cfg_find_db_list (dir_info_p, db_name);
      if (db_info_p == NULL)
        {
#if !defined(CS_MODE)
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CFG_FIND_DATABASE, 2, db_name, DATABASES_FILENAME);
#else /* !CS_MODE */
          er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_CFG_FIND_DATABASE, 2, db_name, DATABASES_FILENAME);
#endif /* !CS_MODE */
        }
      else
        {
          if (db_info_p->hosts != NULL)
        {
          db_info_p = cfg_new_db (db_info_p->name, db_info_p->pathname, db_info_p->logpath, db_info_p->lobpath,
                      (const char **) db_info_p->hosts);
        }
          else
        {
          db_info_p = cfg_new_db (db_info_p->name, db_info_p->pathname, db_info_p->logpath, db_info_p->lobpath,
                      NULL);
        }
        }
      cfg_free_directory (dir_info_p);
    }
    }
  else
    {
#if !defined(CS_MODE)
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CFG_READ_DATABASES, 1, DATABASES_FILENAME);
#else /* !CS_MODE */
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_CFG_READ_DATABASES, 1, DATABASES_FILENAME);
#endif /* !CS_MODE */
    }
  return (db_info_p);
}

/*
 * cfg_delete_db() - Deletes a database entry from a directory list.
 *    return: if success is return true, otherwise return false
 *    dir_info_p(in): pointer to directory list
 *    name(in): database name
 */
bool
cfg_delete_db (DB_INFO ** dir_info_p, const char *name)
{
  DB_INFO *db_info_p, *prev_info_p, *found_info_p;
  bool success = false;

  for (db_info_p = *dir_info_p, found_info_p = NULL, prev_info_p = NULL; db_info_p != NULL && found_info_p == NULL;
       db_info_p = db_info_p->next)
    {
      if (strcmp (db_info_p->name, name) == 0)
    {
      found_info_p = db_info_p;
    }
      else
    {
      prev_info_p = db_info_p;
    }
    }
  if (found_info_p != NULL)
    {
      if (prev_info_p == NULL)
    {
      *dir_info_p = found_info_p->next;
    }
      else
    {
      prev_info_p->next = found_info_p->next;
    }
      found_info_p->next = NULL;
      cfg_free_directory (found_info_p);
      success = true;
    }
  return (success);
}

/*
 * cfg_get_hosts() - assigns value count, to the number of hosts in array.
 *                   cfg_free_hosts should be called to free up memory used
 *                   by the array
 *    return: pointer to an array containing the host list.
 *    dbname(in): database name to connect to. (this is for future use)
 *    prim_host(in): primary hostname for database.
 *    count(in): count will contain the number of host found after processing
 *    include_local_host(in): boolean indicating if the local host name should
 *                            be prepended to the list.
 */
char **
cfg_get_hosts (const char *prim_host, int *count, bool include_local_host)
{
  /* pointers to array of hosts, to return */
  char **host_array;
  char *hosts_data;
  int i;

  *count = 0;

  /*
   * get a clean host list, i.e., null fields and duplicate hosts removed.
   * prim_host will be prepended to the list, and the local host will
   * will be appended if include_local_host is true.
   */
  hosts_data = cfg_create_host_list (prim_host, include_local_host, count);
  if (*count == 0 || hosts_data == NULL)
    {
      return NULL;
    }

  /* create a list of pointers to point to the hosts in hosts_data */
  host_array = (char **) calloc (*count + 1, sizeof (char **));
  if (host_array == NULL)
    {
      free_and_init (hosts_data);
      return NULL;
    }
  for (i = 0; i < *count; i++)
    {
      host_array[i] = hosts_data;
      hosts_data = strchr (hosts_data, CFG_HOST_SEPARATOR);
      if (hosts_data == NULL)
    {
      break;
    }

      *hosts_data++ = '\0';
    }

  return host_array;
}

/*
 * cfg_free_hosts() - free_and_init's host_array and *host_array if not NULL
 *    return: none
 *    host_array(in): array of pointers to buffer containing hostnames.
 */
void
cfg_free_hosts (char **host_array)
{
  if (host_array != NULL)
    {
      if (*host_array != NULL)
    {
      free_and_init (*host_array);
    }
      free_and_init (host_array);
    }
}

/*
 * cfg_pop_host() - pointer to next character in string
 *    return: returns pointer to next character in string
 *    host_list(in): String containing list of hosts
 *    buffer(in): Buffer to pop in hostname.
 *    length(out): Returns the length of the hostname, popped.
 *             -1 indicates that the hostname was too long > MAXHOSTLEN,
 *             and buffer is empty
 *
 *    Note : Sending in a NULL buffer will mean the function will assign length
 *           to the length of the next host in the list only.
 */
static const char *
cfg_pop_host (const char *host_list, char *buffer, int *length)
{
  int current_host_length = 0;
  const char *start, *host;

  host = host_list;

  if (buffer != NULL)
    {
      *buffer = '\0';
    }

  /* Ignore initial spaces/field separators in list */

  while (((char_isspace (*host)) || (*host == CFG_HOST_SEPARATOR)) && (*host != '\0'))
    {
      ++host;
    }

  /* Read in next host, and make a note of its length */

  start = host;
  current_host_length = 0;

  while ((*host != CFG_HOST_SEPARATOR) && (!char_isspace (*host)) && (*host != '\0'))
    {
      host++;
      current_host_length++;
    }

  /*
   * Increment count if we have a valid hostname, and we have reached,
   * a field separator, a space or end of line.
   * Copy host into buffer supplied.
   */
  if (((*host == CFG_HOST_SEPARATOR) || (char_isspace (*host)) || (*host == '\0')) && (current_host_length != 0))
    {
      /* Note buffer is empty if length of host is greater than CUB_MAXHOSTNAMELEN) */
      if ((buffer != NULL) && (current_host_length <= CUB_MAXHOSTNAMELEN))
    {
      strncpy (buffer, start, current_host_length);
      *(buffer + current_host_length) = '\0';
    }
    }

  if (current_host_length >= CUB_MAXHOSTNAMELEN)
    {
      *length = (-1);
    }
  else
    {
      *length = current_host_length;
    }
  return (host);
}

/*
 * cfg_host_exists() - Traverses the host_list to locate hostname.
 *    return: true if item exists.
 *    host_list(in): Pointer to array holding host names
 *    hostname(in): host name to search for.
 *    num_items(in): The number of items currently in the list
 */
static bool
cfg_host_exists (char *host_list, char *hostname, int num_items)
{
  char *current_host;
  char *next_sep;
  int i = 0, len, hostname_len;

  hostname_len = (int) strlen (hostname);

  current_host = host_list;
  while ((current_host != NULL) && (i < num_items))
    {
      next_sep = strchr (current_host, CFG_HOST_SEPARATOR);
      if (next_sep == NULL)
    {
      if (strcmp (current_host, hostname) == 0)
        {
          return true;
        }
      else
        {
          return false;
        }
    }
      else
    {
      len = CAST_STRLEN (next_sep - current_host);

      if (len == hostname_len && strncmp (current_host, hostname, len) == 0)
        {
          return true;
        }
    }

      i++;
      current_host = next_sep + 1;
    }
  return false;
}               /* cfg_host_exists() */

/*
 * cfg_copy_hosts() - a copy of the array holding hostnames.
 *    return: returns a pointer to a copy of the array holding hostnames.
 *    host_array(in): Pointer to array holding host names
 */
static char **
cfg_copy_hosts (const char **host_array, int *num_hosts)
{
  char **new_array;
  const char *host;
  char *buffer;
  int num;
  size_t buffer_size;

  assert (host_array != NULL);
  assert (num_hosts != NULL);

  *num_hosts = 0;
  buffer_size = 0;
  /* count the number of hosts array and calculate the size of buffer */
  for (num = 0, host = host_array[0]; host; num++, host = host_array[num])
    {
      buffer_size += strlen (host) + 1;
    }
  if (num == 0)
    {
      return NULL;
    }

  /* copy the hosts array into the buffer and make new pointer array */
  buffer = (char *) malloc (buffer_size);
  if (buffer == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buffer_size);
      return NULL;
    }

  new_array = (char **) calloc (num + 1, sizeof (char **));
  if (new_array == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, ((num + 1) * sizeof (char **)));
      free_and_init (buffer);
      return NULL;
    }

  for (num = 0, host = host_array[0]; host; num++, host = host_array[num])
    {
      strcpy (buffer, host);
      new_array[num] = buffer;
      buffer += strlen (host) + 1;
    }

  if (host_array[0] == NULL)
    {
      free_and_init (buffer);
    }

  *num_hosts = num;

  return new_array;
}

/*
 * cfg_create_host_lsit()
 *    return: returns a pointer to a copy of the array holding hostnames
 *    primary_host_name(in): String containing primary host name.
 *    include_local_host(in): Flag indicating if the local hostname should be
 *                       included in the list.
 *    count(out): Pointer to integer which will be assigned the number of hosts
 *             in list.
 *
 *    Note : Null or empty hostnames are ignored, and duplicates are not
 *           included in the list.
 */
char *
cfg_create_host_list (const char *primary_host_name, bool include_local_host, int *count)
{
  int host_list_length, host_length, host_count;
  const char *str_ptr;
  char *full_host_list, *host_ptr;
  char local_host[CUB_MAXHOSTNAMELEN + 1];

  assert (count != NULL);

  host_list_length = 0;
  /* include local host to list if required */
  *local_host = '\0';
  if (include_local_host)
    {
#if 0               /* use Unix-domain socket for localhost */
      if (GETHOSTNAME (local_host, CUB_MAXHOSTNAMELEN) == 0)
    {
      local_host[CUB_MAXHOSTNAMELEN] = '\0';
      host_list_length += strlen (local_host) + 1;
    }
#else
      strcpy (local_host, "localhost");
      host_list_length += (int) strlen (local_host) + 1;
#endif
    }
  /* check the given primary hosts list */
  if (primary_host_name != NULL && *primary_host_name != '\0')
    {
      host_list_length += (int) strlen (primary_host_name) + 1;
    }

  /* get the hosts list from parameters */
  if (prm_get_string_value (PRM_ID_CFG_DB_HOSTS) != NULL && *prm_get_string_value (PRM_ID_CFG_DB_HOSTS) != '\0')
    {
      host_list_length += (int) strlen (prm_get_string_value (PRM_ID_CFG_DB_HOSTS)) + 1;
    }

  /*
   * concatenate host lists with separator
   * count the number of hosts in the list
   * ignore null and space
   * removing duplicates
   */
  if (host_list_length == 0)
    {
      return NULL;
    }
  full_host_list = (char *) malloc (host_list_length + 1);
  if (full_host_list == NULL)
    {
      return NULL;
    }
  host_count = 0;
  host_ptr = full_host_list;
  *host_ptr = '\0';
  /* add the given primary hosts to the list */
  if (primary_host_name != NULL && *primary_host_name != '\0')
    {
      str_ptr = primary_host_name;
      while (*str_ptr != '\0')
    {
      str_ptr = cfg_pop_host (str_ptr, host_ptr, &host_length);
      if (host_length > 0)
        {
          if (!cfg_host_exists (full_host_list, host_ptr, host_count))
        {
          host_count++;
          host_ptr += host_length;
          *host_ptr++ = CFG_HOST_SEPARATOR;
        }
          *host_ptr = '\0';
        }
    }
    }
  /* append the hosts from the parameter to the list */
  if (prm_get_string_value (PRM_ID_CFG_DB_HOSTS) != NULL && *prm_get_string_value (PRM_ID_CFG_DB_HOSTS) != '\0')
    {
      str_ptr = prm_get_string_value (PRM_ID_CFG_DB_HOSTS);
      while (*str_ptr != '\0')
    {
      str_ptr = cfg_pop_host (str_ptr, host_ptr, &host_length);
      if (host_length > 0)
        {
          if (!cfg_host_exists (full_host_list, host_ptr, host_count))
        {
          host_count++;
          host_ptr += host_length;
          *host_ptr++ = CFG_HOST_SEPARATOR;
        }
          *host_ptr = '\0';
        }
    }
    }
  /* append local host if exists */
  if (*local_host != '\0')
    {
      if (!cfg_host_exists (full_host_list, local_host, host_count))
    {
      strcpy (host_ptr, local_host);
      host_ptr += strlen (local_host);
      host_count++;
    }
    }

  /* remove last separator */
  host_ptr--;
  if (*host_ptr == CFG_HOST_SEPARATOR)
    {
      *host_ptr = '\0';
    }

  /* return host list and counter */
  if (host_count != 0)
    {
      *count = host_count;
      return full_host_list;
    }

  /* no valid host name */
  free_and_init (full_host_list);
  return NULL;
}