Skip to content

File tcp.c

File List > connection > tcp.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.
 *
 */


/*
 * tcp.c - Open a TCP connection
 */

#ident "$Id$"

#include "config.h"

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#if !defined(WINDOWS)
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#endif
#if defined(AIX)
#include <netinet/if_ether.h>
#include <net/if_dl.h>
#endif /* AIX */
#include <arpa/inet.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#if defined(SOLARIS)
#include <sys/filio.h>
#endif /* SOLARIS */
#if defined(sun)
#include <sys/sockio.h>
#endif /* sun */
#if defined(LINUX)
#include <sys/stat.h>
#endif /* LINUX */
#include <netinet/tcp.h>
#include <assert.h>

#include "porting.h"
#if defined(SERVER_MODE)
#include "connection_error.h"
#include "connection_sr.h"
#endif /* SERVER_MODE */
#include "error_manager.h"
#include "system_parameter.h"
#include "environment_variable.h"
#include "tcp.h"
#include "host_lookup.h"

#ifndef HAVE_GETHOSTBYNAME_R
#include <pthread.h>

static pthread_mutex_t gethostbyname_lock = PTHREAD_MUTEX_INITIALIZER;
#endif /* HAVE_GETHOSTBYNAME_R */
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

#define HOST_ID_ARRAY_SIZE 8    /* size of the host_id string */
#define TCP_MIN_NUM_RETRIES 3
#define CONTROLLEN (sizeof(struct cmsghdr) + sizeof(int))
#if !defined(INADDR_NONE)
#define INADDR_NONE 0xffffffff
#endif /* !INADDR_NONE */

static const int css_Maximum_server_count = 1000;

#if !defined (WINDOWS)
#define SET_NONBLOCKING(fd) { \
      int flags = fcntl (fd, F_GETFL); \
      flags |= O_NONBLOCK; \
      fcntl (fd, F_SETFL, flags); \
}
#endif /* !WINDOWS */

static void css_sockopt (SOCKET sd);
static int css_sockaddr (const char *host, int port, struct sockaddr *saddr, socklen_t * slen);
static int css_fd_error (SOCKET fd);

/*
 * Put the canonical name of the current host in name out variable.
 * The result is null-terminated if namelen is large enough for the full name and the terminator.
 *   return: 0 if success, or error
 *   name(out): buffer for name
 *   namelen(in): max buffer size
 */
int
css_gethostname (char *name, size_t namelen)
{
  if (namelen <= 0)
    {
      return ER_FAILED;
    }

  size_t namelen_ = (size_t) namelen;

  char hostname[namelen_];
  hostname[namelen_ - 1] = '\0';
  if (gethostname (hostname, namelen_) < 0)
    {
      return ER_FAILED;
    }
  else
    {
      strncpy (name, hostname, namelen);
    }

  return NO_ERROR;
}

char *
css_get_master_domain_path (void)
{
  static char path[PATH_MAX];
  static bool need_init = true;

  if (need_init)
    {
      const char *cubrid_tmp = envvar_get ("TMP");

      if (cubrid_tmp == NULL || cubrid_tmp[0] == '\0')
    {
      cubrid_tmp = "/tmp";
    }
      snprintf (path, PATH_MAX, "%s/%s%d", cubrid_tmp, envvar_prefix (), prm_get_master_port_id ());
      need_init = false;
    }

  return path;
}

/*
 * css_tcp_client_open () -
 *   return:
 *   host(in):
 *   port(in):
 */
SOCKET
css_tcp_client_open (const char *host, int port)
{
  SOCKET fd;

  fd = css_tcp_client_open_with_retry (host, port, true);
  if (IS_INVALID_SOCKET (fd))
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_CANNOT_CONNECT_TO_MASTER, 1, host);
    }
  return fd;
}

static void
css_sockopt (SOCKET sd)
{
  int bool_value = 1;
  int prm_value;

  prm_value = prm_get_integer_value (PRM_ID_TCP_RCVBUF_SIZE);
  if (prm_value > 0)
    {
      setsockopt (sd, SOL_SOCKET, SO_RCVBUF, &prm_value, sizeof (int));
    }

  prm_value = prm_get_integer_value (PRM_ID_TCP_SNDBUF_SIZE);
  if (prm_value > 0)
    {
      setsockopt (sd, SOL_SOCKET, SO_SNDBUF, &prm_value, sizeof (int));
    }

  if (prm_get_bool_value (PRM_ID_TCP_NODELAY))
    {
      setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (const char *) &bool_value, sizeof (bool_value));
    }

  if (prm_get_bool_value (PRM_ID_TCP_KEEPALIVE))
    {
      setsockopt (sd, SOL_SOCKET, SO_KEEPALIVE, (const char *) &bool_value, sizeof (bool_value));
    }
}

/*
 * css_hostname_to_ip()
 *   return:
 *   host(in):
 *   ip_addr(out):
 */
int
css_hostname_to_ip (const char *host, unsigned char *ip_addr)
{
  in_addr_t in_addr;

  /*
   * First try to convert to the host name as a dotted-decimal number.
   * Only if that fails do we call gethostbyname.
   */
  in_addr = inet_addr (host);
  if (in_addr != INADDR_NONE)
    {
      memcpy ((void *) ip_addr, (void *) &in_addr, sizeof (in_addr));
      return NO_ERROR;
    }
  else
    {
#ifdef HAVE_GETHOSTBYNAME_R
#if defined (HAVE_GETHOSTBYNAME_R_GLIBC)
      struct hostent *hp, hent;
      int herr;
      char buf[1024];

      if (gethostbyname_r_uhost (host, &hent, buf, sizeof (buf), &hp, &herr) != 0 || hp == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_UNABLE_TO_FIND_HOSTNAME, 2, host, HOSTS_FILE);
      return ER_BO_UNABLE_TO_FIND_HOSTNAME;
    }
      memcpy ((void *) ip_addr, (void *) hent.h_addr, hent.h_length);
#elif defined (HAVE_GETHOSTBYNAME_R_SOLARIS)
      struct hostent hent;
      int herr;
      char buf[1024];

      if (gethostbyname_r_uhost (host, &hent, buf, sizeof (buf), &herr) == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_UNABLE_TO_FIND_HOSTNAME, 2, host, HOSTS_FILE);
      return ER_BO_UNABLE_TO_FIND_HOSTNAME;
    }
      memcpy ((void *) ip_addr, (void *) hent.h_addr, hent.h_length);
