File master.c¶
File List > cubrid > src > executables > master.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.
*
*/
/*
* master.c - master main
*/
#ident "$Id$"
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#if defined(_AIX)
#include <sys/select.h>
#endif
#if defined(WINDOWS)
#include <winsock2.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <io.h>
#else /* ! WINDOWS */
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <pthread.h>
#endif /* ! WINDOWS */
#include "utility.h"
#include "porting.h"
#include "error_manager.h"
#include "connection_globals.h"
#include "client_support.h"
#include "system_parameter.h"
#if defined(WINDOWS)
#include "wintcp.h"
#else /* ! WINDOWS */
#include "tcp.h"
#endif /* ! WINDOWS */
#include "master_util.h"
#include "master_request.h"
#if !defined(WINDOWS)
#include "master_heartbeat.h"
#endif
#include "environment_variable.h"
#include "message_catalog.h"
#include "dbi.h"
#include "util_func.h"
#include "master_server_monitor.hpp"
static void css_master_error (const char *error_string);
static int css_master_timeout (void);
static int css_master_init (int cport, SOCKET * clientfd);
static void css_reject_client_request (CSS_CONN_ENTRY * conn, unsigned short rid, int reason);
static void css_reject_server_request (CSS_CONN_ENTRY * conn, int reason);
static void css_accept_server_request (CSS_CONN_ENTRY * conn, int reason);
static void css_accept_new_request (CSS_CONN_ENTRY * conn, unsigned short rid, char *buffer, int buffer_length,
bool is_client);
static void css_accept_old_request (CSS_CONN_ENTRY * conn, unsigned short rid, SOCKET_QUEUE_ENTRY * entry,
char *server_name, int server_name_length);
static void css_register_new_server (CSS_CONN_ENTRY * conn, unsigned short rid, bool is_client);
static void css_register_new_server2 (CSS_CONN_ENTRY * conn, unsigned short rid);
static bool css_send_new_request_to_server (SOCKET server_fd, SOCKET client_fd, unsigned short rid,
CSS_SERVER_REQUEST request);
static void css_send_to_existing_server (CSS_CONN_ENTRY * conn, unsigned short rid, CSS_SERVER_REQUEST request);
static void css_process_new_connection (SOCKET fd);
static int css_enroll_read_sockets (SOCKET_QUEUE_ENTRY * anchor_p, fd_set * fd_var);
static int css_enroll_write_sockets (SOCKET_QUEUE_ENTRY * anchor_p, fd_set * fd_var);
static int css_enroll_exception_sockets (SOCKET_QUEUE_ENTRY * anchor_p, fd_set * fd_var);
static int css_enroll_master_read_sockets (fd_set * fd_var);
static int css_enroll_master_write_sockets (fd_set * fd_var);
static int css_enroll_master_exception_sockets (fd_set * fd_var);
static void css_master_select_error (void);
static void css_check_master_socket_input (int *count, fd_set * fd_var);
static void css_check_master_socket_output (void);
static int css_check_master_socket_exception (fd_set * fd_var);
static void css_master_loop (void);
static void css_free_entry (SOCKET_QUEUE_ENTRY * entry_p);
#if !defined(WINDOWS)
static void css_daemon_start (void);
#endif
struct timeval *css_Master_timeout = NULL;
int css_Master_timeout_value_in_seconds = 4;
int css_Master_timeout_value_in_microseconds = 500;
#if defined(DEBUG)
static int css_Active_server_count = 0;
#endif
time_t css_Start_time;
int css_Total_request_count = 0;
/* socket for incoming client requests */
SOCKET css_Master_socket_fd[2] = { INVALID_SOCKET, INVALID_SOCKET };
/* This is the queue anchor of sockets used by the Master server. */
SOCKET_QUEUE_ENTRY *css_Master_socket_anchor = NULL;
#if !defined(WINDOWS)
pthread_mutex_t css_Master_socket_anchor_lock;
#endif
pthread_mutex_t css_Master_er_log_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t css_Master_er_log_enable_lock = PTHREAD_MUTEX_INITIALIZER;
bool css_Master_er_log_enabled = true;
/*
* css_master_error() - print error message to syslog or console
* return: none
* error_string(in)
*
* Note: Errors encountered by the master will always be printed.
*/
static void
css_master_error (const char *error_string)
{
#if defined(WINDOWS)
/* send it to the invoking console */
fprintf (stderr, "Master process: %s\n", error_string);
perror ("css_master_error");
#else /* ! WINDOWS */
syslog (LOG_ALERT, "Master process: %s %s\n", error_string, errno > 0 ? strerror (errno) : "");
#endif /* ! WINDOWS */
}
/*
* css_master_timeout()
* return: 0 if css_Master_timeout time is expired,
* otherwise 1
*
* Note:
* This is to handle the case when nothing is happening. We will check the
* process status to see if the server is still running. If not, then we
* remove our entry.
*/
static int
css_master_timeout (void)
{
#if !defined(WINDOWS)
int rv;
SOCKET_QUEUE_ENTRY *temp;
#endif
struct timeval timeout;
/* check for timeout */
if (css_Master_timeout && time ((time_t *) (&timeout.tv_sec)) != (time_t) (-1)
&& css_Master_timeout->tv_sec < timeout.tv_sec)
{
return (0);
}
#if !defined(WINDOWS)
/* On the WINDOWS, it is not clear if we will ever have spawned child processes, at least initially. There don't
* appear to be any similarly named "wait" functions in the MSVC runtime library. */
rv = pthread_mutex_lock (&css_Master_socket_anchor_lock);
for (temp = css_Master_socket_anchor; temp; temp = temp->next)
{
if (kill (temp->pid, 0) && errno == ESRCH)
{
#if defined(DEBUG)
if (css_Active_server_count > 0)
{
css_Active_server_count--;
}
#endif
#if !defined(WINDOWS)
if (temp->ha_mode)
{
hb_cleanup_conn_and_start_process (temp->conn_ptr, temp->fd);
}
else
#endif
{
css_remove_entry_by_conn (temp->conn_ptr, &css_Master_socket_anchor);
}
break;
}
}
pthread_mutex_unlock (&css_Master_socket_anchor_lock);
#endif
return (1);
}
/*
* css_master_cleanup() - cleanup the socket on the port-id
* return: none
* sig(in)
*
* Note: It will not be called if we are killed by an outside process
*/
void
css_master_cleanup (int sig)
{
css_shutdown_socket (css_Master_socket_fd[0]);
css_shutdown_socket (css_Master_socket_fd[1]);
#if !defined(WINDOWS)
unlink (css_get_master_domain_path ());
if (!HA_DISABLED ())
{
MASTER_ER_SET (ER_NOTIFICATION_SEVERITY, ARG_FILE_LINE, ER_HB_STOPPED, 0);
}
#endif
exit (1);
}
/*
* crash_handler() - print call stack for occurring crash
* return: none
* sig_no(in)
*/
static void
crash_handler (int sig)
{
if (os_set_signal_handler (sig, SIG_DFL) == SIG_ERR)
{
return;
}
er_print_crash_callstack (sig);
}
/*
* css_master_init() - setup the signal handling routines and attempt to
* bind the socket address
* return: 1 if success, otherwise 0
* cservice(in)
* clientfd(out)
*/
static int
css_master_init (int cport, SOCKET * clientfd)
{
#if defined(WINDOWS)
if (os_set_signal_handler (SIGABRT, css_master_cleanup) == SIG_ERR
|| os_set_signal_handler (SIGINT, css_master_cleanup) == SIG_ERR
|| os_set_signal_handler (SIGTERM, css_master_cleanup) == SIG_ERR)
{
MASTER_ER_SET (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return (0);
}
#else /* ! WINDOWS */
(void) os_set_signal_handler (SIGSTOP, css_master_cleanup);
if (os_set_signal_handler (SIGTERM, css_master_cleanup) == SIG_ERR
|| os_set_signal_handler (SIGINT, css_master_cleanup) == SIG_ERR
|| os_set_signal_handler (SIGPIPE, SIG_IGN) == SIG_ERR || os_set_signal_handler (SIGCHLD, SIG_IGN) == SIG_ERR)
{
MASTER_ER_SET (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return (0);
}
os_set_signal_handler (SIGABRT, crash_handler);
os_set_signal_handler (SIGILL, crash_handler);
os_set_signal_handler (SIGFPE, crash_handler);
os_set_signal_handler (SIGBUS, crash_handler);
os_set_signal_handler (SIGSEGV, crash_handler);
os_set_signal_handler (SIGSYS, crash_handler);
#endif /* ! WINDOWS */
#if !defined(WINDOWS)
pthread_mutex_init (&css_Master_socket_anchor_lock, NULL);
#endif
return (css_tcp_master_open (cport, clientfd));
}
/*
* css_reject_client_request() - Sends the reject reason to the client
* if the server cannot immediatly accept a connection.
* return: none
* conn(in)
* rid(in)
* reason(in)
*/
static void
css_reject_client_request (CSS_CONN_ENTRY * conn, unsigned short rid, int reason)
{
int reject_reason;
reject_reason = htonl (reason);
css_send_data (conn, rid, (char *) &reject_reason, sizeof (int));
}
/*
* css_reject_server_request() - Sends a reject message to a server request
* return: none
* conn(in)
* reason(in)
*/
static void
css_reject_server_request (CSS_CONN_ENTRY * conn, int reason)
{
int reject_reason;
reject_reason = htonl (reason);
send (conn->fd, (char *) &reject_reason, sizeof (int), 0);
}
/*
* css_accept_server_request() - Sends an accept reply to the server to
* indicate that it is now connected to the master server
* return: none
* conn(in)
* reason(in)
*/
static void
css_accept_server_request (CSS_CONN_ENTRY * conn, int reason)
{
int accept_reason;
accept_reason = htonl (reason);
send (conn->fd, (char *) &accept_reason, sizeof (int), 0);
}
/*
* css_accept_new_request() - Accepts a connect request from a new server
* return: none
* conn(in)
* rid(in)
* buffer(in)
* buffer_length(in)
* is_client(in)
*
*/
static void
css_accept_new_request (CSS_CONN_ENTRY * conn, unsigned short rid, char *buffer, int buffer_length, bool is_client)
{
char *datagram;
char *server_name;
int datagram_length;
SOCKET server_fd = INVALID_SOCKET;
int length;
int server_name_length;
CSS_CONN_ENTRY *datagram_conn;
SOCKET_QUEUE_ENTRY *entry = NULL;
CSS_SERVER_PROC_REGISTER *proc_register = NULL;
datagram = NULL;
datagram_length = 0;
css_accept_server_request (conn, SERVER_REQUEST_ACCEPTED);
if (__gv_cvar.css_receive_data (conn, rid, &datagram, &datagram_length, -1) == NO_ERRORS)
{
if (datagram != NULL && css_tcp_master_datagram (datagram, &server_fd))
{
datagram_conn = __gv_cvar.css_make_conn (server_fd);
#if defined(DEBUG)
css_Active_server_count++;
#endif
if (is_client)
{
server_name = buffer;
server_name_length = buffer_length;
}
else
{
assert (buffer != NULL);
proc_register = (CSS_SERVER_PROC_REGISTER *) buffer;
assert (proc_register->server_name != NULL);
server_name = proc_register->server_name;
server_name_length = proc_register->server_name_length;
}
length = (int) strlen (server_name) + 1;
css_add_request_to_socket_queue (datagram_conn, false, server_name, server_fd, READ_WRITE, 0,
&css_Master_socket_anchor);
// Note : server_name is usually packed(appended) information of server_name, version_string, env_var, pid,
// packed from css_pack_server_name(). Since there are some cases that returns server_name and server_name_length
// as NULL, we need to check if server_name is packed information or not.
assert (length <= DB_MAX_IDENTIFIER_LENGTH);
if (length < server_name_length)
{
entry = css_return_entry_of_server (server_name, css_Master_socket_anchor);
if (entry != NULL)
{
server_name += length;
entry->version_string = (char *) malloc (strlen (server_name) + 1);
if (entry->version_string != NULL)
{
strcpy (entry->version_string, server_name);
server_name += strlen (entry->version_string) + 1;
entry->env_var = (char *) malloc (strlen (server_name) + 1);
if (entry->env_var != NULL)
{
strcpy (entry->env_var, server_name);
}
server_name += strlen (server_name) + 1;
entry->pid = atoi (server_name);
}
else
{
entry->env_var = NULL;
}
if (!entry->ha_mode)
{
#if !defined(WINDOWS)
if (auto_Restart_server && !is_client)
{
/* *INDENT-OFF* */
master_Server_monitor->produce_job (server_monitor::job_type::REGISTER_SERVER,
proc_register->pid, proc_register->exec_path,
proc_register->args, proc_register->server_name);
/* *INDENT-ON* */
}
#endif
}
}
}
}
}
if (datagram)
{
free_and_init (datagram);
}
}
/*
* css_accept_old_request() - Accepts a connect request from a server
* return: none
* conn(in)
* rid(in)
* entry(out)
* server_name(in)
* server_name_length(in)
*/
static void
css_accept_old_request (CSS_CONN_ENTRY * conn, unsigned short rid, SOCKET_QUEUE_ENTRY * entry, char *server_name,
int server_name_length)
{
char *datagram;
int datagram_length;
SOCKET server_fd = INVALID_SOCKET;
int length;
CSS_CONN_ENTRY *datagram_conn;
datagram = NULL;
datagram_length = 0;
css_accept_server_request (conn, SERVER_REQUEST_ACCEPTED);
if (__gv_cvar.css_receive_data (conn, rid, &datagram, &datagram_length, -1) == NO_ERRORS)
{
if (datagram != NULL && css_tcp_master_datagram (datagram, &server_fd))
{
datagram_conn = __gv_cvar.css_make_conn (server_fd);
entry->fd = server_fd;
__gv_cvar.css_free_conn (entry->conn_ptr);
entry->conn_ptr = datagram_conn;
length = (int) strlen (server_name) + 1;
if (length < server_name_length)
{
server_name += length;
if ((entry->version_string = (char *) malloc (strlen (server_name) + 1)) != NULL)
{
strcpy (entry->version_string, server_name);
}
}
}
}
if (datagram)
{
free_and_init (datagram);
}
}
/*
* css_register_new_server() - register a new server by reading the server name
* return: none
* conn(in)
* rid(in)
* is_client(in)
*
* Note: This will allow us to pass fds for future requests to the server.
*/
static void
css_register_new_server (CSS_CONN_ENTRY * conn, unsigned short rid, bool is_client)
{
int data_length;
char *data = NULL;
SOCKET_QUEUE_ENTRY *entry;
// Note: css_register_new_server() is used in two situations:
// 1. When a client requests to connect a cub_server to cub_master, which is already registered.
// 2. When a new cub_server requests to register itself to cub_master.
// For the first situation, css_register_new_server() receives the server name as data.
// For the second situation, css_register_new_server() receives CSS_SERVER_PROC_REGISTER as data.
if (__gv_cvar.css_receive_data (conn, rid, &data, &data_length, -1) == NO_ERRORS)
{
entry = css_return_entry_of_server (data, css_Master_socket_anchor);
if (entry != NULL)
{
if (IS_INVALID_SOCKET (entry->fd))
{
/* accept a server that was auto-started */
css_accept_old_request (conn, rid, entry, data, data_length);
}
else
{
/* reject a server with a duplicate name */
css_reject_server_request (conn, SERVER_ALREADY_EXISTS);
}
}
else
{
#if defined(WINDOWS)
css_accept_server_request (conn, SERVER_REQUEST_ACCEPTED);
#if defined(DEBUG)
css_Active_server_count++;
#endif
css_add_request_to_socket_queue (conn, false, data, conn->fd, READ_WRITE, 0, &css_Master_socket_anchor);
#else /* ! WINDOWS */
/* accept a request from a new server */
css_accept_new_request (conn, rid, data, data_length, is_client);
#endif /* ! WINDOWS */
}
}
if (data != NULL)
{
free_and_init (data);
}
#if !defined(WINDOWS)
/* WINDOWS wants to keep this conn--it is the main connection */
__gv_cvar.css_free_conn (conn);
#endif /* ! WINDOWS */
}
/*
* css_register_new_server2()
* return: none
* conn(in)
* rid(in)
*
* Note:
* Register a server using the new-style of connection protocol where
* the port id is given to us explicitly.
*/
static void
css_register_new_server2 (CSS_CONN_ENTRY * conn, unsigned short rid)
{
int name_length;
char *server_name = NULL;
SOCKET_QUEUE_ENTRY *entry;
int buffer;
int server_name_length, length;
/* read server name */
if (__gv_cvar.css_receive_data (conn, rid, &server_name, &name_length, -1) == NO_ERRORS && server_name != NULL)
{
entry = css_return_entry_of_server (server_name, css_Master_socket_anchor);
if (entry != NULL)
{
if (IS_INVALID_SOCKET (entry->fd))
{
/* accept a server */
css_accept_old_request (conn, rid, entry, server_name, name_length);
}
else
{
/* reject a server with a duplicate name */
css_reject_server_request (conn, SERVER_ALREADY_EXISTS);
}
__gv_cvar.css_free_conn (conn);
}
else
{
/* save length info */
server_name_length = name_length;
/* accept but make it send us a port id */
css_accept_server_request (conn, SERVER_REQUEST_ACCEPTED_NEW);
name_length = sizeof (buffer);
if (css_net_recv (conn, (char *) &buffer, &name_length, -1) == NO_ERRORS)
{
#if defined(DEBUG)
css_Active_server_count++;
#endif
entry =
css_add_request_to_socket_queue (conn, false, server_name, conn->fd, READ_WRITE, 0,
&css_Master_socket_anchor);
/* store this for later */
if (entry != NULL)
{
entry->port_id = ntohl (buffer);
length = (int) strlen (server_name) + 1;
/* read server version_string, env_var, pid */
if (length < server_name_length)
{
entry = css_return_entry_of_server (server_name, css_Master_socket_anchor);
if (entry != NULL)
{
char *recv_data;
recv_data = server_name + length;
entry->version_string = (char *) malloc (strlen (recv_data) + 1);
if (entry->version_string != NULL)
{
strcpy (entry->version_string, recv_data);
recv_data += strlen (entry->version_string) + 1;
entry->env_var = (char *) malloc (strlen (recv_data) + 1);
if (entry->env_var != NULL)
{
strcpy (entry->env_var, recv_data);
}
recv_data += strlen (recv_data) + 1;
entry->pid = atoi (recv_data);
}
else
{
entry->env_var = NULL;
}
}
}
}
else
{
__gv_cvar.css_free_conn (conn);
}
}
else
{
__gv_cvar.css_free_conn (conn);
}
}
}
else
{
__gv_cvar.css_free_conn (conn);
}
if (server_name != NULL)
{
free_and_init (server_name);
}
}
/*
* Master server to Slave server communication support routines.
*/
/*
* css_send_new_request_to_server() - Attempts to transfer a clients request
* to the server
* return: true if success
* server_fd(in)
* client_fd(in)
* rid(in)
*/
static bool
css_send_new_request_to_server (SOCKET server_fd, SOCKET client_fd, unsigned short rid, CSS_SERVER_REQUEST request)
{
return (css_transfer_fd (server_fd, client_fd, rid, request));
}
/*
* css_send_to_existing_server() - Sends a new client request to
* an existing server
* return: none
* conn(in)
* rid(in)
*
* Note:
* There are two ways this can go. First we locate the requsted server on
* our connection list and then see if it was registered with an explicit
* port id. If not, we attempt to pass the open client fd to the server
* using the "old" style connection protocol.
*
* If the server has instead given us an explicit port id, we tell the
* client to use the new style protocol and establish their own connection
* to the server.
*/
static void
css_send_to_existing_server (CSS_CONN_ENTRY * conn, unsigned short rid, CSS_SERVER_REQUEST request)
{
SOCKET_QUEUE_ENTRY *temp;
char *server_name = NULL;
int name_length, buffer;
name_length = 1024;
if (__gv_cvar.css_receive_data (conn, rid, &server_name, &name_length, -1) == NO_ERRORS && server_name != NULL)
{
temp = css_return_entry_of_server (server_name, css_Master_socket_anchor);
if (temp != NULL
#if !defined(WINDOWS)
&& (temp->ha_mode == false || hb_is_deactivation_started () == false)
#endif /* !WINDOWS */
)
{
if (temp->port_id == -1)
{
/* use old style connection */
if (IS_INVALID_SOCKET (temp->fd))
{
css_reject_client_request (conn, rid, SERVER_STARTED);
free_and_init (server_name);
__gv_cvar.css_free_conn (conn);
return;
}
else
{
#if !defined(WINDOWS)
if (hb_is_hang_process (temp->fd))
{
css_reject_client_request (conn, rid, SERVER_HANG);
free_and_init (server_name);
__gv_cvar.css_free_conn (conn);
return;
}
#endif
if (css_send_new_request_to_server (temp->fd, conn->fd, rid, request))
{
free_and_init (server_name);
__gv_cvar.css_free_conn (conn);
return;
}
else if (!temp->ha_mode)
{
temp = css_return_entry_of_server (server_name, css_Master_socket_anchor);
if (temp != NULL)
{
css_remove_entry_by_conn (temp->conn_ptr, &css_Master_socket_anchor);
}
}
}
}
else
{
/* Use new style connection. We found a registered server, give the client a response telling it to
* re-connect using the server's port id. */
#if !defined(WINDOWS)
if (hb_is_hang_process (temp->fd))
{
css_reject_client_request (conn, rid, SERVER_HANG);
free_and_init (server_name);
__gv_cvar.css_free_conn (conn);
return;
}
#endif
buffer = htonl (SERVER_CONNECTED_NEW);
css_send_data (conn, rid, (char *) &buffer, sizeof (int));
buffer = htonl (temp->port_id);
css_send_data (conn, rid, (char *) &buffer, sizeof (int));
}
}
css_reject_client_request (conn, rid, SERVER_NOT_FOUND);
}
__gv_cvar.css_free_conn (conn);
if (server_name != NULL)
{
free_and_init (server_name);
}
}
/*
* css_process_new_connection()
* return: none
* fd(in)
*
* Note:
* Selects the appropriate handler based on the type of connection. We can
* support a client request (to connect to a server), a server request (to
* register itself) and an information client request (master control client).
*/
static void
css_process_new_connection (SOCKET fd)
{
CSS_CONN_ENTRY *conn;
int function_code;
int buffer_size;
unsigned short rid;
buffer_size = sizeof (NET_HEADER);
css_Total_request_count++;
conn = __gv_cvar.css_make_conn (fd);
if (conn == NULL)
{
return;
}
if (css_check_magic (conn) != NO_ERRORS)
{
__gv_cvar.css_free_conn (conn);
return;
}
if (__gv_cvar.css_receive_request (conn, &rid, &function_code, &buffer_size) == NO_ERRORS)
{
switch (function_code)
{
case INFO_REQUEST: /* request for information */
css_add_request_to_socket_queue (conn, true, NULL, fd, READ_WRITE, 0, &css_Master_socket_anchor);
break;
case DATA_REQUEST: /* request from a remote client */
css_send_to_existing_server (conn, rid, SERVER_START_NEW_CLIENT);
break;
case SERVER_REQUEST_FROM_SERVER: /* request from a new server */
css_register_new_server (conn, rid, false);
break;
case SERVER_REQUEST_FROM_CLIENT: /* request from a new server */
css_register_new_server (conn, rid, true);
break;
case SERVER_REQUEST_NEW: /* request from a new server */
/* here the server wants to manage its own connection port */
css_register_new_server2 (conn, rid);
break;
default:
__gv_cvar.css_free_conn (conn);
break;
}
}
else
{
__gv_cvar.css_free_conn (conn);
}
}
/*
* css_enroll_read_sockets() - Sets the fd positions in fd_set for the
* input fds we are interested in reading
* return: none
*
* anchor_p(in)
* fd_var(out)
*/
static int
css_enroll_read_sockets (SOCKET_QUEUE_ENTRY * anchor_p, fd_set * fd_var)
{
SOCKET_QUEUE_ENTRY *temp;
SOCKET max_fd = 0;
FD_ZERO (fd_var);
for (temp = anchor_p; temp; temp = temp->next)
{
if (!IS_INVALID_SOCKET (temp->fd) && temp->fd_type != WRITE_ONLY)
{
FD_SET (temp->fd, fd_var);
if (temp->fd > max_fd)
{
max_fd = temp->fd;
}
}
}
return (int) max_fd;
}
/*
* css_enroll_master_read_sockets() -
* return: none
*
* fd_var(out)
*/
static int
css_enroll_master_read_sockets (fd_set * fd_var)
{
return css_enroll_read_sockets (css_Master_socket_anchor, fd_var);
}
/*
* css_enroll_write_sockets() - Sets the fd positions in fd_set for the
* input fds we are interested in writing (none presently)
* return: none
*
* anchor_p(in)
* fd_var(out)
*/
static int
css_enroll_write_sockets (SOCKET_QUEUE_ENTRY * anchor_p, fd_set * fd_var)
{
FD_ZERO (fd_var);
return 0;
}
/*
* css_enroll_master_write_sockets() -
*
* return: none
* fd_var(out)
*/
static int
css_enroll_master_write_sockets (fd_set * fd_var)
{
return css_enroll_write_sockets (css_Master_socket_anchor, fd_var);
}
/*
* css_enroll_exception_sockets() - Sets the fd positions in fd_set
* for the input fds we are interested in detecting error conditions
* return: last fd
*
* anchor_p(int)
* fd_var(out)
*/
static int
css_enroll_exception_sockets (SOCKET_QUEUE_ENTRY * anchor_p, fd_set * fd_var)
{
SOCKET_QUEUE_ENTRY *temp;
SOCKET max_fd = 0;
FD_ZERO (fd_var);
for (temp = anchor_p; temp; temp = temp->next)
{
if (!IS_INVALID_SOCKET (temp->fd))
{
FD_SET (temp->fd, fd_var);
if (temp->fd > max_fd)
{
max_fd = temp->fd;
}
}
}
return (int) max_fd;
}
/*
* css_enroll_master_exception_sockets()
* return: last fd
* fd_var(out)
*/
static int
css_enroll_master_exception_sockets (fd_set * fd_var)
{
return css_enroll_exception_sockets (css_Master_socket_anchor, fd_var);
}
/*
* css_master_select_error() - Check status of all known fds and remove those
* that are closed
* return: none
*/
static void
css_master_select_error (void)
{
#if !defined(WINDOWS)
int rv;
SOCKET_QUEUE_ENTRY *temp;
again:
rv = pthread_mutex_lock (&css_Master_socket_anchor_lock);
for (temp = css_Master_socket_anchor; temp; temp = temp->next)
{
if (!IS_INVALID_SOCKET (temp->fd) && fcntl (temp->fd, F_GETFL, 0) < 0)
{
#if defined(DEBUG)
if (css_Active_server_count > 0)
{
css_Active_server_count--;
}
#endif
#if !defined(WINDOWS)
if (temp->ha_mode)
{
hb_cleanup_conn_and_start_process (temp->conn_ptr, temp->fd);
}
else
#endif
{
css_remove_entry_by_conn (temp->conn_ptr, &css_Master_socket_anchor);
}
pthread_mutex_unlock (&css_Master_socket_anchor_lock);
goto again;
}
}
pthread_mutex_unlock (&css_Master_socket_anchor_lock);
#endif
}
/*
* css_check_master_socket_input() - checks if there is input from a client
* that must be processed
* return: none
* count(in/out)
* fd_var(in/out)
*/
static void
css_check_master_socket_input (int *count, fd_set * fd_var)
{
#if !defined(WINDOWS)
int rv;
#endif
SOCKET_QUEUE_ENTRY *temp, *next;
SOCKET new_fd;
#if !defined(WINDOWS)
rv = pthread_mutex_lock (&css_Master_socket_anchor_lock);
#endif
for (temp = css_Master_socket_anchor; *count && temp; temp = next)
{
next = temp->next;
if (!IS_INVALID_SOCKET (temp->fd) && FD_ISSET (temp->fd, fd_var))
{
FD_CLR (temp->fd, fd_var);
(*count)--;
if (temp->fd == css_Master_socket_fd[0] || temp->fd == css_Master_socket_fd[1])
{
new_fd = css_master_accept (temp->fd);
if (!IS_INVALID_SOCKET (new_fd))
{
css_process_new_connection (new_fd);
}
}
else if (!IS_INVALID_SOCKET (temp->fd))
{
if (temp->info_p)
{
css_process_info_request (temp->conn_ptr);
}
else
{
if (temp->ha_mode)
{
css_process_heartbeat_request (temp->conn_ptr);
}
else
{
#if defined(DEBUG)
if (css_Active_server_count > 0)
{
css_Active_server_count--;
}
#endif
#if !defined(WINDOWS)
if (auto_Restart_server)
{
/* *INDENT-OFF* */
master_Server_monitor->produce_job (server_monitor::job_type::REVIVE_SERVER, -1, "", "", temp->name);
/* *INDENT-ON* */
}
#endif
css_remove_entry_by_conn (temp->conn_ptr, &css_Master_socket_anchor);
}
}
/* stop loop in case an error caused temp to be deleted */
break;
}
}
}
#if !defined(WINDOWS)
pthread_mutex_unlock (&css_Master_socket_anchor_lock);
#endif
}
/*
* css_check_master_socket_output()
* return: none
*
* Note: currently no-op.
*/
static void
css_check_master_socket_output (void)
{
}
/*
* css_check_master_socket_exception() - Checks for exception conditions for
* open sockets
* return: 0 if master will terminate, otherwise 1.
* fd_var(in/out)
*/
static int
css_check_master_socket_exception (fd_set * fd_var)
{
#if !defined(WINDOWS)
int rv;
#endif
SOCKET_QUEUE_ENTRY *temp;
again:
#if !defined(WINDOWS)
rv = pthread_mutex_lock (&css_Master_socket_anchor_lock);
#endif
for (temp = css_Master_socket_anchor; temp; temp = temp->next)
{
if (!IS_INVALID_SOCKET (temp->fd) && ((FD_ISSET (temp->fd, fd_var) ||
#if !defined(WINDOWS)
(fcntl (temp->fd, F_GETFL, 0) < 0) ||
#endif /* !WINDOWS */
(temp->error_p != 0) || (temp->conn_ptr == NULL)
|| (temp->conn_ptr->status == CONN_CLOSED))))
{
#if defined(DEBUG)
if (css_Active_server_count > 0)
{
css_Active_server_count--;
}
#endif
FD_CLR (temp->fd, fd_var);
if (temp->fd == css_Master_socket_fd[0] || temp->fd == css_Master_socket_fd[1])
{
#if !defined(WINDOWS)
pthread_mutex_unlock (&css_Master_socket_anchor_lock);
#endif
return (0);
}
#if !defined(WINDOWS)
if (temp->ha_mode)
{
hb_cleanup_conn_and_start_process (temp->conn_ptr, temp->fd);
}
else
#endif
{
css_remove_entry_by_conn (temp->conn_ptr, &css_Master_socket_anchor);
}
#if !defined(WINDOWS)
pthread_mutex_unlock (&css_Master_socket_anchor_lock);
#endif
goto again;
}
}
#if !defined(WINDOWS)
pthread_mutex_unlock (&css_Master_socket_anchor_lock);
#endif
return (1);
}
/*
* css_master_loop() - main loop for master
* return: none
*/
static void
css_master_loop (void)
{
fd_set read_fd, write_fd, exception_fd;
static struct timeval timeout;
int rc, run_code;
run_code = 1;
while (run_code)
{
int max_fd, max_fd1, max_fd2, max_fd3;
max_fd1 = css_enroll_master_read_sockets (&read_fd);
max_fd2 = css_enroll_master_write_sockets (&write_fd);
max_fd3 = css_enroll_master_exception_sockets (&exception_fd);
max_fd = MAX (MAX (max_fd1, max_fd2), max_fd3);
timeout.tv_sec = css_Master_timeout_value_in_seconds;
timeout.tv_usec = css_Master_timeout_value_in_microseconds;
rc = select (max_fd + 1, &read_fd, &write_fd, &exception_fd, &timeout);
switch (rc)
{
case 0:
run_code = css_master_timeout ();
break;
case -1:
/* switch error */
css_master_select_error ();
break;
default:
css_check_master_socket_input (&rc, &read_fd);
css_check_master_socket_output ();
run_code = css_check_master_socket_exception (&exception_fd);
break;
}
}
}
/*
* main() - master main function
* return: EXIT_SUCCESS/EXIT_FAILURE
*/
int
main (int argc, char **argv)
{
int port_id;
CSS_CONN_ENTRY *conn;
static const char suffix[] = "_master.err";
char hostname[CUB_MAXHOSTNAMELEN + sizeof (suffix)];
char *errlog = NULL;
int status = EXIT_SUCCESS;
const char *msg_format;
bool util_config_ret;
if (utility_initialize () != NO_ERROR)
{
return EXIT_FAILURE;
}
#if defined(WINDOWS)
FreeConsole ();
if (css_windows_startup () < 0)
{
printf ("Unable to initialize Winsock.\n");
status = EXIT_FAILURE;
goto cleanup;
}
#endif /* WINDOWS */
util_config_ret = master_util_config_startup ((argc > 1) ? argv[1] : NULL, &port_id);
if (GETHOSTNAME (hostname, CUB_MAXHOSTNAMELEN) == 0)
{
/* css_gethostname won't null-terminate if the name is overlong. Put in a guaranteed null-terminator of our own
* so that strcat doesn't go wild. */
hostname[CUB_MAXHOSTNAMELEN] = '\0';
strcat (hostname, suffix);
errlog = hostname;
}
if (er_init (errlog, ER_NEVER_EXIT) != NO_ERROR)
{
PRINT_AND_LOG_ERR_MSG ("Failed to initialize error manager.\n");
status = EXIT_FAILURE;
goto cleanup;
}
if (util_config_ret == false)
{
msg_format = msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_MASTER, MASTER_MSG_NO_PARAMETERS);
css_master_error (msg_format);
util_log_write_errstr (msg_format);
status = EXIT_FAILURE;
goto cleanup;
}
if (__gv_cvar.css_does_master_exist (port_id))
{
msg_format = msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_MASTER, MASTER_MSG_DUPLICATE);
util_log_write_errstr (msg_format, argv[0]);
/* printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_MASTER, MASTER_MSG_DUPLICATE), argv[0]); */
status = EXIT_FAILURE;
goto cleanup;
}
TPRINTF (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_MASTER, MASTER_MSG_STARTING), 0);
/* close the message catalog and let the master daemon reopen. */
(void) msgcat_final ();
er_final (ER_ALL_FINAL);
#if !defined(WINDOWS)
if (envvar_get ("NO_DAEMON") == NULL)
{
css_daemon_start ();
}
#endif
(void) utility_initialize ();
(void) er_init (errlog, ER_NEVER_EXIT);
time (&css_Start_time);
if (css_master_init (port_id, css_Master_socket_fd) != NO_ERROR)
{
PRINT_AND_LOG_ERR_MSG ("%s: %s\n", argv[0], db_error_string (1));
css_master_error (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_MASTER, MASTER_MSG_PROCESS_ERROR));
status = EXIT_FAILURE;
goto cleanup;
}
if (envvar_get ("NO_DAEMON") != NULL)
{
(void) os_set_signal_handler (SIGINT, css_master_cleanup);
}
#if !defined(WINDOWS)
if (!HA_DISABLED ())
{
if (hb_master_init () != NO_ERROR)
{
status = EXIT_FAILURE;
goto cleanup;
}
}
auto_Restart_server = prm_get_bool_value (PRM_ID_AUTO_RESTART_SERVER);
// Since master_Server_monitor is a module for restarting abnormally terminated cub_server,
// it is initialized only when the 'auto_restart_server' parameter is set to true.
if (auto_Restart_server)
{
// *INDENT-OFF*
master_Server_monitor.reset (new server_monitor ());
// *INDENT-ON*
}
#endif
conn = __gv_cvar.css_make_conn (css_Master_socket_fd[0]);
css_add_request_to_socket_queue (conn, false, NULL, css_Master_socket_fd[0], READ_WRITE, 0,
&css_Master_socket_anchor);
conn = __gv_cvar.css_make_conn (css_Master_socket_fd[1]);
css_add_request_to_socket_queue (conn, false, NULL, css_Master_socket_fd[1], READ_WRITE, 0,
&css_Master_socket_anchor);
css_master_loop ();
css_master_cleanup (SIGINT);
css_master_error (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_MASTER, MASTER_MSG_EXITING));
cleanup:
#if defined(WINDOWS)
/* make sure Winsock is properly closed */
css_windows_shutdown ();
#endif /* WINDOWS */
msgcat_final ();
er_final (ER_ALL_FINAL);
return status;
}
/*
* These are the queuing routines for the Master socket queue (which is the
* queue used by the Master Server) and the Open socket queue (which is the
* queue used by the servers).
*/
/*
* css_free_entry() -
* return: void
* entry_p(in/out):
*/
static void
css_free_entry (SOCKET_QUEUE_ENTRY * entry_p)
{
if (entry_p->conn_ptr)
{
__gv_cvar.css_free_conn (entry_p->conn_ptr);
}
if (entry_p->name)
{
free_and_init (entry_p->name);
}
if (entry_p->version_string)
{
free_and_init (entry_p->version_string);
}
if (entry_p->env_var)
{
free_and_init (entry_p->env_var);
}
/* entry->fd has already been closed by css_free_conn */
free_and_init (entry_p);
}
/*
* css_remove_entry_by_conn() -
* return: void
* conn_p(in):
* anchor_p(out):
*/
void
css_remove_entry_by_conn (CSS_CONN_ENTRY * conn_p, SOCKET_QUEUE_ENTRY ** anchor_p)
{
SOCKET_QUEUE_ENTRY *p, *q;
if (conn_p == NULL)
{
return;
}
for (p = *anchor_p, q = NULL; p; q = p, p = p->next)
{
if (p->conn_ptr != conn_p)
{
continue;
}
if (p == *anchor_p)
{
*anchor_p = p->next;
}
else
{
q->next = p->next;
}
css_free_entry (p);
break;
}
}
/*
* css_add_request_to_socket_queue() -
* return:
* conn_p(in):
* info_p(in):
* name_p(in):
* fd(in):
* fd_type(in):
* pid(in):
* anchor_p(out):
*/
SOCKET_QUEUE_ENTRY *
css_add_request_to_socket_queue (CSS_CONN_ENTRY * conn_p, int info_p, char *name_p, SOCKET fd, int fd_type, int pid,
SOCKET_QUEUE_ENTRY ** anchor_p)
{
SOCKET_QUEUE_ENTRY *p;
p = (SOCKET_QUEUE_ENTRY *) malloc (sizeof (SOCKET_QUEUE_ENTRY));
if (p == NULL)
{
return NULL;
}
p->conn_ptr = conn_p;
p->fd = fd;
p->ha_mode = FALSE;
if (name_p)
{
p->name = (char *) malloc (strlen (name_p) + 1);
if (p->name)
{
strcpy (p->name, name_p);
#if !defined(WINDOWS)
if (IS_MASTER_CONN_NAME_HA_SERVER (p->name) || IS_MASTER_CONN_NAME_HA_COPYLOG (p->name)
|| IS_MASTER_CONN_NAME_HA_APPLYLOG (p->name))
{
p->ha_mode = TRUE;
}
#endif
}
}
else
{
p->name = NULL;
}
p->version_string = NULL;
p->env_var = NULL;
p->fd_type = fd_type;
p->queue_p = 0;
p->info_p = info_p;
p->error_p = FALSE;
p->pid = pid;
p->db_error = 0;
p->next = *anchor_p;
p->port_id = -1;
*anchor_p = p;
return p;
}
/*
* css_return_entry_of_server() -
* return:
* name_p(in):
* anchor_p(in):
*/
SOCKET_QUEUE_ENTRY *
css_return_entry_of_server (char *name_p, SOCKET_QUEUE_ENTRY * anchor_p)
{
SOCKET_QUEUE_ENTRY *p;
if (name_p == NULL)
{
return NULL;
}
for (p = anchor_p; p; p = p->next)
{
if (p->name && strcmp (p->name, name_p) == 0)
{
return p;
}
#if !defined(WINDOWS)
/* if HA server exist */
if (p->name && (IS_MASTER_CONN_NAME_HA_SERVER (p->name)))
{
if (strcmp ((char *) (p->name + 1), name_p) == 0)
{
return p;
}
}
#endif
}
return NULL;
}
/*
* css_return_entry_by_conn() -
* return:
* conn(in):
* anchor_p(in):
*/
SOCKET_QUEUE_ENTRY *
css_return_entry_by_conn (CSS_CONN_ENTRY * conn_p, SOCKET_QUEUE_ENTRY ** anchor_p)
{
SOCKET_QUEUE_ENTRY *p;
if (conn_p == NULL)
{
return NULL;
}
for (p = *anchor_p; p; p = p->next)
{
if (p->conn_ptr != conn_p)
{
continue;
}
return (p);
}
return NULL;
}
#if !defined(WINDOWS)
/*
* css_daemon_start() - detach a process from login session context
* return: none
*/
static void
css_daemon_start (void)
{
int childpid, fd;
#if defined (sun)
struct rlimit rlp;
#endif /* sun */
int fd_max;
int ppid = getpid ();
/* If we were started by init (process 1) from the /etc/inittab file there's no need to detatch. This test is
* unreliable due to an unavoidable ambiguity if the process is started by some other process and orphaned (i.e., if
* the parent process terminates before we get here). */
if (getppid () == 1)
{
goto out;
}
/*
* Ignore the terminal stop signals (BSD).
*/
#ifdef SIGTTOU
if (os_set_signal_handler (SIGTTOU, SIG_IGN) == SIG_ERR)
{
exit (0);
}
#endif
#ifdef SIGTTIN
if (os_set_signal_handler (SIGTTIN, SIG_IGN) == SIG_ERR)
{
exit (0);
}
#endif
#ifdef SIGTSTP
if (os_set_signal_handler (SIGTSTP, SIG_IGN) == SIG_ERR)
{
exit (0);
}
#endif
/*
* Call fork and have the parent exit.
* This does several things. First, if we were started as a simple shell
* command, having the parent terminate makes the shell think that the
* command is done. Second, the child process inherits the process group ID
* of the parent but gets a new process ID, so we are guaranteed that the
* child is not a process group leader, which is a prerequirement for the
* call to setsid
*/
childpid = fork ();
if (childpid < 0)
{
MASTER_ER_SET (ER_WARNING_SEVERITY, ARG_FILE_LINE, ERR_CSS_CANNOT_FORK, 0);
}
else if (childpid > 0)
{
exit (0); /* parent goes bye-bye */
}
else
{
/*
* Wait until the parent process has finished. Coded with polling since
* the parent should finish immediately. SO, it is unlikely that we are
* going to loop at all.
*/
while (getppid () == ppid)
{
sleep (1);
}
}
/*
* Create a new session and make the child process the session leader of
* the new session, the process group leader of the new process group.
* The child process has no controlling terminal.
*/
if (os_set_signal_handler (SIGHUP, SIG_IGN) == SIG_ERR)
{
exit (0); /* fail to immune from pgrp leader death */
}
setsid ();
out:
/*
* Close unneeded file descriptors which prevent the daemon from holding
* open any descriptors that it may have inherited from its parent which
* could be a shell. For now, leave in/out/err open
*/
fd_max = css_get_max_socket_fds ();
for (fd = 3; fd < fd_max; fd++)
{
close (fd);
}
errno = 0; /* Reset errno from last close */
/*
* The file mode creation mask that is inherited could be set to deny
* certain permissions. Therefore, clear the file mode creation mask.
*/
umask (0);
}
#endif