438 lines
13 KiB
C
438 lines
13 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2012 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include "mdef.h"
|
|
|
|
#include "gtm_stdlib.h" /* for exit() */
|
|
|
|
#include <sys/wait.h>
|
|
#include <sys/time.h>
|
|
#include <sys/un.h>
|
|
#include <sys/sem.h>
|
|
#include "gtm_ctype.h"
|
|
#include <stddef.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
|
|
#include "gtm_string.h"
|
|
#include "gtm_socket.h"
|
|
#include "gtm_fcntl.h"
|
|
#include "gtm_unistd.h"
|
|
#include "gtm_stdio.h"
|
|
#include "gtm_stat.h"
|
|
#include "gt_timer.h"
|
|
|
|
#include "io.h"
|
|
#include "gtmsecshr.h"
|
|
#include "iosp.h"
|
|
#include "error.h"
|
|
#include "eintr_wrappers.h"
|
|
#include "util.h"
|
|
#include "send_msg.h"
|
|
#include "gtmmsg.h"
|
|
#include "wcs_backoff.h"
|
|
#include "trans_log_name.h"
|
|
#include "gdsroot.h"
|
|
#include "gtm_facility.h"
|
|
#include "fileinfo.h"
|
|
#include "gdsbt.h"
|
|
#include "gdsfhead.h"
|
|
#include "filestruct.h"
|
|
#include "gtm_logicals.h"
|
|
#include "secshr_client.h"
|
|
|
|
GBLREF struct sockaddr_un gtmsecshr_sock_name;
|
|
GBLREF key_t gtmsecshr_key;
|
|
GBLREF int gtmsecshr_sockpath_len;
|
|
GBLREF int gtmsecshr_sockfd;
|
|
GBLREF mstr gtmsecshr_pathname;
|
|
GBLREF int server_start_tries;
|
|
GBLREF boolean_t gtmsecshr_sock_init_done;
|
|
GBLREF uint4 process_id;
|
|
GBLREF ipcs_mesg db_ipcs;
|
|
|
|
static int secshr_sem;
|
|
static int gtmsecshr_file_check_done = 0;
|
|
static mstr gtmsecshr_logname;
|
|
static char gtmsecshr_path[MAX_TRANS_NAME_LEN];
|
|
static volatile int client_timer_popped;
|
|
static unsigned long cur_seqno = 0;
|
|
|
|
|
|
const static char readonly secshr_fail_mesg_code[][MAX_GTMSECSHR_FAIL_MESG_LEN] = {
|
|
"",
|
|
"Wake Message Failed",
|
|
"Check process alive failed",
|
|
"Remove Semaphore failed",
|
|
"Remove Shared Memory segment failed",
|
|
"Ping Message failed",
|
|
"Remove File failed",
|
|
"Continue Process failed",
|
|
};
|
|
|
|
const static char readonly secshrstart_error_code[][MAX_GTMSECSHR_FAIL_MESG_LEN] = {
|
|
"",
|
|
"gtmsecshr unable to set-uid to root",
|
|
"gtmsecshr unable to set-gid to root",
|
|
"gtmsecshr unable to open log file",
|
|
"gtmsecshr unable to dup stdout and stderr",
|
|
"The environmental variable gtm_dist is pointing to an invalid path",
|
|
"Unable to exec gtmsecshr",
|
|
"gtmsecshr unable to create a child process",
|
|
"The environmental variable gtm_log is pointing to an invalid path",
|
|
"Error with gtmsecshr semaphore",
|
|
"Invalid gtm exit message",
|
|
"See syslog for cause of failure",
|
|
};
|
|
|
|
error_def(ERR_GTMSECSHR);
|
|
error_def(ERR_GTMSECSHRPERM);
|
|
error_def(ERR_GTMSECSHRSOCKET);
|
|
error_def(ERR_GTMSECSHRSRVF);
|
|
error_def(ERR_GTMSECSHRSRVFID);
|
|
error_def(ERR_GTMSECSHRSRVFIL);
|
|
error_def(ERR_GTMSECSHRSTART);
|
|
error_def(ERR_GTMSECSHRTMPPATH);
|
|
error_def(ERR_LOGTOOLONG);
|
|
error_def(ERR_SYSCALL);
|
|
error_def(ERR_TEXT);
|
|
|
|
#define MAX_RETRIES 7
|
|
#define CLIENT_ACK_TIMER 5
|
|
|
|
#define START_SERVER \
|
|
{ \
|
|
int arraysize, errorindex; \
|
|
\
|
|
if (0 != (create_server_status = create_server())) \
|
|
{ \
|
|
assert(ARRAYSIZE(secshrstart_error_code) == (SYSLOGHASERRORDETAIL + 1)); \
|
|
errorindex = create_server_status; \
|
|
if ((0 > errorindex) || (SYSLOGHASERRORDETAIL < errorindex)) \
|
|
errorindex = SYSLOGHASERRORDETAIL; \
|
|
assert(0 <= errorindex); \
|
|
assert(ARRAYSIZE(secshrstart_error_code) > errorindex); \
|
|
gtm_putmsg(VARLSTCNT(9) ERR_GTMSECSHRSTART, 3, RTS_ERROR_TEXT("Client"), \
|
|
process_id, ERR_TEXT, 2, \
|
|
RTS_ERROR_STRING(secshrstart_error_code[errorindex])); \
|
|
if (FATALFAILURE(create_server_status)) \
|
|
{ \
|
|
gtmsecshr_sock_cleanup(CLIENT); \
|
|
return create_server_status; \
|
|
} \
|
|
/* For transient failures we will continue after printing out message */ \
|
|
} \
|
|
hiber_start(3000); /* 3000 ms (3 sec) to allow server to come up */ \
|
|
}
|
|
|
|
#define SETUP_FOR_RECV \
|
|
{ \
|
|
recd = 0; \
|
|
mesg_len = 0; \
|
|
recv_ptr = (char *)&mesg; \
|
|
recv_len = SIZEOF(mesg); \
|
|
client_timer_popped = 0; \
|
|
msec_timeout = timeout2msec(CLIENT_ACK_TIMER); \
|
|
start_timer(timer_id, msec_timeout, client_timer_handler, 0, NULL); \
|
|
}
|
|
|
|
void client_timer_handler(void)
|
|
{
|
|
client_timer_popped = 1;
|
|
}
|
|
|
|
int send_mesg2gtmsecshr(unsigned int code, unsigned int id, char *path, int path_len)
|
|
{
|
|
int client_sockfd, create_server_status, fcntl_res;
|
|
int req_code, wait_count = 0;
|
|
int mesg_len;
|
|
int recv_len, send_len;
|
|
ssize_t recd = 0, sent, num_chars_recvd, num_chars_sent;
|
|
int save_errno, ret_code = 0, init_ret_code = 0;
|
|
int loop_count = 0;
|
|
boolean_t retry = FALSE, recv_complete, send_complete;
|
|
size_t server_proc_len;
|
|
int semop_res;
|
|
int selstat, status;
|
|
char *recv_ptr, *send_ptr;
|
|
struct sockaddr_un server_proc;
|
|
struct sembuf sop[4];
|
|
fd_set wait_on_fd;
|
|
gtmsecshr_mesg mesg;
|
|
TID timer_id;
|
|
int4 msec_timeout;
|
|
char *gtm_tmp_ptr;
|
|
|
|
timer_id = (TID)send_mesg2gtmsecshr;
|
|
|
|
if (!gtmsecshr_file_check_done)
|
|
{
|
|
struct stat stat_buf;
|
|
|
|
gtmsecshr_logname.addr = GTMSECSHR_PATH;
|
|
gtmsecshr_logname.len = SIZEOF(GTMSECSHR_PATH) - 1;
|
|
status = TRANS_LOG_NAME(>msecshr_logname, >msecshr_pathname, gtmsecshr_path, SIZEOF(gtmsecshr_path),
|
|
dont_sendmsg_on_log2long);
|
|
if (SS_NORMAL != status)
|
|
{
|
|
if (SS_LOG2LONG == status)
|
|
send_msg(VARLSTCNT(5) ERR_LOGTOOLONG, 3, gtmsecshr_logname.len, gtmsecshr_logname.addr,
|
|
SIZEOF(gtmsecshr_path) - 1);
|
|
send_msg(VARLSTCNT(9) ERR_GTMSECSHRSTART, 3, RTS_ERROR_TEXT("Client"), process_id,
|
|
ERR_TEXT, 2, RTS_ERROR_STRING(secshrstart_error_code[INVTRANSGTMSECSHR]));
|
|
rts_error(VARLSTCNT(9) ERR_GTMSECSHRSTART, 3, RTS_ERROR_TEXT("Client"), process_id,
|
|
ERR_TEXT, 2, RTS_ERROR_STRING(secshrstart_error_code[INVTRANSGTMSECSHR]));
|
|
}
|
|
gtmsecshr_pathname.addr[gtmsecshr_pathname.len] = '\0';
|
|
if (-1 == Stat(gtmsecshr_pathname.addr, &stat_buf))
|
|
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("stat"), CALLFROM, errno);
|
|
if ((ROOTUID != stat_buf.st_uid) ||
|
|
!(stat_buf.st_mode & S_ISUID))
|
|
rts_error(VARLSTCNT(1) ERR_GTMSECSHRPERM);
|
|
gtmsecshr_file_check_done = 1;
|
|
}
|
|
if (!gtmsecshr_sock_init_done && ((init_ret_code = gtmsecshr_sock_init(CLIENT)) > 0))
|
|
return init_ret_code;
|
|
while (MAX_RETRIES > loop_count)
|
|
{ /* first, try the sendto */
|
|
req_code = mesg.code = code;
|
|
mesg.len = (int4)(GTM_MESG_HDR_SIZE);
|
|
if (REMOVE_FILE == code)
|
|
{
|
|
memcpy(mesg.mesg.path, path, path_len);
|
|
mesg.len += path_len;
|
|
} else if (FLUSH_DB_IPCS_INFO == code)
|
|
{
|
|
memcpy(&mesg.mesg.db_ipcs, &db_ipcs, SIZEOF(struct ipcs_mesg_struct));
|
|
/* most of the time file length is much smaller than MAX_TRANS_NAME_LEN */
|
|
mesg.len += (SIZEOF(struct ipcs_mesg_struct) - MAX_TRANS_NAME_LEN);
|
|
mesg.len += mesg.mesg.db_ipcs.fn_len;
|
|
} else
|
|
{
|
|
mesg.mesg.id = id;
|
|
mesg.len += SIZEOF(mesg.mesg.id);
|
|
}
|
|
mesg.pid = process_id;
|
|
mesg.seqno = ++cur_seqno;
|
|
sent = 0;
|
|
send_ptr = (char *)&mesg;
|
|
send_len = mesg_len = mesg.len;
|
|
for (send_complete = FALSE; !send_complete;)
|
|
{
|
|
SENDTO_SOCK(gtmsecshr_sockfd, send_ptr, send_len, 0, (struct sockaddr *)>msecshr_sock_name,
|
|
(GTM_SOCKLEN_TYPE)gtmsecshr_sockpath_len, num_chars_sent);
|
|
if (0 <= num_chars_sent)
|
|
{
|
|
sent += num_chars_sent;
|
|
if (sent == mesg_len)
|
|
{
|
|
send_complete = TRUE;
|
|
break;
|
|
}
|
|
send_ptr += num_chars_sent;
|
|
send_len -= (int)num_chars_sent;
|
|
} else
|
|
{
|
|
/* sendto failed - start server and attempt to resend */
|
|
save_errno = errno;
|
|
if ((EISCONN == save_errno) || (EBADF == save_errno))
|
|
{
|
|
gtmsecshr_sock_cleanup(CLIENT);
|
|
gtmsecshr_sock_init(CLIENT);
|
|
wcs_backoff(loop_count + 1);
|
|
} else
|
|
{
|
|
# ifdef SECSHR_DEBUG
|
|
util_out_print("secshr_client starting server due to sendto failure errno = !UL",
|
|
TRUE, save_errno);
|
|
# endif
|
|
send_msg(VARLSTCNT(10) ERR_GTMSECSHRSRVF, 3, RTS_ERROR_TEXT("Client"), process_id,
|
|
ERR_TEXT, 2, RTS_ERROR_TEXT("sendto to gtmsecshr failed"), save_errno);
|
|
/* if gtm_tmp is not defined, show default path */
|
|
if (gtm_tmp_ptr = GETENV("gtm_tmp"))
|
|
send_msg(VARLSTCNT(8) ERR_GTMSECSHRTMPPATH, 2, RTS_ERROR_TEXT(gtm_tmp_ptr),
|
|
ERR_TEXT, 2, RTS_ERROR_TEXT("(from $gtm_tmp)"));
|
|
else
|
|
send_msg(VARLSTCNT(4) ERR_GTMSECSHRTMPPATH, 2, RTS_ERROR_TEXT("/tmp"));
|
|
START_SERVER;
|
|
}
|
|
loop_count++;
|
|
break;
|
|
}
|
|
}
|
|
if (!send_complete)
|
|
continue;
|
|
SETUP_FOR_RECV;
|
|
for (save_errno = 0, recv_complete = FALSE; !recv_complete;)
|
|
{
|
|
num_chars_recvd = recvfrom(gtmsecshr_sockfd, recv_ptr, MAX_MESG, 0, (struct sockaddr *)0,
|
|
(GTM_SOCKLEN_TYPE *)0);
|
|
if (0 <= num_chars_recvd)
|
|
{
|
|
recd += num_chars_recvd;
|
|
if ((0 == mesg_len) && (SIZEOF(int) <= recd))
|
|
mesg_len = mesg.len;
|
|
if (recd == mesg_len)
|
|
{
|
|
if (mesg.seqno == cur_seqno)
|
|
{
|
|
recv_complete = TRUE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
cancel_timer(timer_id);
|
|
recv_complete = FALSE;
|
|
save_errno = 0;
|
|
SETUP_FOR_RECV;
|
|
continue;
|
|
}
|
|
|
|
}
|
|
recv_ptr += num_chars_recvd;
|
|
recv_len -= (int)num_chars_recvd;
|
|
continue;
|
|
}
|
|
if (client_timer_popped)
|
|
break;
|
|
if (EINTR == errno)
|
|
continue;
|
|
save_errno = errno;
|
|
if (EBADF == errno)
|
|
break;
|
|
send_msg(VARLSTCNT(10) ERR_GTMSECSHRSRVF, 3, RTS_ERROR_TEXT("Client"), process_id,
|
|
ERR_TEXT, 2, RTS_ERROR_TEXT("recvfrom from gtmsecshr failed"), save_errno);
|
|
if ((ECONNRESET == save_errno) || (ENOTCONN == save_errno))
|
|
{
|
|
num_chars_recvd = 0;
|
|
break;
|
|
}
|
|
gtmsecshr_sock_cleanup(CLIENT);
|
|
cancel_timer(timer_id);
|
|
return save_errno;
|
|
}
|
|
if ((client_timer_popped || (EBADF == save_errno)))
|
|
{
|
|
gtmsecshr_sock_cleanup(CLIENT);
|
|
gtmsecshr_sock_init(CLIENT);
|
|
retry = TRUE;
|
|
if (client_timer_popped)
|
|
{
|
|
# ifdef SECSHR_DEBUG
|
|
util_out_print("secshr_client starting server due to recvfrom timeout", TRUE);
|
|
# endif
|
|
START_SERVER;
|
|
}
|
|
loop_count++;
|
|
continue;
|
|
}
|
|
cancel_timer(timer_id);
|
|
if (ret_code = mesg.code) /* assign to ret_code, then test value */
|
|
{
|
|
switch(req_code)
|
|
{
|
|
case REMOVE_FILE :
|
|
if (retry && ENOENT == ret_code)
|
|
ret_code = 0; /* assume first time worked */
|
|
else
|
|
send_msg(VARLSTCNT(14) ERR_GTMSECSHRSRVFIL, 7, RTS_ERROR_TEXT("Client"),
|
|
process_id, mesg.pid, req_code, RTS_ERROR_TEXT(mesg.mesg.path),
|
|
ERR_TEXT, 2, RTS_ERROR_STRING(secshr_fail_mesg_code[req_code]),
|
|
mesg.code);
|
|
break;
|
|
case REMOVE_SEM:
|
|
case REMOVE_SHMMEM:
|
|
if (retry && (mesg.mesg.id == id))
|
|
ret_code = 0;
|
|
break;
|
|
case FLUSH_DB_IPCS_INFO:
|
|
break;
|
|
default :
|
|
if (EPERM != mesg.code && EACCES != mesg.code)
|
|
send_msg(VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_TEXT("Client"),
|
|
process_id, mesg.pid, req_code, mesg.mesg.id, ERR_TEXT, 2,
|
|
RTS_ERROR_STRING(secshr_fail_mesg_code[req_code]),
|
|
mesg.code);
|
|
break;
|
|
|
|
}
|
|
}
|
|
if (recv_complete)
|
|
break;
|
|
}
|
|
if (MAX_RETRIES == loop_count)
|
|
{
|
|
ret_code = -1;
|
|
gtm_putmsg(VARLSTCNT(9) ERR_GTMSECSHRSRVF, 3, RTS_ERROR_TEXT("Client"), process_id,
|
|
ERR_TEXT, 2, RTS_ERROR_TEXT("Unable to communicate with gtmsecshr"));
|
|
}
|
|
if (ONETIMESOCKET == init_ret_code)
|
|
gtmsecshr_sock_cleanup(CLIENT);
|
|
return ret_code;
|
|
}
|
|
|
|
int create_server(void)
|
|
{
|
|
int child_pid, done_pid, status = 0;
|
|
# ifdef _BSD
|
|
union wait chld_status;
|
|
# define CSTAT chld_status
|
|
# else
|
|
# define CSTAT status
|
|
# endif
|
|
int save_errno;
|
|
|
|
if (0 == (child_pid = fork())) /* BYPASSOK: we exec immediately, no FORK_CLEAN needed */
|
|
{
|
|
process_id = getpid();
|
|
|
|
/* do exec using gtmsecshr_path, which was initialize in file check code - send_mesg2gtmsecshr */
|
|
status = EXECL(gtmsecshr_path, gtmsecshr_path, 0);
|
|
if (-1 == status)
|
|
{
|
|
send_msg(VARLSTCNT(9) ERR_GTMSECSHRSTART, 3, RTS_ERROR_TEXT("Client"), process_id,
|
|
ERR_TEXT, 2, RTS_ERROR_STRING(secshrstart_error_code[INVTRANSGTMSECSHR]));
|
|
exit(UNABLETOEXECGTMSECSHR);
|
|
}
|
|
} else
|
|
{
|
|
if (-1 == child_pid)
|
|
{
|
|
status = GNDCHLDFORKFLD;
|
|
gtm_putmsg(VARLSTCNT(10) ERR_GTMSECSHRSTART, 3, RTS_ERROR_TEXT("Client"), process_id,
|
|
ERR_TEXT, 2, RTS_ERROR_TEXT("Failed to fork off gtmsecshr"), errno);
|
|
/* Sleep for a while and hope a subsequent fork will succeed */
|
|
hiber_start(1000);
|
|
}
|
|
for ( ; !status ; )
|
|
{
|
|
/* To prevent a warning message that the compiler issues */
|
|
done_pid = wait(&CSTAT);
|
|
if (done_pid == child_pid)
|
|
{
|
|
status = WEXITSTATUS(CSTAT);
|
|
break;
|
|
} else if (-1 == done_pid)
|
|
{
|
|
if (ECHILD == errno) /* Assume normal exit status */
|
|
break;
|
|
else if (EINTR != errno)
|
|
{
|
|
status = GNDCHLDFORKFLD;
|
|
gtm_putmsg(VARLSTCNT(10) ERR_GTMSECSHRSTART, 3, RTS_ERROR_TEXT("Client"), process_id,
|
|
ERR_TEXT, 2, RTS_ERROR_TEXT("Error spawning gtmsecshr"), errno);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|