#elif defined (HAVE_GETHOSTBYNAME_R_HOSTENT_DATA)
      struct hostent hent;
      struct hostent_data ht_data;

      if (gethostbyname_r_uhost (host, &hent, &ht_data) == -1)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_UNABLE_TO_FIND_HOSTNAME, 2, host, HOSTS_FILE);
      return ER_BO_UNABLE_TO_FIND_HOSTNAME;
    }
      memcpy ((void *) ip_addr, (void *) hent.h_addr, hent.h_length);
#else
#error "HAVE_GETHOSTBYNAME_R"
#endif
#else /* HAVE_GETHOSTBYNAME_R */
      struct hostent *hp;

      pthread_mutex_lock (&gethostbyname_lock);
      hp = gethostbyname_uhost (host);
      if (hp == NULL)
    {
      pthread_mutex_unlock (&gethostbyname_lock);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_UNABLE_TO_FIND_HOSTNAME, 2, host, HOSTS_FILE);
      return ER_BO_UNABLE_TO_FIND_HOSTNAME;
    }
      memcpy ((void *) ip_addr, (void *) hp->h_addr, hp->h_length);
      pthread_mutex_unlock (&gethostbyname_lock);
#endif /* !HAVE_GETHOSTBYNAME_R */
    }

  return NO_ERROR;
}

/*
 * css_sockaddr()
 *   return:
 *   host(in):
 *   port(in):
 *   saddr(out):
 *   slen(out):
 */
static int
css_sockaddr (const char *host, int port, struct sockaddr *saddr, socklen_t * slen)
{
  struct sockaddr_in tcp_saddr;
  struct sockaddr_un unix_saddr;
  in_addr_t in_addr;

  /*
   * Construct address for TCP socket
   */
  memset ((void *) &tcp_saddr, 0, sizeof (tcp_saddr));
  tcp_saddr.sin_family = AF_INET;
  tcp_saddr.sin_port = htons (port);

  /*
   * First try to convert to the host name as a dotten-decimal number.
   * Only if that fails do we call gethostbyname.
   */
  in_addr = inet_addr (host);
  if (in_addr != INADDR_NONE)
    {
      memcpy ((void *) &tcp_saddr.sin_addr, (void *) &in_addr, sizeof (in_addr));
    }
  else
    {
#ifdef HAVE_GETHOSTBYNAME_R
#if defined (HAVE_GETHOSTBYNAME_R_GLIBC)
      struct hostent *hp, hent;
      int herr;
      char buf[1024];

      if (gethostbyname_r_uhost (host, &hent, buf, sizeof (buf), &hp, &herr) != 0 || hp == NULL)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_HOST_NAME_ERROR, 2, host, HOSTS_FILE);
      return INVALID_SOCKET;
    }
      memcpy ((void *) &tcp_saddr.sin_addr, (void *) hent.h_addr, hent.h_length);
#elif defined (HAVE_GETHOSTBYNAME_R_SOLARIS)
      struct hostent hent;
      int herr;
      char buf[1024];

      if (gethostbyname_r_uhost (host, &hent, buf, sizeof (buf), &herr) == NULL)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_HOST_NAME_ERROR, 2, host, HOSTS_FILE);
      return INVALID_SOCKET;
    }
      memcpy ((void *) &tcp_saddr.sin_addr, (void *) hent.h_addr, hent.h_length);
#elif defined (HAVE_GETHOSTBYNAME_R_HOSTENT_DATA)
      struct hostent hent;
      struct hostent_data ht_data;

      if (gethostbyname_r_uhost (host, &hent, &ht_data) == -1)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_HOST_NAME_ERROR, 2, host, HOSTS_FILE);
      return INVALID_SOCKET;
    }
      memcpy ((void *) &tcp_saddr.sin_addr, (void *) hent.h_addr, hent.h_length);
#else
#error "HAVE_GETHOSTBYNAME_R"
#endif
#else /* HAVE_GETHOSTBYNAME_R */
      struct hostent *hp;
      int r;

      r = pthread_mutex_lock (&gethostbyname_lock);
      hp = gethostbyname_uhost (host);
      if (hp == NULL)
    {
      pthread_mutex_unlock (&gethostbyname_lock);
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_HOST_NAME_ERROR, 2, host, HOSTS_FILE);
      return INVALID_SOCKET;
    }
      memcpy ((void *) &tcp_saddr.sin_addr, (void *) hp->h_addr, hp->h_length);
      pthread_mutex_unlock (&gethostbyname_lock);
#endif /* !HAVE_GETHOSTBYNAME_R */
    }

  /*
   * Compare with the TCP address with localhost.
   * If it is, use Unix domain socket rather than TCP for the performance
   */
  memcpy ((void *) &in_addr, (void *) &tcp_saddr.sin_addr, sizeof (in_addr));
  if (in_addr == inet_addr ("127.0.0.1"))
    {
      memset ((void *) &unix_saddr, 0, sizeof (unix_saddr));
      unix_saddr.sun_family = AF_UNIX;
      strncpy (unix_saddr.sun_path, css_get_master_domain_path (), sizeof (unix_saddr.sun_path) - 1);
      *slen = sizeof (unix_saddr);
      memcpy ((void *) saddr, (void *) &unix_saddr, *slen);
    }
  else
    {
      *slen = sizeof (tcp_saddr);
      memcpy ((void *) saddr, (void *) &tcp_saddr, *slen);
    }

  return NO_ERROR;
}

/*
 * css_tcp_client_open_with_retry () -
 *   return:
 *   host(in):
 *   port(in):
 *   willretry(in):
 */
