Skip to content

File pl_comm.c

File List > cubrid > src > sp > pl_comm.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.
 *
 */


/*
 * pl_comm.c - Functions to communicate with Procedural Language Server
 *
 * Note:
 */

#include "pl_comm.h"

#include "config.h"

#include <assert.h>
#if !defined(WINDOWS)
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#else /* not WINDOWS */
#include <winsock2.h>
#include <windows.h>
#endif /* not WINDOWS */

#include "pl_file.h"
#include "connection_support.hpp"
#include "porting.h"
#include "error_manager.h"
#include "environment_variable.h"

#include "system_parameter.h"
#include "object_representation.h"
#include "host_lookup.h"

#if defined (CS_MODE)
#include "network_interface_cl.h"
#else
#include "boot_sr.h"
#endif
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

static int pl_connect_server_tcp (int server_port, SOCKET & out);
#if !defined (WINDOWS)
static int pl_connect_server_uds (const char *db_name, SOCKET & out);
#endif
/*
 * pl_connect_server
 *   return: connect fail - return Error Code
 *           connection success - return socket fd
 *
 * Note:
 */

int
pl_connect_server (const char *db_name, int server_port, SOCKET & out)
{
#if defined (WINDOWS)
  return pl_connect_server_tcp (server_port, out);
#else
  if (server_port == PL_PORT_UDS_MODE)
    {
      return pl_connect_server_uds (db_name, out);
    }
  else
    {
      return pl_connect_server_tcp (server_port, out);
    }
#endif
}

/*
 * pl_disconnect_server -
 *   return: none
 *   sockfd(in) : close connection
 *
 * Note:
 */

void
pl_disconnect_server (SOCKET & sockfd)
{
  if (!IS_INVALID_SOCKET (sockfd))
    {
      struct linger linger_buffer;

      linger_buffer.l_onoff = 1;
      linger_buffer.l_linger = 0;
      setsockopt (sockfd, SOL_SOCKET, SO_LINGER, (char *) &linger_buffer, sizeof (linger_buffer));
#if defined(WINDOWS)
      closesocket (sockfd);
#else /* not WINDOWS */
      close (sockfd);
#endif /* not WINDOWS */
      sockfd = INVALID_SOCKET;
    }
}

/*
 * pl_writen
 *   return: fail return -1,
 *   fd(in): Specifies the socket file descriptor.
 *   vptr(in): Points to the buffer containing the message to send.
 *   n(in): Specifies the length of the message in bytes
 *
 * Note:
 */

int
pl_writen (SOCKET fd, const void *vptr, int n)
{
  int nwritten;
  int nleft = n;
  const char *ptr = (const char *) vptr;

  while (nleft > 0)
    {
#if defined(WINDOWS)
      nwritten = send (fd, ptr, nleft, 0);
#else
      nwritten = send (fd, ptr, (size_t) nleft, 0);
#endif

      if (nwritten <= 0)
    {
#if defined(WINDOWS)
      if (nwritten < 0 && errno == WSAEINTR)
#else /* not WINDOWS */
      if (nwritten < 0 && errno == EINTR)
#endif /* not WINDOWS */
        {
          nwritten = 0; /* and call write() again */
        }
      else
        {
          return (-1);  /* error */
        }
    }

      nleft -= nwritten;
      ptr += nwritten;
    }

  return (n - nleft);
}

/*
 * pl_readn
 *   return: read size
 *   fd(in): Specifies the socket file descriptor.
 *   vptr(in/out): Points to a buffer where the message should be stored.
 *   n(in): Specifies  the  length in bytes of the buffer pointed
 *          to by the buffer argument.
 *
 * Note:
 */

int
pl_readn (SOCKET fd, void *vptr, int n)
{
  const static int PING_TIMEOUT = 5000;
  return css_readn (fd, (char *) vptr, n, PING_TIMEOUT);
}

int
pl_readn_with_timeout (SOCKET fd, void *vptr, int n, int timeout)
{
  return css_readn (fd, (char *) vptr, n, timeout);
}

int
pl_ping (SOCKET fd)
{
  char buffer[DB_MAX_IDENTIFIER_LENGTH];

  OR_ALIGNED_BUF (OR_INT_SIZE * 2) a_request;
  char *request = OR_ALIGNED_BUF_START (a_request);
  char *ptr = or_pack_int (request, OR_INT_SIZE);
  ptr = or_pack_int (ptr, SP_CODE_UTIL_PING);

  int nbytes = pl_writen (fd, request, OR_INT_SIZE * 2);
  if (nbytes != OR_INT_SIZE * 2)
    {
      return ER_SP_NETWORK_ERROR;
    }

  int res_size = 0;
  nbytes = pl_readn (fd, (char *) &res_size, OR_INT_SIZE);
  if (nbytes != OR_INT_SIZE)
    {
      return ER_SP_NETWORK_ERROR;
    }
  res_size = ntohl (res_size);

  nbytes = pl_readn (fd, buffer, res_size);
  if (nbytes != res_size)
    {
      return ER_SP_NETWORK_ERROR;
    }

  return NO_ERROR;
}

