fis-gtm/sr_unix/gtmsecshr.c

1129 lines
45 KiB
C

/****************************************************************
* *
* Copyright 2001, 2013 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. *
* *
****************************************************************/
/********************************************************************************************
* W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G *
* *
* This routine (gtmsecshr) runs as setuid to root to perform various functions on behalf *
* of GT.M processes. Extreme care must be taken to prevent all forms of deceptive access, *
* linking with unauthorized libraries, etc. Same applies to anything it calls. *
* *
* W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G *
********************************************************************************************/
#include "mdef.h"
#include "main_pragma.h"
#include <sys/time.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include "gtm_stat.h"
#include "gtm_socket.h"
#include <sys/un.h>
#include <sys/param.h>
#include <signal.h>
#if !defined(_AIX) && !defined(__linux__) && !defined(__hpux) && !defined(__CYGWIN__) && !defined(__MVS__)
# include <siginfo.h>
#endif
#include "gtm_syslog.h"
#include "gtm_limits.h"
#include "gtm_stdlib.h"
#include "gtm_sem.h"
#include "gtm_string.h"
#include "gtm_fcntl.h"
#include <errno.h>
#include "gtm_time.h"
#include "gtm_unistd.h"
#include "gtm_stdio.h"
#include "gtm_permissions.h"
#if defined(__MVS__)
# include "gtm_zos_io.h"
#endif
#include "cli.h"
#include "error.h"
#include "gtm_logicals.h"
#include "io.h"
#include "gtmsecshr.h"
#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "mutex.h"
#include "iosp.h"
#include "gt_timer.h"
#include "gtm_c_stack_trace.h"
#include "eintr_wrappers.h"
#include "eintr_wrapper_semop.h"
#include "gtmimagename.h"
#include "util.h"
#include "send_msg.h"
#include "generic_signal_handler.h"
#include "gtmmsg.h"
#include "have_crit.h"
#include "sig_init.h"
#include "gtmio.h"
#include "file_head_read.h"
#include "file_head_write.h"
#include "gtm_env_init.h" /* for gtm_env_init() prototype */
#include "gtm_imagetype_init.h"
#include "gtm_threadgbl_init.h"
#include "hashtab.h"
#include "fork_init.h"
#ifdef UNICODE_SUPPORTED
# include "gtm_icu_api.h"
# include "gtm_utf8.h"
#endif
#define execname "gtmsecshr" /* this is what this executable is supposed to be called. A different name is verboten */
#define intent_open "for open" /* FLUSH_DB_IPCS_INFO types */
#define intent_close "for close"
#define TZLOCATOR "TZ="
#define NEWLINE 0x0a
GBLDEF CLI_ENTRY *cmd_ary = NULL; /* GTMSECSHR does not have any command tables so initialize command array to NULL */
GBLREF int gtmsecshr_sockfd;
GBLREF struct sockaddr_un gtmsecshr_sock_name;
GBLREF int gtmsecshr_sockpath_len;
GBLREF key_t gtmsecshr_key;
GBLREF uint4 process_id;
GBLREF boolean_t need_core;
GBLREF boolean_t first_syslog; /* Defined in util_output.c */
LITREF char gtm_release_name[];
LITREF int4 gtm_release_name_len;
static volatile int gtmsecshr_timer_popped;
static int gtmsecshr_socket_dir_len;
void clean_client_sockets(char *path);
void gtmsecshr_exit (int exit_code, boolean_t dump);
void gtmsecshr_init(char_ptr_t argv[], char **rundir, int *rundir_len);
void gtmsecshr_timer_handler(void);
void gtmsecshr_signal_handler(int sig, siginfo_t *info, void *context);
ZOS_ONLY(boolean_t gtm_tag_error(char *filename, int realtag, int desiredtag);)
error_def(ERR_ASSERT);
error_def(ERR_BADTAG);
error_def(ERR_DBFILOPERR);
error_def(ERR_DBNOTGDS);
error_def(ERR_GTMASSERT);
error_def(ERR_GTMASSERT2);
error_def(ERR_GTMCHECK);
error_def(ERR_GTMSECSHR);
error_def(ERR_GTMSECSHRBADDIR);
error_def(ERR_GTMSECSHRCHDIRF);
error_def(ERR_GTMSECSHRDMNSTARTED);
error_def(ERR_GTMSECSHRFORKF);
error_def(ERR_GTMSECSHRGETSEMFAIL);
error_def(ERR_GTMSECSHRISNOT);
error_def(ERR_GTMSECSHRNOARG0);
error_def(ERR_GTMSECSHROPCMP);
error_def(ERR_GTMSECSHRRECVF);
error_def(ERR_GTMSECSHRREMFILE);
error_def(ERR_GTMSECSHRREMSEM);
error_def(ERR_GTMSECSHRREMSEMFAIL);
error_def(ERR_GTMSECSHRREMSHM);
error_def(ERR_GTMSECSHRSCKSEL);
error_def(ERR_GTMSECSHRSEMGET);
error_def(ERR_GTMSECSHRSENDF);
error_def(ERR_GTMSECSHRSHMCONCPROC);
error_def(ERR_GTMSECSHRSOCKET);
error_def(ERR_GTMSECSHRSRVFID);
error_def(ERR_GTMSECSHRSRVFIL);
error_def(ERR_GTMSECSHRSSIDF);
error_def(ERR_GTMSECSHRSTART);
error_def(ERR_GTMSECSHRSUIDF);
error_def(ERR_GTMSECSHRTMOUT);
error_def(ERR_GTMSECSHRTMPPATH);
error_def(ERR_GTMSECSHRUPDDBHDR);
error_def(ERR_MEMORY);
error_def(ERR_OUTOFSPACE);
error_def(ERR_STACKOFLOW);
error_def(ERR_SYSCALL);
error_def(ERR_TEXT);
/* Note that this condition handler is not really properly setup as a condition handler
* in that it has none of the required condition handler macros in it. It's job is just
* to perform shutdown logic when it is called. No further handlers are called hence
* the streamlined nature.
*/
CONDITION_HANDLER(gtmsecshr_cond_hndlr)
{
START_CH;
gtmsecshr_exit(arg, DUMPABLE ? TRUE : FALSE);
}
/* If there was a leftover socket, the client will append a lower case letter
* which we take as a flag to delete all sockets for the current client pid
*/
void clean_client_sockets(char *path)
{
char last, suffix;
int len;
len = STRLEN(path);
last = path[len - 1];
for (suffix = 'a'; last > suffix; suffix++)
{
path[len - 1] = suffix; /* OK since main loop is done with this */
UNLINK(path); /* We could care less about errors */
}
path[len - 1] = '\0'; /* The normal name for the pid */
UNLINK(path);
return;
}
/* For gtmsecshr, override this routine since crit doesn't exist here */
uint4 have_crit(uint4 crit_state)
{
return 0;
}
/* For gtmsecshr, no forking but still allow to create a core */
void gtm_fork_n_core(void)
{ /* Should we switch to an otherwise unpriv'd id of some sort and chdir to create the core
* in say $gtm_dist ?
*/
DUMP_CORE;
}
/* Main gtmsecshr entry point.
*
* Note the functions in util_output know the caller is gtmsecshr so ALL messages flushed through it
* automatically go to syslog. There is no flat-file logging.
*/
int main(int argc, char_ptr_t argv[])
{
int selstat;
int save_errno;
int recv_complete, send_complete;
int num_chars_recd, num_chars_sent, rundir_len;
int4 msec_timeout; /* timeout in milliseconds */
TID timer_id;
GTM_SOCKLEN_TYPE client_addr_len;
char *recv_ptr, *send_ptr, *rundir;
fd_set wait_on_fd;
struct sockaddr_un client_addr;
struct timeval input_timeval;
gtmsecshr_mesg mesg;
DCL_THREADGBL_ACCESS;
GTM_THREADGBL_INIT;
gtm_imagetype_init(GTMSECSHR_IMAGE); /* Side-effect : Sets skip_dbtriggers = TRUE if platorm lacks trigger support */
gtm_wcswidth_fnptr = NULL;
gtm_env_init(); /* read in all environment variables */
err_init(gtmsecshr_cond_hndlr);
gtmsecshr_init(argv, &rundir, &rundir_len);
timer_id = (TID)main;
while (TRUE)
{
input_timeval.tv_sec = MAX_TIMEOUT_VALUE; /* Restart timeout each interation for platforms that save
* unexpired time when select exits.
*/
input_timeval.tv_usec = 0;
FD_ZERO(&wait_on_fd);
FD_SET(gtmsecshr_sockfd, &wait_on_fd);
gtmsecshr_timer_popped = FALSE;
SELECT(gtmsecshr_sockfd+1, (void *)&wait_on_fd, NULL, NULL, &input_timeval, selstat);
if (0 > selstat)
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_GTMSECSHR, 1, process_id, ERR_GTMSECSHRSCKSEL, 0, errno);
else if (0 == selstat)
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_GTMSECSHRTMOUT);
gtmsecshr_exit(0, 0); /* Doesn't return */
}
recv_ptr = (char *)&mesg;
client_addr_len = SIZEOF(struct sockaddr_un);
msec_timeout = timeout2msec(GTMSECSHR_MESG_TIMEOUT);
DBGGSSHR((LOGFLAGS, "gtmsecshr: Select rc = %d message timeout = %d\n", selstat, msec_timeout));
start_timer(timer_id, msec_timeout, gtmsecshr_timer_handler, 0, NULL);
recv_complete = FALSE;
do
{ /* Note RECVFROM does not loop on EINTR return codes so must be handled */
num_chars_recd = (int)(RECVFROM(gtmsecshr_sockfd, (void *)recv_ptr, SIZEOF(mesg), 0,
(struct sockaddr *)&client_addr, &client_addr_len));
save_errno = errno;
DBGGSSHR((LOGFLAGS, "gtmsecshr: timer-popped: %d RECVFROM rc = %d errno = %d (only relevant if rc = "
"-1)\n", gtmsecshr_timer_popped, num_chars_recd, save_errno));
if ((0 >= num_chars_recd) && (gtmsecshr_timer_popped || EINTR != save_errno))
/* Note error includes 0 return from UDP read - should never be possible with UDP */
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_GTMSECSHR, 1,
process_id, ERR_GTMSECSHRRECVF, 0, save_errno);
if (0 < num_chars_recd)
recv_complete = TRUE; /* Only complete messages received via UDP datagram */
} while (!recv_complete);
cancel_timer(timer_id);
assert(0 < num_chars_recd);
service_request(&mesg, num_chars_recd, rundir, rundir_len);
DBGGSSHR((LOGFLAGS, "gtmsecshr: service request complete - return code: %d\n", mesg.code));
if (INVALID_COMMAND != mesg.code)
{ /* Reply if code not overridden to mean no acknowledgement required */
send_ptr = (char *)&mesg;
msec_timeout = timeout2msec(GTMSECSHR_MESG_TIMEOUT);
start_timer(timer_id, msec_timeout, gtmsecshr_timer_handler, 0, NULL);
send_complete = FALSE;
do
{
num_chars_sent = (int)(SENDTO(gtmsecshr_sockfd, send_ptr, GTM_MESG_HDR_SIZE, 0,
(struct sockaddr *)&client_addr, (GTM_SOCKLEN_TYPE)client_addr_len));
save_errno = errno;
DBGGSSHR((LOGFLAGS, "gtmsecshr: timer-popped: %d SENDTO rc = %d errno = %d (only relevant if "
"rc = -1)\n", gtmsecshr_timer_popped, num_chars_recd, save_errno));
if ((0 >= num_chars_sent) && (gtmsecshr_timer_popped || save_errno != EINTR))
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_GTMSECSHR, 1,
process_id, ERR_GTMSECSHRSENDF, 0, save_errno);
if (0 < num_chars_sent)
send_complete = TRUE;
} while (!send_complete);
cancel_timer(timer_id);
} else
DBGGSSHR((LOGFLAGS, "gtmsecshr: SENDTO reply skipped due to mesg.code = INVALID_COMMAND\n"));
assert('a' > 'F' && 'a' > '9');
if ('a' <= client_addr.sun_path[strlen(client_addr.sun_path) - 1])
clean_client_sockets(client_addr.sun_path);
}
}
void gtmsecshr_init(char_ptr_t argv[], char **rundir, int *rundir_len)
{
int file_des, save_errno, len = 0;
int create_attempts = 0;
int secshr_sem;
int semop_res, rndirln, modlen;
char *name_ptr, *rndir, *cp, realpathbef[GTM_PATH_MAX], *realpathaft, gtmdist[GTM_PATH_MAX];
char *path, *chrrv, *envvarnm;
pid_t pid;
struct sembuf sop[4];
gtmsecshr_mesg mesg;
struct stat stat_buf;
char *gtm_tmp_ptr;
int rc;
int lib_gid;
struct stat dist_stat_buff;
# ifdef _AIX
FILE *envfile;
int recnum, reclen;
char *fgets_rc, *newtz;
boolean_t tzfnd;
# endif
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
process_id = getpid();
set_blocksig();
/* Before priv escalation need to understand how/where we were invoked in terms of module path and name.
*
* Steps:
*
* 1. Verify we are "gtmsecshr" and not a module renamed for some other purpose.
* 2. Verify current working dir is $gtm_dist/gtmsecshrdir.
* 3. Verify we are loaded from $gtm_dist (a pretense setup by the wrapper).
*
* Note when/if this module merges with its wrapper, step 2 should be modified to that extent. Note our dependence
* on argv[0] for correct startup directory may preclude getting rid of the wrapper. The execl*() functions allow
* an invocation dir to be specified that has little to nothing to do with the actual invocation.
*/
/* Step 1 */
rndir = argv[0];
rndirln = STRLEN(rndir);
if (0 == rndirln)
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_GTMSECSHRSTART, 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRNOARG0);
gtmsecshr_exit(UNABLETODETERMINEPATH, FALSE);
}
for (cp = rndir + rndirln - 1; cp >= rndir; cp--)
{
if ('/' == *cp)
break;
}
cp++; /* Bump back past the '/' */
modlen = (rndir + rndirln) - cp;
if ((STRLEN(execname) != modlen) || (0 != memcmp(execname, cp, modlen)))
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_GTMSECSHRSTART, 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRISNOT, 2, modlen, cp);
gtmsecshr_exit(NOTGTMSECSHR, FALSE);
}
rndirln = rndirln - modlen - 1;
/* Step 2 */
realpathaft = malloc(GTM_PATH_MAX); /* Malloc space as this dir is used later during message validations */
memcpy(realpathbef, rndir, rndirln); /* Need to copy before we modify it */
realpathbef[rndirln] = '\0'; /* Terminate directory string (executable/dir name already checked) */
chrrv = realpath(realpathbef, realpathaft); /* Normalize dir name - eliminate all symlinks */
if (NULL == chrrv)
{
save_errno = errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_GTMSECSHRSTART, 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRISNOT, 0, save_errno);
gtmsecshr_exit(NOTGTMSECSHR, FALSE);
}
rndirln = STRLEN(realpathaft); /* Record (new) length now that realpath has normalized the path */
/* Temporaryily add gtmsecshrdir to exec path for verification purposes */
memcpy(realpathaft + rndirln, GTMSECSHR_DIR_SUFFIX, SIZEOF(GTMSECSHR_DIR_SUFFIX)); /* includes null terminator */
chrrv = getcwd(gtmdist, GTM_PATH_MAX); /* Use gtmdist 'cause it's convenient */
if ((NULL == chrrv) || (0 != strncmp(gtmdist, realpathaft, GTM_PATH_MAX)))
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_GTMSECSHRSTART, 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRBADDIR);
gtmsecshr_exit(BADGTMDISTDIR, FALSE);
}
realpathaft[rndirln] = '\0'; /* Remove gtmsecshrdir part - Put it back to supplied path ($gtm_dist) */
*rundir = realpathaft; /* This value for $gtm_dist is used in later validations */
*rundir_len = rndirln; /* .. and can be used either with this len or NULL char terminator */
/* Step 3 */
envvarnm = GTM_DIST_LOG;
path = GETENV(++envvarnm); /* Retrieve value for $gtm_dist (bumping ptr past the '$' for getenv)*/
chrrv = realpath(path, gtmdist);
if ((NULL == chrrv) || (0 != strncmp(realpathaft, gtmdist, GTM_PATH_MAX)))
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_GTMSECSHRSTART, 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRBADDIR);
gtmsecshr_exit(BADGTMDISTDIR, FALSE);
}
# ifdef _AIX
/* Note time is handled very oddly on AIX. If one undefines the TZ environment variable (such as our wrapper does to
* prevent reporting time in the timezone of the process that started gtmsecshr, process time reverts to GMT. So to
* prevent that, lookup the default timezone in /etc/environment and use it to prevent whacky time reporting by
* gtmsecshr in the operator log. Secure file read method is to switch dir first, then do relative open.
*/
if (-1 == CHDIR("/etc")) /* Note chdir is changed again below so this is only temporary */
{
save_errno = errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_GTMSECSHRSTART, 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT,
2, RTS_ERROR_LITERAL("Error during chdir to /etc - TZ cannot be determined"), save_errno);
} else
{
envfile = fopen("environment", "r");
if (NULL == envfile)
{
save_errno = errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_GTMSECSHRSTART, 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Failed to read /etc/environment - TZ cannot be determined"), save_errno);
} else
{ /* /etc/environments is open, locate TZ= record */
tzfnd = FALSE;
for (recnum = 0; ; recnum++)
{ /* Reuse realpathbef as it is not otherwise needed any longer */
FGETS(realpathbef, GTM_PATH_MAX, envfile, fgets_rc);
if (NULL == fgets_rc)
break;
if (STRLEN(TZLOCATOR) >= (reclen = STRLEN(realpathbef)))
continue;
if (0 == memcmp(TZLOCATOR, realpathbef, STRLEN(TZLOCATOR)))
{
tzfnd = TRUE;
break;
}
}
if (!tzfnd)
{ /* We didn't find the TZ record - report it depending if we just hit EOF or something more
* severe.
*/
if (!feof(envfile))
{
save_errno = errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_GTMSECSHRSTART, 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Error reading /etc/environment - TZ cannot be determined"),
save_errno);
} else
/* Have EOF - didn't find TZ */
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_GTMSECSHRSTART, 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("TZ cannot be determined"));
} else
{ /* TZ record acquired - isolate TZ - malloc space to put it once determine exact length */
if (NEWLINE == realpathbef[reclen - 1])
realpathbef[reclen-- - 1] = '\0'; /* Overwrite nl with null and decr length */
newtz = malloc(reclen); /* putenv arg must be new - no stack vars */
strcpy(newtz, realpathbef);
rc = putenv(newtz);
if (0 != rc)
{
save_errno = errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_GTMSECSHRSTART, 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("TZ reset with putenv() failed"), save_errno);
}
}
fclose(envfile);
}
}
# endif /* _AIX */
/*
**** With invocation validated, begin our priviledge escalation ****
*/
if (-1 == setuid(ROOTUID))
{
save_errno = errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) MAKE_MSG_WARNING(ERR_GTMSECSHRSTART), 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRSUIDF, 0, ERR_GTMSECSHROPCMP, 0, save_errno);
gtmsecshr_exit(SETUIDROOT, FALSE);
}
/* Before we fork, close the system log because when the facility name disappears in this middle-process,
* the logging capability disappears on some systems too - On others, it takes the executable name instead.
* Either one causes our tests to fail.
*/
DEFER_INTERRUPTS(INTRPT_IN_LOG_FUNCTION);
CLOSELOG();
ENABLE_INTERRUPTS(INTRPT_IN_LOG_FUNCTION);
first_syslog = TRUE;
FORK(pid); /* Timers have not been initialized, no need to do FORK_CLEAN; BYPASSOK */
if (0 > pid)
{ /* Fork failed */
save_errno = errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_GTMSECSHRSTART, 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRFORKF, 0, save_errno);
exit(GNDCHLDFORKFLD);
} else if (0 < pid)
/* This is the original process - it dies quietly (no exit handler of any sort) to isolate us */
_exit(EXIT_SUCCESS);
/****** We are now in the (isolated) child process ******/
process_id = getpid();
pid = getsid(process_id);
if ((pid != process_id) && ((pid_t)-1 == setsid()))
{
save_errno = errno;
DEBUG_ONLY(util_out_print("expected sid !UL but have !UL", OPER, process_id, pid));
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) MAKE_MSG_WARNING(ERR_GTMSECSHRSTART), 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRSSIDF, 0, save_errno);
}
/* Close standard IO devices - we don't need/want them as all IO goes to operator log. Else we would have IO devices
* tied to whatever process started gtmsecshr up which we definitely don't want.
*/
CLOSEFILE(0, rc); /* No stdin */
CLOSEFILE(1, rc); /* No stdout */
CLOSEFILE(2, rc); /* No stderr */
/* Init signal handling which works slightly different than in other utilities - gtmsecshr has its own handler which
* *calls* generic_signal_handler (which always returns for gtmsecshr) - we then drive our normal exit handling.
*/
sig_init(gtmsecshr_signal_handler, NULL, NULL, NULL);
file_des = sysconf(_SC_OPEN_MAX);
for (file_des = file_des - 1; file_des >= 3; file_des--)
{ /* Close the file only if we have it open. This is to avoid a CLOSEFAIL error in case of
* trying to close an invalid file descriptor.
*/
CLOSEFILE_IF_OPEN(file_des, rc);
}
if (-1 == CHDIR(P_tmpdir)) /* Switch to temporary directory as CWD */
{
save_errno = errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_GTMSECSHRSTART, 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRCHDIRF, 2, LEN_AND_STR(P_tmpdir), save_errno);
exit(UNABLETOCHDIR);
}
umask(0);
if (0 != gtmsecshr_pathname_init(SERVER, *rundir, *rundir_len))
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_GTMSECSHRSOCKET, 3, RTS_ERROR_LITERAL("Server path"), process_id);
if (-1 == (secshr_sem = semget(gtmsecshr_key, FTOK_SEM_PER_ID, RWDALL | IPC_NOWAIT)))
{
secshr_sem = INVALID_SEMID;
if ((ENOENT != errno) || /* Below will fail otherwise */
(-1 == (secshr_sem = semget(gtmsecshr_key, FTOK_SEM_PER_ID, RWDALL | IPC_CREAT | IPC_NOWAIT | IPC_EXCL))))
{
secshr_sem = INVALID_SEMID;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_GTMSECSHRSEMGET, 1, errno);
gtmsecshr_exit(SEMGETERROR, FALSE);
}
}
sop[0].sem_num = 0;
sop[0].sem_op = 0;
sop[0].sem_flg = IPC_NOWAIT;
sop[1].sem_num = 0;
sop[1].sem_op = 1;
sop[1].sem_flg = IPC_NOWAIT | SEM_UNDO;
SEMOP(secshr_sem, sop, 2, semop_res, NO_WAIT);
if (0 > semop_res)
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) MAKE_MSG_SEVERE(ERR_GTMSECSHRSTART), 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("server already running"), errno);
/* If gtm_tmp is not defined, show default path */
if (gtm_tmp_ptr = GETENV("gtm_tmp")) /* Warning - assignment */
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_GTMSECSHRTMPPATH, 2,
RTS_ERROR_TEXT(gtm_tmp_ptr), ERR_TEXT, 2, RTS_ERROR_TEXT("(from $gtm_tmp)"));
else
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GTMSECSHRTMPPATH, 2, RTS_ERROR_TEXT("/tmp"));
gtmsecshr_exit(SEMAPHORETAKEN, FALSE);
}
if (0 != gtmsecshr_sock_init(SERVER))
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_GTMSECSHRSOCKET, 3, RTS_ERROR_LITERAL("Server"), process_id);
if (-1 == Stat(gtmsecshr_sock_name.sun_path, &stat_buf))
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) MAKE_MSG_WARNING(ERR_GTMSECSHRSTART), 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Unable to get status of socket file"), errno);
/* Get the distribution group */
lib_gid = gtm_get_group_id(&dist_stat_buff);
/* If it is world accessible then make mode 666 */
if ((-1 != lib_gid) && (dist_stat_buff.st_mode & 04))
lib_gid = -1;
if (-1 == lib_gid)
stat_buf.st_mode = 0666;
else
{
/* Change group if different from current user group */
if (lib_gid != GETGID() && (-1 == CHOWN(gtmsecshr_sock_name.sun_path, -1, lib_gid)))
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) MAKE_MSG_WARNING(ERR_GTMSECSHRSTART), 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Unable to change socket file group"), errno);
stat_buf.st_mode = 0660;
}
if (-1 == CHMOD(gtmsecshr_sock_name.sun_path, stat_buf.st_mode))
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) MAKE_MSG_WARNING(ERR_GTMSECSHRSTART), 3,
RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Unable to change socket file permisions"), errno);
name_ptr = strrchr(gtmsecshr_sock_name.sun_path, '/');
while (*name_ptr == '/') /* back off in case of double-slash */
name_ptr--;
gtmsecshr_socket_dir_len = (int)(name_ptr - gtmsecshr_sock_name.sun_path + 1);
/* Preallocate some timer blocks. */
prealloc_gt_timers(); /* Note we do NOT call gt_timers_add_safe_hndlrs here - don't need them or their baggage */
/* Create communication key used in all gtmsecshr messages. Key's purpose is to eliminate cross-version
* communication issues.
*/
STR_HASH((char *)gtm_release_name, gtm_release_name_len, TREF(gtmsecshr_comkey), 0);
/* Initialization complete */
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_GTMSECSHRDMNSTARTED, 5,
gtmsecshr_key, gtm_release_name_len, gtm_release_name, *rundir_len, *rundir);
return;
}
void gtmsecshr_exit(int exit_code, boolean_t dump)
{
int gtmsecshr_sem;
if (dump)
DUMP_CORE;
gtmsecshr_sock_cleanup(SERVER);
gtmsecshr_sockfd = FD_INVALID;
if (SEMAPHORETAKEN != exit_code)
{ /* remove semaphore */
if (-1 == (gtmsecshr_sem = semget(gtmsecshr_key, FTOK_SEM_PER_ID, RWDALL | IPC_NOWAIT)))
{
gtmsecshr_sem = INVALID_SEMID;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_GTMSECSHRGETSEMFAIL, 1, errno);
}
if (-1 == semctl(gtmsecshr_sem, 0, IPC_RMID, 0))
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_GTMSECSHRREMSEMFAIL, 1, errno);
}
/* Note shutdown message taken care of by generic_signal_handler */
exit(exit_code);
}
void gtmsecshr_timer_handler(void)
{
gtmsecshr_timer_popped = TRUE;
}
void gtmsecshr_signal_handler(int sig, siginfo_t *info, void *context)
{
/* Do standard signal handling */
(void)generic_signal_handler(sig, info, context);
/* Note that we are not letting process_signal run gtm_fork_n_core. In testing,
* bad things occurred when the root process ran into trouble trying to fork
* so our object is to avoid the problem entirely and core here if we need to
* and if the OS will do it (Linux seems not to core if root process).
*/
gtmsecshr_exit(sig, need_core);
}
void service_request(gtmsecshr_mesg *buf, int msglen, char *rundir, int rundir_len)
{
int fn_len, index, basind, save_errno, save_code;
int stat_res, fd;
char *basnam, *fn;
struct shmid_ds temp_shmctl_buf;
struct stat statbuf;
sgmnt_data header;
endian32_struct check_endian;
char *intent;
ZOS_ONLY(int realfiletag;)
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
save_code = buf->code;
/* First up, validate the communication key for this gtmsecshr version */
if (TREF(gtmsecshr_comkey) != buf->comkey)
{
buf->code = INVALID_COMKEY;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Comkey not correct for this gtmsecshr version"));
return;
}
/* Process code (with code specific valications) */
switch(buf->code)
{
# ifdef NOT_CURRENTLY_USED
/* Currently, M LOCK wakeup logic is disabled in the client and here. This code was not being used due to a
* requirement (and check) that crit was not being held. Since mlk_unlock()->mlk_wake_pending->crit_wake()
* runs in crit, this prevented gtmsecshr from EVER being called with this message. This code is disabled until
* the next gtmsecshr improvement phase which will modify M unLOCK logic to drive the wakeups only after the
* process has released crit. This will restore fast M LOCK wakeups for processes with a different userid than
* the current process which currently waits until a 100ms poll timer expires to retry the lock.
*/
case WAKE_MESSAGE:
/* if (0 != validate_receiver(buf, rundir, rundir_len, save_code))
return; / * buf->code already set - to be re-enabled when routine is completed */
if (buf->code = (-1 == kill((pid_t)buf->mesg.id, SIGALRM)) ? errno : 0)
{
save_errno = errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Unable to wake up process"), save_errno);
}
# ifdef DEBUG
else
/* Output msg only in DEBUG mode as these could otherwise be frequent */
util_out_print("[client pid !UL] Process !UL was awakened", OPER, buf->pid, buf->mesg.id);
# endif
break;
# endif
case CONTINUE_PROCESS:
/* if (0 != validate_receiver(buf, rundir, rundir_len, save_code))
return; / * buf->code already set - to be re-enabled when routine is completed */
if (buf->code = (-1 == kill((pid_t)buf->mesg.id, SIGCONT)) ? errno : 0)
{
save_errno = errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Unable to request process to resume processing"), save_errno);
}
# ifdef DEBUG
else
/* Output msg only in DEBUG mode as these could otherwise be frequent */
util_out_print("[client pid !UL] Process !UL was requested to resume processing", OPER,
buf->pid, buf->mesg.id);
# endif
break ;
case REMOVE_SEM:
buf->code = (-1 == semctl((int)buf->mesg.id, 0, IPC_RMID, 0)) ? errno : 0;
if (!buf->code)
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GTMSECSHRREMSEM, 2, buf->pid, buf->mesg.id);
else
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Unable to remove semaphore"), buf->code);
}
break;
case REMOVE_SHM:
buf->code = (-1 == shmctl((int)buf->mesg.id, IPC_STAT, &temp_shmctl_buf)) ? errno : 0;
if (buf->code)
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Unable to get shared memory statistics"), buf->code);
break;
} else if (1 < temp_shmctl_buf.shm_nattch)
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GTMSECSHRSHMCONCPROC, 2,
buf->mesg.id, temp_shmctl_buf.shm_nattch);
buf->code = EBUSY;
break;
}
buf->code = (-1 == shmctl((int)buf->mesg.id, IPC_RMID, 0)) ? errno : 0;
if (!buf->code)
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_GTMSECSHRREMSHM, 3,
buf->pid, buf->mesg.id, temp_shmctl_buf.shm_nattch);
else
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Unable to remove shared memory segment"), buf->code);
}
break;
# ifndef MUTEX_MSEM_WAKE
case REMOVE_FILE :
/* This case only exists for plastforms using socket based long sleep when waiting for the
* critical section. The memory semaphore based sleeps do not use so cannot orphan this socket.
*/
for (index = 0; index < SIZEOF(buf->mesg.path); index++)
{ /* Verify has null terminator within the message */
if ('\0' == buf->mesg.path[index])
break;
}
if ((0 == index) || (index >= SIZEOF(buf->mesg.path)) || (index != (msglen - GTM_MESG_HDR_SIZE - 1)))
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFIL, 7,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, buf->code,
index >= SIZEOF(buf->mesg.path) ? SIZEOF(buf->mesg.path) - 1 : index,
buf->mesg.path, ERR_TEXT, 2,
RTS_ERROR_LITERAL("no file name or length too long or invalid"));
buf->code = EINVAL;
} else
{
if (('/' == buf->mesg.path[index - 1]) && (1 < index))
{ /* remove trailing slash unless only slash */
buf->mesg.path[index - 1] = '\0';
index--;
}
for (basind = index - 1; 0 <= basind; basind--)
{
if ('/' == buf->mesg.path[basind])
break;
}
if ((0 < basind) || ((0 == basind) && ('/' == buf->mesg.path[basind])))
basnam = &buf->mesg.path[basind + 1];
else
basnam = &buf->mesg.path[0];
/* Verify:
*
* 1. File exists.
* 2. File is a socket.
* 3. Has a name prefix GT.M would use
* 4. File is resident in expected directory ($gtm_tmp or /tmp).
*
*/
STAT_FILE(buf->mesg.path, &statbuf, stat_res);
if (-1 == stat_res)
{
# ifdef DEBUG
if (buf->usesecshr && (ENOENT == errno))
/* ALL unlinks for the mutex socket come thru here in this mode so if socket does
* not exist, just let it go.
*/
buf->code = 0;
else
# endif
{
save_errno = errno;
buf->code = save_errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(14) ERR_GTMSECSHRSRVFIL, 7,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, buf->code,
index, buf->mesg.path, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Unable to get file status"), save_errno);
}
} else if ((!S_ISSOCK(statbuf.st_mode)) ZOS_ONLY(|| (S_ISCHR(statbuf.st_mode))))
{
buf->code = EINVAL;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFIL, 7,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, buf->code,
index, buf->mesg.path, ERR_TEXT, 2,
RTS_ERROR_LITERAL("File is not a GTM mutex socket file"));
} else if (0 != MEMCMP_LIT(basnam, MUTEX_SOCK_FILE_PREFIX))
{
buf->code = EINVAL;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFIL, 7,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, buf->code,
index, buf->mesg.path, ERR_TEXT, 2,
RTS_ERROR_LITERAL("File name does not match the naming convention for a GT.M "
"mutex socket file"));
} else if (0 != memcmp(gtmsecshr_sock_name.sun_path, buf->mesg.path, gtmsecshr_socket_dir_len))
{
buf->code = EINVAL;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFIL, 7,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, buf->code,
index, buf->mesg.path, ERR_TEXT, 2,
RTS_ERROR_LITERAL("File does not reside in the normal directory for a GT.M "
"mutex socket file"));
} else if (buf->code = (-1 == UNLINK(buf->mesg.path)) ? errno : 0)
{
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(14) ERR_GTMSECSHRSRVFIL, 7,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code,
RTS_ERROR_STRING(buf->mesg.path), ERR_TEXT, 2,
RTS_ERROR_LITERAL("Unable to remove file"), buf->code);
} else
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_GTMSECSHRREMFILE, 3,
buf->pid, RTS_ERROR_STRING(buf->mesg.path));
}
break;
# endif
case FLUSH_DB_IPCS_INFO:
/* Most of this code came from file_head_read/write.c but those routines don't follow our rules
* (no stdout/stderr IO) so we streamline it here.
*/
fn = buf->mesg.db_ipcs.fn;
fn_len = buf->mesg.db_ipcs.fn_len;
if ((GTM_PATH_MAX < fn_len) || ('\0' != *(fn + fn_len))
|| (fn_len != (msglen - GTM_MESG_HDR_SIZE - offsetof(ipcs_mesg, fn[0]) - 1)))
{
buf->code = EINVAL;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("invalid file name argument"));
break;
}
/* First open and read-in the fileheader */
OPENFILE(fn, O_RDWR, fd);
if (FD_INVALID == fd)
{
save_errno = buf->code = errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code,
buf->mesg.id, ERR_DBFILOPERR, 2, fn_len, fn, save_errno);
break;
}
# ifdef __MVS__
if (-1 == gtm_zos_tag_to_policy(fd, TAG_BINARY, &realfiletag))
TAG_POLICY_SEND_MSG(fn, errno, realfiletag, TAG_BINARY);
# endif
/* Verify is a GT.M database - validations:
*
* 1. Is a regular file (not a special type).
* 2. Is of a size at least as large as the file header (doesn't get fancy with this)
* 3. Able to read the file.
* 4. Verify tag at top of file denotes a GT.M database of the correct version.
* 5. Validate fields being set into database file (see details below).
* 6. Set the fields into the database.
* 7. Write updated database header and close the database.
*/
FSTAT_FILE(fd, &statbuf, save_errno);
if (-1 == save_errno)
{
buf->code = save_errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid,
save_code, buf->mesg.id, ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno);
CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */
break;
}
if (!S_ISREG(statbuf.st_mode) || (SIZEOF(header) > statbuf.st_size))
{
buf->code = ERR_DBNOTGDS;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid,
save_code, buf->mesg.id, ERR_DBNOTGDS, 2, LEN_AND_STR(fn));
CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */
break;
}
LSEEKREAD(fd, 0, &header, SIZEOF(header), save_errno);
if (0 != save_errno)
{
buf->code = save_errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid,
save_code, buf->mesg.id, ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno);
CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */
break;
}
if (0 != memcmp(header.label, GDS_LABEL, GDS_LABEL_SZ - 1)) /* Verify is GT.M database file */
{
buf->code = ERR_DBNOTGDS;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid,
save_code, buf->mesg.id, ERR_DBNOTGDS, 2, LEN_AND_STR(fn));
CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */
break;
}
/* It would be easier to use the CHECK_DB_ENDIAN macro here but we'd prefer it didn't raise rts_error */
check_endian.word32 = header.minor_dbver;
if (!check_endian.shorts.ENDIANCHECKTHIS)
{
buf->code = ERR_DBENDIAN;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid,
save_code, buf->mesg.id, ERR_DBENDIAN, 4, fn_len, fn, ENDIANOTHER, ENDIANTHIS);
CLOSEFILE_RESET(fd, save_errno);
break;
}
/* Verify the fields to update make sense.
*
* 1. If new semid is INVALID_SEMID, then (file close):
* a. New shmid should be INVALID_SHMID.
* b. Both ctime fields should be zero.
* c. If current value of semid in header is INVALID_SEMID, verify the other settings are also correct
* for a closed database, close file.
* d. If current value of semid is not INVALID_SEMID, check its value - should be <= 1 else error.
* e. The current shmid should exist and have 1 attachment.
* 2. If new semid is *not* INVALID_SEMID then (file open):
* a. Initial impulse is to check that new shmid is NOT INVALID_SHMID, but in one case which we can't
* detect (standalone access of R/O DB by MUPIP INTEG and perhaps others), only the semaphore id
* and ctime are modified. The shm fields are left as is.
* b. If previous semid is not INVALID_SEMID, verify is same as we've been requested to update and close
* with an already open message.
* c. If previous semid is INVALID_SEMID, check the new semid and query its value. Should be <= 1 or
* raise error.
* d. Ditto for shmid and its attach value should also be <= 1.
*
* Note - Items 1c - 1e and items 2b - 2d not yet implemented (phase 3).
*/
if (INVALID_SEMID == buf->mesg.db_ipcs.semid)
{ /* Intent is to close the given database */
intent = intent_close;
if ((INVALID_SHMID != buf->mesg.db_ipcs.shmid) || (0 != buf->mesg.db_ipcs.gt_sem_ctime)
|| (0 != buf->mesg.db_ipcs.gt_shm_ctime))
{
buf->code = EINVAL;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id,
buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Invalid header value combination"));
CLOSEFILE_RESET(fd, save_errno);
break;
}
} else
{ /* Intent is to open the given database - Note if this is a standalone access of a read-only
* DB by such as MUPIP INTEG, only the semaphore is set, not shared memory so we must relax our
* checks a bit and allow the shm id and ctime to remain in "closed" state. But if shmid is
* specified, do validate that ctime was specified and vice-versa.
*/
intent = intent_open;
if ((0 == buf->mesg.db_ipcs.gt_sem_ctime)
|| ((INVALID_SEMID == buf->mesg.db_ipcs.shmid) && (0 != buf->mesg.db_ipcs.gt_shm_ctime))
|| ((INVALID_SEMID != buf->mesg.db_ipcs.shmid) && (0 == buf->mesg.db_ipcs.gt_shm_ctime)))
{
buf->code = EINVAL;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id,
buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Invalid header value combination"));
CLOSEFILE_RESET(fd, save_errno);
break;
}
}
/* Update file header fields */
header.semid = buf->mesg.db_ipcs.semid;
header.shmid = buf->mesg.db_ipcs.shmid;
header.gt_sem_ctime.ctime = buf->mesg.db_ipcs.gt_sem_ctime;
header.gt_shm_ctime.ctime = buf->mesg.db_ipcs.gt_shm_ctime;
/* And flush the changes back out. Note this service code is only used by read-only processes so
* we don't need any anticipatory freeze insertion which anyway pulls in the world so LSEEKWRITE does
* what we need.
*/
LSEEKWRITE(fd, 0, &header, SIZEOF(header), save_errno);
if (0 != save_errno)
{
buf->code = save_errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id,
buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Unable to write database file header"), save_errno);
CLOSEFILE_RESET(fd, save_errno);
break;
}
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_GTMSECSHRUPDDBHDR, 5,
buf->pid, fn_len, fn, RTS_ERROR_STRING(intent));
buf->code = 0;
CLOSEFILE_RESET(fd, save_errno);
break;
default:
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid,
buf->code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Invalid Service Request"));
buf->code = 0x8000; /* Flag for no-ack required - invalid commands get no response */
}
return;
}
/* Service request asks gtmsecshr to send a signal to a given process. Verify (as best we can) this process is running something
* related to GT.M. Two potential methods are used:
*
* 1. Target process has an execution directory the same as ours ($gtm_dist). Note it is possible a target process is doing
* a call-in so this test is not always TRUE but is the faster of the two tests.
* 2. Target process has libgtmshr.{suffix} (aka GTMSHR_IMAGE_NAME from mdefsa.h) loaded which we can tell by examining the
* open files of the target process.
*
* Note - this routine is currently NOT USED as it is incomplete and not yet implemented for all platforms. We leave it in here
* for now and plan to complete it in an upcoming version.
*/
int validate_receiver(gtmsecshr_mesg *buf, char *rundir, int rundir_len, int save_code)
{
int save_errno;
# ifdef __linux__
# define PROCPATH_PREFIX "/proc/"
# define PROCPATH_CMDLSUFFIX "/cmdline"
# define PROCPATH_MAPSSUFFIX "/maps"
int lnln, clrv, cmdbufln;
FILE *procstrm;
char procpath[GTM_PATH_MAX], cmdbuf[GTM_PATH_MAX], rpcmdbuf[GTM_PATH_MAX];
char *ppptr, *ppptr_save, *csrv, *cptr;
/* Check #1 - open /proc/<pid>/cmdline, read to first NULL - this is the command name */
ppptr = procpath;
MEMCPY_LIT(procpath, PROCPATH_PREFIX);
ppptr += STRLEN(PROCPATH_PREFIX);
ppptr = i2asc(ppptr, buf->mesg.id);
ppptr_save = ppptr; /* Save where adding cmdline so can replace if need to move to check #2 */
memcpy(ppptr, PROCPATH_CMDLSUFFIX, SIZEOF(PROCPATH_CMDLSUFFIX)); /* Copy includes terminating null of literal */
procstrm = Fopen(procpath, "r");
if (NULL == procstrm)
{
save_errno = errno;
buf->code = save_errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Could not open /proc/<pid>/cmdline"), save_errno);
return save_errno;
}
FGETS(cmdbuf, GTM_PATH_MAX, procstrm, csrv);
if (NULL == csrv)
{
save_errno = errno;
buf->code = save_errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Could not read /proc/<pid>/cmdline"), save_errno);
return save_errno;
}
FCLOSE(procstrm, clrv);
if (-1 == clrv)
/* Not a functional issue so just warn about it in op-log */
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fclose()"), CALLFROM, errno);
lnln = STRLEN(cmdbuf);
/* Look from the end backwards to find the last '/' to isolate the directory */
for (cptr = cmdbuf + lnln - 1; (cptr >= cmdbuf) && ('/' != *cptr); cptr--)
;
cmdbufln = INTCAST(cptr - cmdbuf);
if (0 < cmdbufln)
{ /* Normalize the directory via realpath so comparison possible */
cmdbuf[cmdbufln] = rpcmdbuf[0] = '\0';
csrv = realpath(cmdbuf, rpcmdbuf);
cmdbufln = STRLEN(rpcmdbuf);
if ((cmdbufln == rundir_len) && (0 == memcmp(rundir, rpcmdbuf, cmdbufln)))
{
buf->code = 0; /* Successful validation */
DEBUG_ONLY(util_out_print("Successful validation of target processid !UL", OPER, buf->pid));
return 0;
}
}
/* Check #1 failed - attempt check #2 - read /proc/<pid>/maps to see if libgtmshr is there */
memcpy(ppptr_save, PROCPATH_MAPSSUFFIX, SIZEOF(PROCPATH_MAPSSUFFIX)); /* Copy includes terminating null of literal */
procstrm = Fopen(procpath, "r");
if (NULL == procstrm)
{
save_errno = errno;
buf->code = save_errno;
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6,
RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Could not open /proc/<pid>/cmdline"), save_errno);
return save_errno;
}
/* Insert map reading code TODO */
FCLOSE(procstrm, clrv);
if (-1 == clrv)
/* Not a functional issue so just warn about it in op-log */
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fclose()"), CALLFROM, errno);
# endif
return 0;
}
#ifdef __MVS__
boolean_t gtm_tag_error(char *filename, int realtag, int desiredtag)
{
char *errmsg;
errmsg = STRERROR(errno);
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_BADTAG, 4, LEN_AND_STR(filename),
realtag, desiredtag, ERR_TEXT, 2, RTS_ERROR_STRING(errmsg));
return FALSE;
}
#endif