SOCKET
css_tcp_client_open_with_retry (const char *host, int port, bool will_retry)
{
  SOCKET sd = INVALID_SOCKET;
  struct sockaddr *saddr;
  socklen_t slen;
  time_t start_contime;
  int nsecs, sleep_nsecs = 1;
  int success, num_retries = 0;
  union
  {
    struct sockaddr_in in;
    struct sockaddr_un un;
  } saddr_buf;

  assert (host != NULL);
  assert (port > 0);

  saddr = (struct sockaddr *) &saddr_buf;
  if (css_sockaddr (host, port, saddr, &slen) != NO_ERROR)
    return INVALID_SOCKET;

  start_contime = time (NULL);
  do
    {
      sd = socket (saddr->sa_family, SOCK_STREAM, 0);
      if (IS_INVALID_SOCKET (sd))
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_CANNOT_CREATE_SOCKET, 0);
      return INVALID_SOCKET;
    }
      else
    {
      css_sockopt (sd);
    }

      /*
       * If we get an ECONNREFUSED from the connect, we close the socket, and
       * retry again. This is needed since the backlog parameter of the SUN
       * machine is too small (See man page of listen...see BUG section).
       * To avoid a possible infinite loop, we only retry five times
       */
    again_eintr:
      if ((success = connect (sd, saddr, slen)) == 0)
    {
      /* connection is established successfully */
      break;
    }
      if (errno == EINTR)
    {
      goto again_eintr;
    }

      if ((errno == ECONNREFUSED || errno == ETIMEDOUT) && will_retry == true)
    {
      nsecs = (int) difftime (time (NULL), start_contime);
      nsecs -= prm_get_integer_value (PRM_ID_TCP_CONNECTION_TIMEOUT);
      if (nsecs >= 0 && num_retries > TCP_MIN_NUM_RETRIES)
        {
          will_retry = false;
        }
      else
        {
          /*
           * Wait a little bit to change the load of the server.
           * Don't wait for more than 1/2 min or the timeout period
           */
          if (sleep_nsecs > 30)
        {
          sleep_nsecs = 30;
        }

          /*
           * Sleep only when we have not timed out. That is, when nsecs is
           * negative.
           */
          if (nsecs < 0 && sleep_nsecs > (-nsecs))
        {
          sleep_nsecs = -nsecs;
        }
          if (nsecs < 0)
        {
          (void) sleep (sleep_nsecs);
          sleep_nsecs *= 2; /* Go 1, 2, 4, 8, etc */
        }
          num_retries++;
        }
    }
      else
    {
      will_retry = false;   /* Don't retry */
    }

      /*
       * According to the Sun man page of connect & listen. When a connect
       * was forcefully rejected. The calling program must close the
       * socket descriptor, before another connect is retried.
       *
       * The server's host is probably overloaded. Sleep for a while, then
       * try again. We sleep a different number of seconds between 1 and 30
       * to avoid having the same situation with other processes that could
       * have reached the timeout/refuse connection.
       *
       * The sleep time is guessing that the server is not going to be
       * overloaded by connections in that amount of time.
       *
       * Similar things are suggested by R. Stevens Unix Network programming
       * book. See Remote Command execution example in Chapter 14
       *
       * See connect and listen MAN pages.
       */
      close (sd);
      sd = INVALID_SOCKET;
    }
  while (success < 0 && will_retry == true);

  if (success < 0)
    {
#if defined(CUBRID_DEBUG)
      er_log_debug (ARG_FILE_LINE, "css_tcp_client_open_with_retry:" "connection failed with retries %d errno %d\n",
            num_retries, errno);
#endif /* CUBRID_DEBUG */
      return INVALID_SOCKET;
    }

  return sd;
}

#if !defined (WINDOWS)
/*
 * css_tcp_client_open_with_timeout() -
 *   return: socket descriptor
 *   host(in): host name
 *   port(in): port no
 *   timeout(in): timeout in milli-seconds
 */
SOCKET
css_tcp_client_open_with_timeout (const char *host, int port, int timeout)
{
  SOCKET sd = -1;
  struct sockaddr *saddr;
  socklen_t slen;
  int n;
  struct pollfd po[1] = { {0, 0, 0} };
  union
  {
    struct sockaddr_in in;
    struct sockaddr_un un;
  } saddr_buf;

  assert (host != NULL);
  assert (port > 0);
  assert (timeout >= 0);

  saddr = (struct sockaddr *) &saddr_buf;
  if (css_sockaddr (host, port, saddr, &slen) != NO_ERROR)
    return INVALID_SOCKET;

  sd = socket (saddr->sa_family, SOCK_STREAM, 0);
  if (sd < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_CANNOT_CREATE_SOCKET, 0);
      return INVALID_SOCKET;
    }
  else
    {
      css_sockopt (sd);
      SET_NONBLOCKING (sd);
    }

again_eintr:
  n = connect (sd, saddr, slen);
  if (n == 0)
    {
      /* connection is established immediately */
      return sd;
    }
  if (errno == EINTR)
    {
      goto again_eintr;
    }

  if (errno != EINPROGRESS)
    {
      close (sd);
#if defined(CUBRID_DEBUG)
      er_log_debug (ARG_FILE_LINE, "css_tcp_client_open_with_timeout:" "connect failed with errno %d", errno);
#endif /* CUBRID_DEBUG */
      return INVALID_SOCKET;
    }

