File pl.cpp¶
File List > cubrid > src > executables > pl.cpp
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.cpp - utility PL JVM server main routine
*
*/
#ident "$Id$"
#include "config.h"
#if !defined(WINDOWS)
#include <dlfcn.h>
#include <execinfo.h>
#endif
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if !defined(WINDOWS)
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#else /* not WINDOWS */
#include <winsock2.h>
#include <windows.h>
#include <process.h>
#include <io.h>
#include <tlhelp32.h>
#endif /* not WINDOWS */
#include "process_util.h"
#include "environment_variable.h"
#include "system_parameter.h"
#include "error_code.h"
#include "error_manager.h"
#include "message_catalog.h"
#include "utility.h"
#include "databases_file.h"
#include "object_representation.h"
#include "method_struct_invoke.hpp"
#include "pl_connection.hpp"
#include "pl_comm.h"
#include "pl_file.h"
#include "pl_sr_jvm.h"
#include "packer.hpp"
#include <string>
#include <algorithm>
#include <array>
#include <atomic>
#define PL_PING_LEN PATH_MAX
#define PL_PRINT_ERR_MSG(...) \
do {\
fprintf (stderr, __VA_ARGS__);\
}while (0)
#if defined(WINDOWS)
#define NULL_DEVICE "NUL:"
#else
#define NULL_DEVICE "/dev/null"
#endif
static int pl_start_server (const PL_SERVER_INFO pl_info, const std::string &db_name, const std::string &path);
static int pl_stop_server (const PL_SERVER_INFO pl_info, const std::string &db_name);
static int pl_status_server (const PL_SERVER_INFO pl_info, const std::string &db_name);
static void pl_dump_status (FILE *fp, PL_STATUS_INFO status_info);
static int pl_ping_server (const PL_SERVER_INFO pl_info, const char *db_name, char *buf);
static bool pl_is_running (const PL_SERVER_INFO pl_info, const std::string &db_name);
static int pl_get_server_info (const std::string &db_name, PL_SERVER_INFO &info);
static int pl_check_argument (int argc, char *argv[], std::string &command, std::string &db_name);
static int pl_check_database (const std::string &db_name, std::string &db_path);
static int pl_get_port_param ();
#if !defined(WINDOWS)
static void pl_signal_handler (int sig);
#endif
static bool is_signal_handling = false;
static char executable_path[PATH_MAX];
static std::string command;
static std::string db_name;
static PL_SERVER_INFO running_info = PL_SERVER_INFO_INITIALIZER;
#if defined(WINDOWS)
static bool
get_ppid (DWORD &ppid)
{
HANDLE h_proc_snap;
PROCESSENTRY32 pe32;
/* Take a snapshot of all processes in the system. */
h_proc_snap = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
if (h_proc_snap == INVALID_HANDLE_VALUE)
{
return false;
}
pe32.dwSize = sizeof (PROCESSENTRY32);
if (Process32First (h_proc_snap, &pe32))
{
DWORD pid = GetCurrentProcessId();
do
{
if (pe32.th32ProcessID != pid)
{
continue;
}
CloseHandle (h_proc_snap);
ppid = pe32.th32ParentProcessID;
return true;
}
while (Process32Next (h_proc_snap, &pe32));
}
CloseHandle (h_proc_snap);
return false;
}
#endif
/*
* main() - pl server main function
*/
int
main (int argc, char *argv[])
{
int status = NO_ERROR;
FILE *redirect = NULL; /* for ping */
#if defined(WINDOWS)
FARPROC pl_old_hook = NULL;
#else
if (os_set_signal_handler (SIGTRAP, SIG_IGN) == SIG_ERR)
{
return ER_GENERIC_ERROR;
}
if (os_set_signal_handler (SIGCHLD, SIG_IGN) == SIG_ERR)
{
return ER_GENERIC_ERROR;
}
os_set_signal_handler (SIGABRT, pl_signal_handler);
os_set_signal_handler (SIGTERM, pl_signal_handler);
os_set_signal_handler (SIGILL, pl_signal_handler);
os_set_signal_handler (SIGFPE, pl_signal_handler);
os_set_signal_handler (SIGBUS, pl_signal_handler);
os_set_signal_handler (SIGSEGV, pl_signal_handler);
os_set_signal_handler (SIGSYS, pl_signal_handler);
#endif /* WINDOWS */
{
/*
* COMMON PART FOR PING AND OTHER COMMANDS
*/
// supress error message
er_init (NULL_DEVICE, ER_NEVER_EXIT);
/* check arguments, get command and database name */
status = pl_check_argument (argc, argv, command, db_name);
if (status != NO_ERROR)
{
return ER_GENERIC_ERROR;
}
/* check database exists and get path name of database */
std::string pathname;
status = pl_check_database (db_name, pathname);
if (status != NO_ERROR)
{
goto exit;
}
/* initialize error manager for command */
if (command.compare ("ping") != 0)
{
/* finalize supressing error message for ping */
er_final (ER_ALL_FINAL);
/* error message log file */
char er_msg_file[PATH_MAX];
snprintf (er_msg_file, sizeof (er_msg_file) - 1, "pl/%s_pl.err", db_name.c_str ());
er_init (er_msg_file, ER_NEVER_EXIT);
}
/* try to create info dir and get absolute path for info file; $CUBRID/var/pl_<db_name>.info */
PL_SERVER_INFO pl_info = PL_SERVER_INFO_INITIALIZER;
status = pl_get_server_info (db_name, pl_info);
if (status != NO_ERROR && command.compare ("start") != 0)
{
char info_path[PATH_MAX], err_msg[PATH_MAX + 32];
pl_get_info_file (info_path, PATH_MAX, db_name.c_str ());
snprintf (err_msg, sizeof (err_msg), "Error while opening file (%s)", info_path);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1, err_msg);
goto exit;
}
#if defined(WINDOWS)
// socket startup for windows
windows_socket_startup (pl_old_hook);
#endif /* WINDOWS */
/*
* PROCESS PING
*/
if (command.compare ("ping") == 0)
{
// redirect stderr
if ((redirect = freopen (NULL_DEVICE, "w", stderr)) == NULL)
{
assert (false);
goto exit;
}
// check process is running
if (pl_info.pid == PL_PID_DISABLED || is_terminated_process (pl_info.pid) == true)
{
fprintf (stdout, "NO_PROCESS");
goto exit;
}
char buffer[PL_PING_LEN] = {0};
if (status == NO_ERROR)
{
status = pl_ping_server (pl_info, db_name.c_str (), buffer);
}
if (status == NO_ERROR)
{
std::string ping_db_name;
packing_unpacker unpacker (buffer, PL_PING_LEN);
unpacker.unpack_string (ping_db_name);
fprintf (stdout, "%s", ping_db_name.c_str ());
}
else
{
fprintf (stdout, "NO_CONNECTION");
status = NO_ERROR;
goto exit;
}
return status;
}
/*
* BEGIN TO PROCESS FOR OTHER COMMANDS
*/
// load system parameter
sysprm_load_and_init (db_name.c_str (), NULL, SYSPRM_IGNORE_INTL_PARAMS);
/* pl command main routine */
if (command.compare ("start") == 0)
{
#if !defined (WINDOWS)
pid_t ppid = getppid ();
#endif
(void) pl_start_server (pl_info, db_name, pathname);
command = "running";
pl_read_info (db_name.c_str(), running_info);
do
{
#if defined (WINDOWS)
DWORD parent_ppid = 0;
HANDLE hParent;
DWORD result;
if (get_ppid (parent_ppid) == false)
{
break;// parent process is terminated
}
hParent = OpenProcess (SYNCHRONIZE, FALSE, parent_ppid);
result = WaitForSingleObject (hParent, INFINITE);
CloseHandle (hParent);
if (result == WAIT_OBJECT_0)
{
break;// parent process is terminated
}
#else
if (getppid () != ppid)
{
// parent process is terminated
break;
}
#endif
sleep (1);
}
while (true);
}
else if (command.compare ("stop") == 0)
{
status = pl_stop_server (pl_info, db_name);
}
else if (command.compare ("status") == 0)
{
status = pl_status_server (pl_info, db_name);
}
else
{
PL_PRINT_ERR_MSG ("Invalid command: %s\n", command.c_str ());
status = ER_GENERIC_ERROR;
}
}
exit:
#if defined(WINDOWS)
// socket shutdown for windows
windows_socket_shutdown (pl_old_hook);
#endif /* WINDOWS */
if (command.compare ("ping") == 0)
{
if (status != NO_ERROR)
{
fprintf (stdout, "ERROR");
}
if (redirect)
{
fclose (redirect);
}
}
else
{
if (er_has_error ())
{
PL_PRINT_ERR_MSG ("%s\n", er_msg ());
}
}
return status;
}
#if !defined(WINDOWS)
static void pl_signal_handler (int sig)
{
PL_SERVER_INFO pl_info = PL_SERVER_INFO_INITIALIZER;
if (os_set_signal_handler (sig, SIG_DFL) == SIG_ERR)
{
return;
}
if (is_signal_handling == true)
{
return;
}
int status = pl_get_server_info (db_name, pl_info); // if failed,
if (status == NO_ERROR && pl_info.pid != PL_PID_DISABLED)
{
if (command.compare ("running") != 0 || db_name.empty ())
{
return;
}
if (running_info.pid == pl_info.pid && running_info.port == pl_info.port)
{
is_signal_handling = true;
}
else
{
return;
}
er_print_crash_callstack (sig);
exit (1);
}
// resume signal hanlding
os_set_signal_handler (sig, pl_signal_handler);
is_signal_handling = false;
}
#endif
static int
pl_get_port_param ()
{
int prm_port = 0;
#if defined (WINDOWS)
const bool is_uds_mode = false;
#else
const bool is_uds_mode = prm_get_bool_value (PRM_ID_STORED_PROCEDURE_UDS);
#endif
prm_port = (is_uds_mode) ? PL_PORT_UDS_MODE : prm_get_integer_value (PRM_ID_STORED_PROCEDURE_PORT);
return prm_port;
}
static int
pl_start_server (const PL_SERVER_INFO pl_info, const std::string &db_name, const std::string &path)
{
int status = NO_ERROR;
if (pl_info.pid != PL_PID_DISABLED && pl_is_running (pl_info, db_name))
{
status = ER_GENERIC_ERROR;
}
else
{
#if !defined(WINDOWS)
/* create a new session */
setsid ();
#endif
er_clear (); // clear error before string JVM
status = pl_start_jvm_server (db_name.c_str (), path.c_str (), pl_get_port_param ());
if (er_has_error())
{
PL_PRINT_ERR_MSG ("%s\n", er_msg());
er_clear();
}
PL_SERVER_INFO pl_new_info { getpid(), pl_server_port () };
pl_unlink_info (db_name.c_str ());
if ((pl_open_info_dir () && pl_write_info (db_name.c_str (), &pl_new_info)))
{
/* succeed */
}
else
{
/* failed to write info file */
char info_path[PATH_MAX], err_msg[PATH_MAX + 32];
pl_get_info_file (info_path, PATH_MAX, db_name.c_str ());
snprintf (err_msg, sizeof (err_msg), "Error while writing to file: (%s)", info_path);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1, err_msg);
status = ER_SP_CANNOT_START_PL_SERVER;
}
}
return status;
}
static int
pl_stop_server (const PL_SERVER_INFO pl_info, const std::string &db_name)
{
#define MAX_RETRY_COUNT 10
int status = ER_FAILED;
int retry_count = 0;
if (pl_info.pid != -1 && !is_terminated_process (pl_info.pid))
{
terminate_process (pl_info.pid);
while (retry_count < MAX_RETRY_COUNT)
{
if (is_terminated_process (pl_info.pid) == true)
{
status = NO_ERROR;
break;
}
usleep (10);
retry_count++;
}
}
return status;
}
static int
pl_status_server (const PL_SERVER_INFO pl_info, const std::string &db_name)
{
int status = NO_ERROR;
cubmem::block buffer;
if (pl_info.pid == -1 || is_terminated_process (pl_info.pid))
{
goto exit;
}
{
cubpl::connection_pool connection_pool (1, db_name, pl_info.port, true);
cubpl::connection_view cv = connection_pool.claim ();
if (cv->is_valid())
{
cubmethod::header header (DB_EMPTY_SESSION, SP_CODE_UTIL_STATUS);
status = cv->send_buffer_args (header);
if (status != NO_ERROR)
{
goto exit;
}
status = cv->receive_buffer (buffer);
if (status != NO_ERROR)
{
goto exit;
}
if (status == NO_ERROR)
{
int num_args = 0;
PL_STATUS_INFO status_info;
status_info.pid = pl_info.pid;
packing_unpacker unpacker (buffer.ptr, buffer.dim);
unpacker.unpack_int (status_info.port);
unpacker.unpack_string (status_info.db_name);
unpacker.unpack_int (num_args);
std::string arg;
for (int i = 0; i < num_args; i++)
{
unpacker.unpack_string (arg);
status_info.vm_args.push_back (arg);
}
pl_dump_status (stdout, status_info);
}
}
else
{
status = ER_GENERIC_ERROR;
}
}
exit:
if (status != NO_ERROR)
{
fprintf (stdout, "Procedural Language Server (%s, pid %d) - Abnormal State \n", db_name.c_str (), pl_info.pid);
}
if (buffer.ptr)
{
free_and_init (buffer.ptr);
}
return status;
}
static int
pl_ping_server (const PL_SERVER_INFO pl_info, const char *db_name, char *buf)
{
int status = NO_ERROR;
cubmem::block ping_blk {0, NULL};
cubpl::connection_pool connection_pool (1, db_name, pl_info.port, true);
cubpl::connection_view cv = connection_pool.claim ();
if (cv->is_valid())
{
cubmethod::header header (DB_EMPTY_SESSION, SP_CODE_UTIL_PING);
status = cv->send_buffer_args (header);
if (status != NO_ERROR)
{
goto exit;
}
status = cv->receive_buffer (ping_blk);
if (status != NO_ERROR)
{
goto exit;
}
memcpy (buf, ping_blk.ptr, ping_blk.dim);
}
exit:
if (ping_blk.is_valid ())
{
delete [] ping_blk.ptr;
}
return er_errid ();
}
static void
pl_dump_status (FILE *fp, PL_STATUS_INFO status_info)
{
if (status_info.port == PL_PORT_UDS_MODE)
{
fprintf (fp, "Procedural Language Server (%s, pid %d, UDS)\n", status_info.db_name.c_str (), status_info.pid);
}
else
{
fprintf (fp, "Procedural Language Server (%s, pid %d, port %d)\n", status_info.db_name.c_str (), status_info.pid,
status_info.port);
}
auto vm_args_len = status_info.vm_args.size();
if (vm_args_len > 0)
{
fprintf (fp, "VM arguments :\n");
fprintf (fp, " -------------------------------------------------\n");
for (int i = 0; i < (int) vm_args_len; i++)
{
fprintf (fp, " %s\n", status_info.vm_args[i].c_str());
}
fprintf (fp, " -------------------------------------------------\n");
}
}
static bool
pl_is_running (const PL_SERVER_INFO pl_info, const std::string &db_name)
{
// check server running
bool result = false;
char buffer[PL_PING_LEN] = {0};
if (pl_ping_server (pl_info, db_name.c_str (), buffer) == NO_ERROR)
{
if (db_name.compare (0, db_name.size (), buffer) == 0)
{
result = true;
}
}
return result;
}
static int
pl_get_server_info (const std::string &db_name, PL_SERVER_INFO &info)
{
if (pl_open_info_dir ()
&& pl_read_info (db_name.c_str(), info))
{
return NO_ERROR;
}
else
{
return ER_GENERIC_ERROR;
}
}
static int
pl_check_database (const std::string &db_name, std::string &path)
{
int status = NO_ERROR;
/* check database exists */
DB_INFO *db = cfg_find_db (db_name.c_str ());
if (db == NULL)
{
status = ER_GENERIC_ERROR;
}
else
{
path.assign (db->pathname);
cfg_free_directory (db);
}
return status;
}
static int
pl_check_argument (int argc, char *argv[], std::string &command, std::string &db_name)
{
int status = NO_ERROR;
/* check argument number */
if (argc == 3)
{
command.assign (argv[1]);
db_name.assign (argv[2]);
}
else if (argc == 2)
{
command.assign ("start");
db_name.assign (argv[1]);
}
else
{
status = ER_GENERIC_ERROR;
PL_PRINT_ERR_MSG ("Invalid number of arguments: %d\n", argc);
}
if (status == NO_ERROR)
{
/* check command */
std::array<std::string, 5> commands = {"start", "stop", "restart", "status", "ping"};
auto it = find (commands.begin(), commands.end(), command);
if (it == commands.end())
{
status = ER_GENERIC_ERROR;
PL_PRINT_ERR_MSG ("Invalid command: %s\n", command.c_str ());
}
}
return status;
}