char *
pl_get_socket_file_path (const char *db_name)
{
  static char path[PATH_MAX];
  static bool need_init = true;

  if (need_init)
    {
      const size_t DIR_PATH_MAX = 128;  /* Guaranteed not to exceed 108 characters, see envvar_check_environment() */
      char dir_path[DIR_PATH_MAX] = { 0 };
      const char *cubrid_tmp = envvar_get ("TMP");
      if (cubrid_tmp == NULL || cubrid_tmp[0] == '\0')
    {
      envvar_vardir_file (dir_path, DIR_PATH_MAX, "CUBRID_SOCK/");
    }
      else
    {
      snprintf (dir_path, DIR_PATH_MAX, "%s/", cubrid_tmp);
    }

      snprintf (path, PATH_MAX, "%s%s%s%s", dir_path, "sp_", db_name, ".sock");
      need_init = false;
    }

  return path;
}

#if !defined (WINDOWS)
static int
pl_connect_server_uds (const char *db_name, SOCKET & out)
{
  struct sockaddr_un sock_addr;
  SOCKET sockfd = INVALID_SOCKET;

  sockfd = socket (AF_UNIX, SOCK_STREAM, 0);
  if (IS_INVALID_SOCKET (sockfd))
    {
      out = INVALID_SOCKET;
    }

  int slen = sizeof (sock_addr);
  memset (&sock_addr, 0, slen);
  sock_addr.sun_family = AF_UNIX;
  snprintf (sock_addr.sun_path, sizeof (sock_addr.sun_path), "%s", pl_get_socket_file_path (db_name));

  int success = connect (sockfd, (struct sockaddr *) &sock_addr, slen);
  if (success < 0)
    {
      out = INVALID_SOCKET;
      return ER_SP_CANNOT_CONNECT_PL_SERVER;
    }

  int one = 1;
  setsockopt (sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof (one));
  out = sockfd;

  return NO_ERROR;
}
#endif

static int
pl_connect_server_tcp (int server_port, SOCKET & out)
{
  struct sockaddr_in tcp_srv_addr;

  SOCKET sockfd = INVALID_SOCKET;
  int success = -1;
  unsigned int inaddr;
  int b;
  char *server_host = (char *) "127.0.0.1"; /* assume as local host */

  union
  {
    struct sockaddr_in in;
  } saddr_buf;
  struct sockaddr *saddr = (struct sockaddr *) &saddr_buf;
  socklen_t slen;

  if (server_port < 0)
    {
      out = sockfd;     /* INVALID_SOCKET (-1) */
      return ER_SP_CANNOT_CONNECT_PL_SERVER;
    }

  inaddr = inet_addr (server_host);
  memset ((void *) &tcp_srv_addr, 0, sizeof (tcp_srv_addr));
  tcp_srv_addr.sin_family = AF_INET;
  tcp_srv_addr.sin_port = htons (server_port);

  if (inaddr != INADDR_NONE)
    {
      memcpy ((void *) &tcp_srv_addr.sin_addr, (void *) &inaddr, sizeof (inaddr));
    }
  else
    {
      struct hostent *hp;
      hp = gethostbyname_uhost (server_host);

      if (hp == NULL)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_HOST_NAME_ERROR, 2, server_host,
                   HOSTS_FILE);
      out = INVALID_SOCKET;
    }
      memcpy ((void *) &tcp_srv_addr.sin_addr, (void *) hp->h_addr, hp->h_length);
    }
  slen = sizeof (tcp_srv_addr);
  memcpy ((void *) saddr, (void *) &tcp_srv_addr, slen);

  sockfd = socket (saddr->sa_family, SOCK_STREAM, 0);
  if (IS_INVALID_SOCKET (sockfd))
    {
      out = INVALID_SOCKET;
      return ER_SP_CANNOT_CONNECT_PL_SERVER;
    }
  else
    {
      b = 1;
      setsockopt (sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &b, sizeof (b));
    }

  success = connect (sockfd, saddr, slen);
  if (success < 0)
    {
      out = INVALID_SOCKET;
      return ER_SP_CANNOT_CONNECT_PL_SERVER;
    }

  out = sockfd;

  return NO_ERROR;
}

#if defined(WINDOWS)
/*
 * windows_blocking_hook() -
 *   return: false
 *
 * Note: WINDOWS Code
 */

BOOL
windows_blocking_hook ()
{
  return false;
}

/*
 * windows_socket_startup() -
 *   return: return -1 on error otherwise return 1
 *
 * Note:
 */

int
windows_socket_startup (FARPROC hook)
{
  WORD wVersionRequested;
  WSADATA wsaData;
  int err;

  hook = NULL;
  wVersionRequested = 0x101;
  err = WSAStartup (wVersionRequested, &wsaData);
  if (err != 0)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_WINSOCK_STARTUP, 1, err);
      return (-1);
    }

  /* Establish a blocking "hook" function to prevent Windows messages from being dispatched when we block on reads. */
  hook = WSASetBlockingHook ((FARPROC) windows_blocking_hook);
  if (hook == NULL)
    {
      /* couldn't set up our hook */
      err = WSAGetLastError ();
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_WINSOCK_STARTUP, 1, err);
      (void) WSACleanup ();
      return -1;
    }

  return 1;
}

/*
 * windows_socket_shutdown() -
 *   return:
 *
 * Note:
 */

void
windows_socket_shutdown (FARPROC hook)
{
  int err;

  if (hook != NULL)
    {
      (void) WSASetBlockingHook (hook);
    }

  err = WSACleanup ();
}
#endif /* WINDOWS */