retry_poll:
  po[0].fd = sd;
  po[0].events = POLLOUT;
  po[0].revents = 0;
  n = poll (po, 1, timeout);
  if (n < 0)
    {
      if (errno == EINTR)
    {
      goto retry_poll;
    }

#if defined(CUBRID_DEBUG)
      er_log_debug (ARG_FILE_LINE, "css_tcp_client_open_with_timeout:" "poll failed errno %d", errno);
#endif /* CUBRID_DEBUG */
      close (sd);
      return INVALID_SOCKET;
    }
  else if (n == 0)
    {
      /* 0 means it timed out and no fd is changed */
      errno = ETIMEDOUT;
      close (sd);
#if defined(CUBRID_DEBUG)
      er_log_debug (ARG_FILE_LINE, "css_tcp_client_open_with_timeout:" "poll failed with timeout %d", timeout);
#endif /* CUBRID_DEBUG */
      return INVALID_SOCKET;
    }

  /* has connection been established? */
  slen = sizeof (n);
  if (getsockopt (sd, SOL_SOCKET, SO_ERROR, (void *) &n, &slen) < 0)
    {
#if defined(CUBRID_DEBUG)
      er_log_debug (ARG_FILE_LINE, "css_tcp_client_open_with_timeout:" "getsockopt failed errno %d", errno);
#endif /* CUBRID_DEBUG */
      close (sd);
      return INVALID_SOCKET;
    }
  if (n != 0)
    {
#if defined(CUBRID_DEBUG)
      er_log_debug (ARG_FILE_LINE, "css_tcp_client_open_with_timeout:" "connection failed errno %d", n);
#endif /* CUBRID_DEBUG */
      close (sd);
      return INVALID_SOCKET;
    }

  return sd;
}
#endif /* !WINDOWS */

/*
 * css_tcp_master_open () -
 *   return:
 *   port(in):
 *   sockfd(in):
 */
int
css_tcp_master_open (int port, SOCKET * sockfd)
{
  struct sockaddr_in tcp_srv_addr;  /* server's internet socket addr */
  struct sockaddr_un unix_srv_addr;
  int retry_count = 0;
  int reuseaddr_flag = 1;
  struct stat unix_socket_stat;

  /*
   * We have to create a socket ourselves and bind our well-known address to it.
   */

  memset ((void *) &tcp_srv_addr, 0, sizeof (tcp_srv_addr));
  tcp_srv_addr.sin_family = AF_INET;
  tcp_srv_addr.sin_addr.s_addr = htonl (INADDR_ANY);

  if (port > 0)
    {
      tcp_srv_addr.sin_port = htons (port);
    }
  else
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_PORT_ERROR, 0);
      return ERR_CSS_TCP_PORT_ERROR;
    }

  unix_srv_addr.sun_family = AF_UNIX;
  strncpy (unix_srv_addr.sun_path, css_get_master_domain_path (), sizeof (unix_srv_addr.sun_path) - 1);

  /*
   * Create the socket and Bind our local address so that any
   * client may send to us.
   */

retry:
  /*
   * Allow the new master to rebind the CUBRID port even if there are
   * clients with open connections from previous masters.
   */

  sockfd[0] = socket (AF_INET, SOCK_STREAM, 0);
  if (IS_INVALID_SOCKET (sockfd[0]))
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_CANNOT_CREATE_STREAM, 0);
      return ERR_CSS_TCP_CANNOT_CREATE_STREAM;
    }

  if (setsockopt (sockfd[0], SOL_SOCKET, SO_REUSEADDR, (char *) &reuseaddr_flag, sizeof (reuseaddr_flag)) < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_BIND_ABORT, 0);
      css_shutdown_socket (sockfd[0]);
      return ERR_CSS_TCP_BIND_ABORT;
    }

  if (bind (sockfd[0], (struct sockaddr *) &tcp_srv_addr, sizeof (tcp_srv_addr)) < 0)
    {
      if (errno == EADDRINUSE && retry_count <= 5)
    {
      retry_count++;
      css_shutdown_socket (sockfd[0]);
      (void) sleep (1);
      goto retry;
    }
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_BIND_ABORT, 0);
      css_shutdown_socket (sockfd[0]);
      return ERR_CSS_TCP_BIND_ABORT;
    }

  /*
   * And set the listen parameter, telling the system that we're
   * ready to accept incoming connection requests.
   */
  if (listen (sockfd[0], css_Maximum_server_count) != 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_ACCEPT_ERROR, 0);
      css_shutdown_socket (sockfd[0]);
      return ERR_CSS_TCP_ACCEPT_ERROR;
    }

  /*
   * Since the master now forks /M drivers, make sure we do a close
   * on exec on the socket.
   */
#if defined(HPUX)
  fcntl (sockfd[0], F_SETFD, 1);
#else /* HPUX */
  ioctl (sockfd[0], FIOCLEX, 0);
#endif /* HPUX */

  if (access (css_get_master_domain_path (), F_OK) == 0)
    {
      if (stat (css_get_master_domain_path (), &unix_socket_stat) == -1)
    {
      /* stat() failed */
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_UNIX_DOMAIN_SOCKET_FILE_EXIST, 1,
                   css_get_master_domain_path ());
      css_shutdown_socket (sockfd[0]);
      return ERR_CSS_UNIX_DOMAIN_SOCKET_FILE_EXIST;
    }
      if (!S_ISSOCK (unix_socket_stat.st_mode))
    {
      /* not socket file */
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_UNIX_DOMAIN_SOCKET_FILE_EXIST, 1,
          css_get_master_domain_path ());
      css_shutdown_socket (sockfd[0]);
      return ERR_CSS_UNIX_DOMAIN_SOCKET_FILE_EXIST;
    }
      if (unlink (css_get_master_domain_path ()) == -1)
    {
      /* unlink() failed */
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_UNIX_DOMAIN_SOCKET_FILE_EXIST, 1,
                   css_get_master_domain_path ());
      css_shutdown_socket (sockfd[0]);
      return ERR_CSS_UNIX_DOMAIN_SOCKET_FILE_EXIST;
    }
    }

retry2:

  sockfd[1] = socket (AF_UNIX, SOCK_STREAM, 0);
  if (IS_INVALID_SOCKET (sockfd[1]))
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_CANNOT_CREATE_STREAM, 0);
      css_shutdown_socket (sockfd[0]);
      return ERR_CSS_TCP_CANNOT_CREATE_STREAM;
    }

  if (setsockopt (sockfd[1], SOL_SOCKET, SO_REUSEADDR, (char *) &reuseaddr_flag, sizeof (reuseaddr_flag)) < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_BIND_ABORT, 0);
      css_shutdown_socket (sockfd[0]);
      css_shutdown_socket (sockfd[1]);
      return ERR_CSS_TCP_BIND_ABORT;
    }

  if (bind (sockfd[1], (struct sockaddr *) &unix_srv_addr, sizeof (unix_srv_addr)) < 0)
    {
      if (errno == EADDRINUSE && retry_count <= 5)
    {
      retry_count++;
      css_shutdown_socket (sockfd[1]);
      (void) sleep (1);
      goto retry2;
    }
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_BIND_ABORT, 0);
      css_shutdown_socket (sockfd[0]);
      css_shutdown_socket (sockfd[1]);
      return ERR_CSS_TCP_BIND_ABORT;
    }

  if (listen (sockfd[1], css_Maximum_server_count) != 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_ACCEPT_ERROR, 0);
      css_shutdown_socket (sockfd[0]);
      css_shutdown_socket (sockfd[1]);
      return ERR_CSS_TCP_ACCEPT_ERROR;
    }

#if defined(HPUX)
  fcntl (sockfd[1], F_SETFD, 1);
#else /* HPUX */
  ioctl (sockfd[1], FIOCLEX, 0);
#endif /* HPUX */

  return NO_ERROR;
}

/*
 * css_master_accept() - master accept of a request from a client
 *   return:
 *   sockfd(in):
 */
SOCKET
css_master_accept (SOCKET sockfd)
{
  struct sockaddr sa;
  static SOCKET new_sockfd;
  socklen_t clilen;
  int boolean = 1;

  while (true)
    {
      clilen = sizeof (sa);
      new_sockfd = accept (sockfd, &sa, &clilen);

      if (IS_INVALID_SOCKET (new_sockfd))
    {
      if (errno == EINTR)
        {
          errno = 0;
          continue;
        }

      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_ACCEPT_ERROR, 0);
      if (errno == EMFILE)
        {
          er_log_debug (ARG_FILE_LINE,
                "failed to accept connection: too many open files in this process (EMFILE). current process fd limit may be insufficient.");
        }
      else if (errno == ENFILE)
        {
          er_log_debug (ARG_FILE_LINE,
                "failed to accept connection: system-wide file descriptor limit reached (ENFILE).");
        }

      return INVALID_SOCKET;
    }

      break;
    }

  if (sa.sa_family == AF_INET)
    {
      setsockopt (sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &boolean, sizeof (boolean));
    }

  return new_sockfd;
}

/*
 * css_tcp_setup_server_datagram() - server datagram open support
 *   return:
 *   pathname(in):
 *   sockfd(in):
 *
 * Note: This will let the master server open a unix domain socket to the
 *       server to pass internet domain socket fds to the server. It returns
 *       the new socket fd
 */
bool
css_tcp_setup_server_datagram (const char *pathname, SOCKET * sockfd)
{
  int servlen;
  struct sockaddr_un serv_addr;

  *sockfd = socket (AF_UNIX, SOCK_STREAM, 0);
  if (IS_INVALID_SOCKET (*sockfd))
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_DATAGRAM_SOCKET, 0);
      return false;
    }

  memset ((void *) &serv_addr, 0, sizeof (serv_addr));
  serv_addr.sun_family = AF_UNIX;
  strncpy (serv_addr.sun_path, pathname, sizeof (serv_addr.sun_path) - 1);
  servlen = strlen (pathname) + 1 + sizeof (serv_addr.sun_family);

  if (bind (*sockfd, (struct sockaddr *) &serv_addr, servlen) < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_DATAGRAM_BIND, 0);
      return false;
    }

  /*
   * some operating system does not set the permission for unix domain socket.
   * so a server can't connect to master which is initiated by other user.
   */
#if defined(LINUX)
  chmod (pathname, S_IRWXU | S_IRWXG | S_IRWXO);
#endif /* LINUX */

  if (listen (*sockfd, 5) != 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_ACCEPT_ERROR, 0);
      return false;
    }

  return true;
}

/*
 * css_tcp_listen_server_datagram() - verifies that the pipe to the master has
 *                                    been setup properly
 *   return:
 *   sockfd(in):
 *   newfd(in):
 */
bool
css_tcp_listen_server_datagram (SOCKET sockfd, SOCKET * newfd)
{
  socklen_t clilen;
  struct sockaddr_un cli_addr;
  int boolean = 1;

  clilen = sizeof (cli_addr);

  while (true)
    {
      *newfd = accept (sockfd, (struct sockaddr *) &cli_addr, &clilen);
      if (IS_INVALID_SOCKET (*newfd))
    {
      if (errno == EINTR)
        {
          continue;
        }

      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_DATAGRAM_ACCEPT, 0);
      return false;
    }

      break;
    }

  setsockopt (sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &boolean, sizeof (boolean));

  return true;
}

/*
 * css_tcp_master_datagram() - master side of the datagram interface
 *   return:
 *   path_name(in):
 *   sockfd(in):
 */
bool
css_tcp_master_datagram (char *path_name, SOCKET * sockfd)
{
  int servlen;
  struct sockaddr_un serv_addr;
  bool will_retry = true;
  int success = -1;
  int num_retries = 0;

  memset ((void *) &serv_addr, 0, sizeof (serv_addr));
  serv_addr.sun_family = AF_UNIX;
  strncpy (serv_addr.sun_path, path_name, sizeof (serv_addr.sun_path) - 1);
  servlen = strlen (serv_addr.sun_path) + 1 + sizeof (serv_addr.sun_family);

  do
    {
      /*
       * If we get an ECONNREFUSED from the connect, we close the socket, and
       * retry again. This is needed since the backlog parameter of the SUN
       * machine is too small (See man page of listen...see BUG section).
       * To avoid a possible infinite loop, we only retry few times
       */
      *sockfd = socket (AF_UNIX, SOCK_STREAM, 0);
      if (IS_INVALID_SOCKET (*sockfd))
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_DATAGRAM_SOCKET, 0);
      return false;
    }

    again_eintr:
      success = connect (*sockfd, (struct sockaddr *) &serv_addr, servlen);
      if (success < 0)
    {
      if (errno == EINTR)
        {
          goto again_eintr;
        }

      if (errno == ECONNREFUSED || errno == ETIMEDOUT)
        {

          if (num_retries > TCP_MIN_NUM_RETRIES)
        {
          will_retry = false;
        }
          else
        {
          will_retry = true;
          num_retries++;
        }
        }
      else
        {
          will_retry = false;   /* Don't retry */
        }

      close (*sockfd);
      *sockfd = INVALID_SOCKET;
      (void) sleep (1);
      continue;
    }
      break;
    }
  while (success < 0 && will_retry == true);


  if (success < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_DATAGRAM_CONNECT, 0);
#if defined(CUBRID_DEBUG)
      er_log_debug (ARG_FILE_LINE, "Failed with number of retries = %d during connection\n", num_retries);
#endif /* CUBRID_DEBUG */
      return false;
    }

  if (num_retries > 0)
    {
#if defined(CUBRID_DEBUG)
      er_log_debug (ARG_FILE_LINE, "Connected after number of retries = %d\n", num_retries);
#endif /* CUBRID_DEBUG */
    }

  return true;
}

/*
 * css_open_new_socket_from_master() - the message interface to the master
 *                                     server
 *   return:
 *   fd(in):
 *   rid(in):
 */
SOCKET
css_open_new_socket_from_master (SOCKET fd, unsigned short *rid)
{
  union
  {
    struct cmsghdr hdr;
    char buf[CONTROLLEN];
  } cmsgbuf;
  struct cmsghdr *cmsg;
  SOCKET new_fd = INVALID_SOCKET;
  unsigned short req_id;
  struct msghdr msg;
  struct iovec iov;
  int pid;
  int rc;

  memset (&cmsgbuf, 0, sizeof (cmsgbuf));

  iov.iov_base = &req_id;
  iov.iov_len = sizeof (unsigned short);

  msg.msg_name = (caddr_t) NULL;
  msg.msg_namelen = 0;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_control = cmsgbuf.buf;
  msg.msg_controllen = sizeof (cmsgbuf.buf);

  rc = recvmsg (fd, &msg, 0);
  if (rc < 0)
    {
      TPRINTF ("recvmsg failed for fd = %d\n", rc);
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_RECVMSG, 0);
      return INVALID_SOCKET;
    }

  for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg))
    {
      if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS && cmsg->cmsg_len >= CMSG_LEN (sizeof (int)))
    {
      memcpy (&new_fd, CMSG_DATA (cmsg), sizeof (new_fd));
      break;
    }
    }

  if (IS_INVALID_SOCKET (new_fd))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_RECVMSG, 0);
      return INVALID_SOCKET;
    }

  *rid = ntohs (req_id);
  pid = getpid ();
  fcntl (new_fd, F_SETOWN, pid);
  css_sockopt (new_fd);

  return new_fd;
}

/*
 * css_transfer_fd() - send the fd of a new client request to a server
 *   return:
 *   server_fd(in):
 *   client_fd(in):
 *   rid(in):
 */
bool
css_transfer_fd (SOCKET server_fd, SOCKET client_fd, unsigned short rid, CSS_SERVER_REQUEST request_for_server)
{
  int request;
  unsigned short req_id;
  struct iovec iov[1];
  struct msghdr msg;
#if defined(LINUX) || defined(AIX)
  static struct cmsghdr *cmptr = NULL;
#endif /* LINUX || AIX */

  request = htonl (request_for_server);
  if (send (server_fd, (char *) &request, sizeof (int), 0) < 0)
    {
      /* Master->Server link down. remove old link, and try again. */
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_PASSING_FD, 0);
      return false;
    }
  req_id = htons (rid);

  /* Pass the fd to the server */
  iov[0].iov_base = (char *) &req_id;
  iov[0].iov_len = sizeof (unsigned short);
  msg.msg_iov = iov;
  msg.msg_iovlen = 1;
  msg.msg_namelen = 0;
  msg.msg_name = (caddr_t) 0;
#if !defined(LINUX) && !defined(AIX)
  msg.msg_accrights = (caddr_t) & client_fd;
  msg.msg_accrightslen = sizeof (client_fd);
#else /* LINUX || AIX */
  if (cmptr == NULL && (cmptr = (struct cmsghdr *) malloc (CONTROLLEN)) == NULL)
    {
      return false;
    }
  cmptr->cmsg_level = SOL_SOCKET;
  cmptr->cmsg_type = SCM_RIGHTS;
  cmptr->cmsg_len = CONTROLLEN;
  msg.msg_control = (void *) cmptr;
  msg.msg_controllen = CONTROLLEN;
  *(SOCKET *) CMSG_DATA (cmptr) = client_fd;
#endif /* LINUX || AIX */

  if (sendmsg (server_fd, &msg, 0) < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_PASSING_FD, 0);
      return false;
    }

  return true;
}

/*
 * css_shutdown_socket() -
 *   return:
 *   fd(in):
 */
void
css_shutdown_socket (SOCKET fd)
{
  int rc;

  if (!IS_INVALID_SOCKET (fd))
    {
    again_eintr:
      rc = close (fd);
      if (rc != 0)
    {
      if (errno == EINTR)
        {
          goto again_eintr;
        }
#if defined(CUBRID_DEBUG)
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
#endif /* CUBRID_DEBUG */
    }
    }
}

/*
 * css_gethostid() - returns the 32 bit host identifier for this machine
 *   return: 32 bit host identifier
 *
 * Note: Used for host key validation and some other purposes. Uses gethostid()
 *       on the Sun machines, for the rest, it tries to determine the IP
 *       address and encodes that as a 32 bit value.
 */
unsigned int
css_gethostid (void)
{
  unsigned int id = 0;

#ifdef HAVE_GETHOSTID
  id = (unsigned int) gethostid ();
#endif /* !HAVE_GETHOSTID */
  return id;
}

/*
 * css_open_server_connection_socket() -
 *   return:
 *
 * Note: Stub functions for the new-style connection protocol.
 *       Eventually should try to support these on non-NT platforms.
 *       See also wintcp.c
 */
int
css_open_server_connection_socket (void)
{
  return -1;
}

/*
 * css_close_server_connection_socket() -
 *   return; void
 *
 * Note: Stub functions for the new-style connection protocol.
 *       Eventually should try to support these on non-NT platforms.
 *       See also wintcp.c
 */
void
css_close_server_connection_socket (void)
{
}

/*
 * css_server_accept() -
 *   return:
 *   sockfd(in):
 *
 * Note: Stub functions for the new-style connection protocol.
 *       Eventually should try to support these on non-NT platforms.
 *       See also wintcp.c
 */
SOCKET
css_server_accept (SOCKET sockfd)
{
  return INVALID_SOCKET;
}

/*
 * css_fd_error() -
 *   return:
 *   fd(in):
 */
static int
css_fd_error (SOCKET fd)
{
  int rc = 0, count = 0;

again_:
  errno = 0;
  rc = ioctl (fd, FIONREAD, (caddr_t) & count);
  if (rc < 0)
    {
      if (errno == EINTR)
    {
      goto again_;
    }
      else
    {
      return rc;
    }
    }

  return count;
}

#if defined (ENABLE_UNUSED_FUNCTION)
/*
 * css_fd_down() -
 *   return:
 *   fd(in):
 */
int
css_fd_down (SOCKET fd)
{
  int error_code;
  socklen_t error_size = sizeof (socklen_t);
  int rc = 0;

  if (getsockopt (fd, SOL_SOCKET, SO_ERROR, (char *) &error_code, &error_size) >= 0)
    {
      if (error_code > 0 || css_fd_error (fd) <= 0)
    {
      rc = 1;
    }
    }

  return rc;
}
#endif

int
css_get_max_socket_fds (void)
{
  return (int) sysconf (_SC_OPEN_MAX);
}

#define SET_NONBLOCKING(fd) { \
      int flags = fcntl (fd, F_GETFL); \
      flags |= O_NONBLOCK; \
      fcntl (fd, F_SETFL, flags); \
}

/*
 * in_cksum - Checksum routine for Internet Protocol family headers
 */
static int
in_cksum (u_short * addr, int len)
{
  int nleft = len;
  u_short *w = addr;
  int sum = 0;
  u_short answer = 0;

  /*
   * Our algorithm is simple, using a 32 bit accumulator (sum), we add
   * sequential 16 bit words to it, and at the end, fold back all the
   * carry bits from the top 16 bits into the lower 16 bits.
   */
  while (nleft > 1)
    {
      sum += *w++;
      nleft -= 2;
    }

  /* mop up an odd byte, if necessary */
  if (nleft == 1)
    {
      *(u_char *) (&answer) = *(u_char *) w;
      sum += answer;
    }

  /* add back carry outs from top 16 bits to low 16 bits */
  sum = (sum >> 16) + (sum & 0xffff);   /* add hi 16 to low 16 */
  sum += (sum >> 16);       /* add carry */
  answer = ~sum;        /* truncate to 16 bits */
  return (answer);
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * css_ping() - ping implementation
 *              Send a ICMP_ECHO_REQUEST packet every second until either the
 *              timeout expires or a answer is received.
 *  return: 0 if an ICMP_ECHO_REPLY is received, otherwise errno
 *          ETIME if timed out
 *  sd(in): raw socket
 *  sa_send(in): peer address
 *  timeout(in): timeout in mili seconds
 */
int
css_ping (SOCKET sd, struct sockaddr_in *sa_send, int timeout)
{
  char sendbuf[1500], recvbuf[1500];
  struct icmp *icmp;
  struct ip *ip;
  struct sockaddr_in sa_recv;
  struct timeval tv;
  fd_set fds;
  uint16_t pid;
  int size, seq, n, hlen;
  size_t plen;
  socklen_t slen;

  /* icmp_id is a 16 bit data type, therefore down cast the pid */
  pid = (uint16_t) getpid ();
  size = 60 * 1024;
  setsockopt (sd, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size));

  /* make the socket non blocking so we can use select */
  SET_NONBLOCKING (sd);

  seq = 0;
  do
    {
      /* create the ICMP request */
      icmp = (struct icmp *) sendbuf;
      icmp->icmp_type = ICMP_ECHO;
      icmp->icmp_code = 0;
      icmp->icmp_id = htons (pid);
      icmp->icmp_seq = htons (seq);
      seq++;
      gettimeofday (&tv, NULL);
      memcpy (icmp->icmp_data, &tv, sizeof (tv));
      plen = ICMP_ADVLENMIN + sizeof (tv);
      icmp->icmp_cksum = 0;
      icmp->icmp_cksum = in_cksum ((u_short *) icmp, plen);
      /* send it */
      n = sendto (sd, sendbuf, plen, 0, (struct sockaddr *) sa_send, sizeof (struct sockaddr));
      if (n < 0 && errno != EINPROGRESS)
    {
      er_log_debug (ARG_FILE_LINE, "css_ping: can't send ICMP packet to %s errno %d\n",
            inet_ntoa (sa_send->sin_addr), errno);
      close (sd);
      return errno;
    }

      FD_ZERO (&fds);
      FD_SET (sd, &fds);
      /* wait one second */
      tv.tv_sec = timeout >= 1000 ? 1 : 0;
      tv.tv_usec = timeout >= 1000 ? 0 : timeout * 1000;
      n = select (sd + 1, &fds, NULL, NULL, &tv);
      if (n < 0 && errno != EINTR)
    {
      er_log_debug (ARG_FILE_LINE, "css_ping: select() errno %d\n", errno);
      close (sd);
      return errno;
    }
      if (n > 0)
    {
      /* something is available to read */
      slen = sizeof (sa_recv);
      n = recvfrom (sd, recvbuf, sizeof (recvbuf), 0, (struct sockaddr *) &sa_recv, &slen);
      ip = (struct ip *) recvbuf;
      hlen = (ip->ip_hl) << 2;
      icmp = (struct icmp *) (recvbuf + hlen);
      /*
       * We did received somthing, but is it what we were expecting?
       * Is is ICMP_ECHO_REPLY packet with the proper PID value?
       */
      if ((n - hlen >= 8) && icmp->icmp_type == ICMP_ECHOREPLY && (ntohs (icmp->icmp_id) == pid)
          && (sa_send->sin_addr.s_addr == sa_recv.sin_addr.s_addr))
        {
          /* er_log_debug (ARG_FILE_LINE, "css_ping: success\n"); */
          close (sd);
          return 0;
        }
    }
      timeout -= 1000;
    }
  while (timeout > 0);
  /* timed out */
  er_log_debug (ARG_FILE_LINE, "css_ping: timed out\n");
  close (sd);
  return ETIME;
}
#endif

/*
 * css_peer_alive() - check if the peer is alive or not
 *                    Try to ping the peer or connect to the port 7 (ECHO)
 *    return: true or false
 *    sd(in): socket descriptor connected to the peer
 *    timeout(in): timeout in mili seconds
 */
bool
css_peer_alive (SOCKET sd, int timeout)
{
  SOCKET nsd;
  int n;
  socklen_t size;
  struct sockaddr_in saddr;
  socklen_t slen;
  struct pollfd po[1];

#if defined (CS_MODE)
  er_log_debug (ARG_FILE_LINE, "The css_peer_alive() is calling.");
#endif

  slen = sizeof (saddr);
  if (getpeername (sd, (struct sockaddr *) &saddr, &slen) < 0)
    {
      er_log_debug (ARG_FILE_LINE, "css_peer_alive: returning errno %d from getpeername()\n", errno);
      return false;
    }

  /* if Unix domain socket, the peer(=local) is alive always */
  if (saddr.sin_family != AF_INET)
    {
      return true;
    }

#if 0               /* temporarily disabled */
  /* try to make raw socket to ping the peer */
  if ((nsd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0)
    {
      return (css_ping (nsd, &saddr, timeout) == 0);
    }
#endif
  /* failed to make a ICMP socket; try to connect to the port ECHO */
  if ((nsd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
    {
      er_log_debug (ARG_FILE_LINE, "css_peer_alive: errno %d from socket(SOCK_STREAM)\n", errno);
      return false;
    }

  /* make the socket non blocking so we can use select */
  SET_NONBLOCKING (nsd);

  saddr.sin_port = htons (7);   /* port ECHO */
  n = connect (nsd, (struct sockaddr *) &saddr, slen);

  /*
   * Connection will be established or refused immediately.
   * Either way it means that the peer host is alive.
   */
  if (n == 0 || (n < 0 && errno == ECONNREFUSED))
    {
      close (nsd);
      return true;
    }

  switch (errno)
    {
    case EINPROGRESS:       /* non-blocking, asynchronously */
      break;
    case ENETUNREACH:       /* network unreachable */
    case EAFNOSUPPORT:      /* address family not supported */
    case EADDRNOTAVAIL: /* address is not available on the remote machine */
    case EINVAL:        /* on some linux, connecting to the loopback */
      er_log_debug (ARG_FILE_LINE, "css_peer_alive: errno %d from connect()\n", errno);
      close (nsd);
      return false;
    default:            /* otherwise, connection failed */
      er_log_debug (ARG_FILE_LINE, "css_peer_alive: errno %d from connect()\n", errno);
      close (nsd);
      return false;
    }

retry_poll:
  po[0].fd = nsd;
  po[0].events = POLLOUT;
  po[0].revents = 0;
  n = poll (po, 1, timeout);
  if (n < 0)
    {
      if (errno == EINTR)
    {
      goto retry_poll;
    }
      er_log_debug (ARG_FILE_LINE, "css_peer_alive: errno %d from poll()\n", errno);
      close (nsd);
      return false;
    }
  else if (n == 0)
    {
      er_log_debug (ARG_FILE_LINE, "css_peer_alive: timed out %d\n", timeout);
      close (nsd);
      return false;
    }

  /* has connection been established? */
  size = sizeof (n);
  if (getsockopt (nsd, SOL_SOCKET, SO_ERROR, (void *) &n, &size) < 0)
    {
      er_log_debug (ARG_FILE_LINE, "css_peer_alive: getsockopt() return error %d\n", errno);
      close (nsd);
      return false;
    }

  if (n == 0 || n == ECONNREFUSED)
    {
      close (nsd);
      return true;
    }

  er_log_debug (ARG_FILE_LINE, "css_peer_alive: errno %d from connect()\n", n);
  close (nsd);
  return false;
}

/*
 * css_get_peer_name() - get the hostname of the peer socket
 *   return: 0 if success; otherwise errno
 *   hostname(in): buffer for hostname
 *   len(in): size of the hostname buffer
 */
int
css_get_peer_name (SOCKET sockfd, char *hostname, size_t len)
{
  union
  {
    struct sockaddr_in in;
    struct sockaddr_un un;
  } saddr_buf;
  struct sockaddr *saddr;
  socklen_t saddr_len;

  saddr = (struct sockaddr *) &saddr_buf;
  saddr_len = sizeof (saddr_buf);
  if (getpeername (sockfd, saddr, &saddr_len) != 0)
    {
      return errno;
    }
  return getnameinfo_uhost (saddr, saddr_len, hostname, len, NULL, 0, NI_NOFQDN);
}

/*
 * css_get_sock_name() - get the hostname of the socket
 *   return: 0 if success; otherwise errno
 *   hostname(in): buffer for hostname
 *   len(in): size of the hostname buffer
 */
int
css_get_sock_name (SOCKET sockfd, char *hostname, size_t len)
{
  union
  {
    struct sockaddr_in in;
    struct sockaddr_un un;
  } saddr_buf;
  struct sockaddr *saddr;
  socklen_t saddr_len;

  saddr = (struct sockaddr *) &saddr_buf;
  saddr_len = sizeof (saddr_buf);
  if (getsockname (sockfd, saddr, &saddr_len) != 0)
    {
      return errno;
    }
  return getnameinfo_uhost (saddr, saddr_len, hostname, len, NULL, 0, NI_NOFQDN);
}