/****************************************************************
 *								*
 * Copyright (c) 2001-2019 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	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 <sys/shm.h>
#include <errno.h>
#include <sys/sem.h>

#include "gtm_ipc.h"
#include "gtm_fcntl.h"
#include "gtm_unistd.h"
#include "gtm_inet.h"
#include "gtm_stdlib.h"
#include "gtm_string.h"
#include "gtm_sem.h"
#include "gtm_stat.h"
#include "gtm_stdio.h"
#include "gtmio.h"

#include "gdsroot.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "iosp.h"
#include "mutex.h"
#include "jnl.h"
#include "repl_sem.h"
#include "eintr_wrappers.h"
#include "mu_rndwn_file.h"
#include "repl_msg.h"
#include "gtmsource.h"
#include "gtmrecv.h"
#include "gtm_logicals.h"
#include "min_max.h"
#include "util.h"
#include "mu_rndwn_replpool.h"
#include "mu_rndwn_all.h"
#include "dbfilop.h"
#include "ipcrmid.h"
#include "mu_gv_cur_reg_init.h"
#include "gtmmsg.h"
#include "cliif.h"
#include "mu_rndwn_repl_instance.h"
#include "send_msg.h"
#include "do_shmat.h"	/* for do_shmat() prototype */
#include "shmpool.h" /* Needed for the shmpool structures */
#include "error.h"
#include "db_snapshot.h"

#define PRINT_AND_SEND_SHMREMOVED_MSG(MSGBUFF, FNAME_LEN, FNAME, SHMID)							\
MBSTART {														\
	gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), 					\
			ERR_SHMREMOVED, 3, SHMID, FNAME_LEN, FNAME);							\
	send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), 					\
			ERR_SHMREMOVED, 3, SHMID, FNAME_LEN, FNAME);							\
} MBEND

#define PRINT_AND_SEND_REPLPOOL_FAILURE_MSG(MSGBUFF, REPLPOOL_ID, SHMID)						\
MBSTART {														\
	int		msgid;												\
	unsigned char	ipcs_buff[MAX_IPCS_ID_BUF], *ipcs_ptr;								\
															\
	ipcs_ptr = i2asc(ipcs_buff, SHMID);										\
	*ipcs_ptr = '\0';												\
	msgid = (JNLPOOL_SEGMENT == REPLPOOL_ID->pool_type) ? ERR_MUJPOOLRNDWNFL : ERR_MURPOOLRNDWNFL;			\
	gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), 					\
				msgid, 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(REPLPOOL_ID->instfilename));		\
	send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), msgid, 4, LEN_AND_STR(ipcs_buff),	\
			LEN_AND_STR(REPLPOOL_ID->instfilename));							\
} MBEND

#define PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(MSGBUFF, FNAME, SHMID)							\
MBSTART {														\
	gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), ERR_MUFILRNDWNFL2, 3, 		\
			SHMID, LEN_AND_STR(FNAME));									\
	send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), ERR_MUFILRNDWNFL2, 3, 		\
			SHMID, LEN_AND_STR(FNAME));									\
} MBEND

GBLREF	gd_region		*gv_cur_region;
GBLREF	mval			dollar_zdir;
GBLREF	semid_queue_elem	*keep_semids;
GBLREF	uid_t			user_id;
GBLREF	uint4			process_id;

LITREF char             gtm_release_name[];
LITREF int4             gtm_release_name_len;

error_def(ERR_DBFILERR);
error_def(ERR_MUFILRNDWNFL2);
error_def(ERR_MUFILRNDWNSUC);
error_def(ERR_MUJPOOLRNDWNFL);
error_def(ERR_MUJPOOLRNDWNSUC);
error_def(ERR_MUNOTALLSEC);
error_def(ERR_MURNDWNARGLESS);
error_def(ERR_MURPOOLRNDWNFL);
error_def(ERR_MURPOOLRNDWNSUC);
error_def(ERR_SEMREMOVED);
error_def(ERR_SHMREMOVED);
error_def(ERR_SYSCALL);
error_def(ERR_TEXT);

STATICFNDCL boolean_t validate_db_shm_entry(shm_parms *parm_buff, char *fname, int *tmp_exit_status);
STATICFNDCL boolean_t validate_replpool_shm_entry(shm_parms *parm_buff, replpool_id_ptr_t replpool_id, int *tmp_exit_status);
STATICFNDCL shm_parms *get_shm_parm(char *entry);
STATICFNDCL char *parse_shm_entry(char *entry, int which_field);
STATICFNDCL void mu_rndwn_all_helper(shm_parms *parm_buff, char *fname, int *exit_status, int *tmp_exit_status);

STATICDEF	boolean_t	mu_rndwn_all_helper_error = FALSE;

/* This condition handler is necessary so the argumentless "mupip rundown" does not terminate in case of an error
 * while processing one ipc. Instead it moves on to the next ipc. The only exception is fatal errors (SEVERE)
 * where it might not be safe to continue processing so we transfer control to a higher level condition handler.
 */
CONDITION_HANDLER(mu_rndwn_all_helper_ch)
{
	START_CH(TRUE);
	mu_rndwn_all_helper_error = TRUE;
	PRN_ERROR;
	if (SEVERITY == SEVERE)
	{
		NEXTCH;
	} else
		UNWIND(NULL, NULL);
}

STATICFNDEF void mu_rndwn_all_helper(shm_parms *parm_buff, char *fname, int *exit_status, int *tmp_exit_status)
{
	replpool_identifier	replpool_id;
	boolean_t 		ret_status, jnlpool_sem_created;
	unsigned char		ipcs_buff[MAX_IPCS_ID_BUF], *ipcs_ptr;

	ESTABLISH(mu_rndwn_all_helper_ch);
	if (validate_db_shm_entry(parm_buff, fname, tmp_exit_status))
	{
		if (SS_NORMAL == *tmp_exit_status)
		{	/* shm still exists */
			mu_gv_cur_reg_init();
			gv_cur_region->dyn.addr->fname_len = strlen(fname);
			STRNCPY_STR(gv_cur_region->dyn.addr->fname, fname, gv_cur_region->dyn.addr->fname_len);
			if (mu_rndwn_file(gv_cur_region, FALSE))
			{	/* If this is a statsdb, "mu_rundwn_file" would have invoked rundown on basedb which would
				 * in turn invoke rundown on the statsdb. The basedb rundown would print the MUFILRNDWNSUC
				 * message if needed. So skip double-printing it here.
				 */
				if (!IS_RDBF_STATSDB(gv_cur_region))
					gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUFILRNDWNSUC, 2, DB_LEN_STR(gv_cur_region));
			} else
			{	/* Save semid so that it will not be removed by mu_rndwn_sem_all() */
				add_to_semids_list(FILE_INFO(gv_cur_region)->semid);
				*exit_status = ERR_MUNOTALLSEC;
			}
			mu_gv_cur_reg_free();
		} else
		{	/* shm has been cleaned up by "validate_db_shm_entry" so no need of any more cleanup here */
			assert(ERR_SHMREMOVED == *tmp_exit_status);
			*tmp_exit_status = SS_NORMAL;	/* reset tmp_exit_status for below logic to treat this as normal */
		}
	} else if ((SS_NORMAL == *tmp_exit_status)
		&& validate_replpool_shm_entry(parm_buff, (replpool_id_ptr_t)&replpool_id, tmp_exit_status))
	{
		if (SS_NORMAL == *tmp_exit_status)
		{
			assert(JNLPOOL_SEGMENT == replpool_id.pool_type || RECVPOOL_SEGMENT == replpool_id.pool_type);
			ret_status = mu_rndwn_repl_instance(&replpool_id, TRUE, FALSE, &jnlpool_sem_created);
			ipcs_ptr = i2asc((uchar_ptr_t)ipcs_buff, parm_buff->shmid);
			*ipcs_ptr = '\0';
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) (JNLPOOL_SEGMENT == replpool_id.pool_type) ?
				(ret_status ? ERR_MUJPOOLRNDWNSUC : ERR_MUJPOOLRNDWNFL) :
				(ret_status ? ERR_MURPOOLRNDWNSUC : ERR_MURPOOLRNDWNFL),
				4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(replpool_id.instfilename));
			if (!ret_status)
				*exit_status = ERR_MUNOTALLSEC;
		} else
		{	/* shm has been cleaned up by "validate_replpool_shm_entry" so no need of any more cleanup here */
			assert(ERR_SHMREMOVED == *tmp_exit_status);
			*tmp_exit_status = SS_NORMAL;	/* reset tmp_exit_status for below logic to treat this as normal */
		}
	}
	REVERT;
}

int mu_rndwn_all(void)
{
	int 			save_errno, fname_len, exit_status = SS_NORMAL, shmid, tmp_exit_status;
	char			entry[MAX_ENTRY_LEN];
	FILE			*pf;
	char			*fname, *fgets_res;
	shm_parms		*parm_buff;

	/* Record the start of an argumentless MUPIP RUNDOWN with current directory info. Useful for debugging purposes */
	send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MURNDWNARGLESS, 4, process_id, user_id,
									dollar_zdir.str.len, dollar_zdir.str.addr);
	if (NULL == (pf = POPEN(IPCS_CMD_STR ,"r")))
        {
		save_errno = errno;
		gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("POPEN()"), CALLFROM, save_errno);
                return ERR_MUNOTALLSEC;
        }
	fname = (char *)malloc(MAX_FN_LEN + 1);
	while (NULL != (FGETS(entry, SIZEOF(entry), pf, fgets_res)) && entry[0] != '\n')
	{
		tmp_exit_status = SS_NORMAL;
		parm_buff = get_shm_parm(entry);
		if (NULL == parm_buff)
		{
			exit_status = ERR_MUNOTALLSEC;
			continue;
		}
		mu_rndwn_all_helper(parm_buff, fname, &exit_status, &tmp_exit_status);
		if ((SS_NORMAL == exit_status) && (SS_NORMAL != tmp_exit_status))
			exit_status = tmp_exit_status;
		if (mu_rndwn_all_helper_error)
		{	/* Encountered a runtime error while processing this ipc. Make sure we return with
			 * MUNOTALLSEC and reset this static variable before starting processing on next ipc.
			 */
			mu_rndwn_all_helper_error = FALSE;
			if (SS_NORMAL == exit_status)
				exit_status = ERR_MUNOTALLSEC;
		}
		if (NULL != parm_buff)
			free(parm_buff);
	}
	pclose(pf);
	free(fname);
	return exit_status;
}

/* Takes an entry from 'ipcs -m' and checks for its validity to be a GT.M db segment.
 * Returns TRUE if the shared memory segment is a valid GT.M db segment
 * (based on a check on some fields in the shared memory) else FALSE.
 * If the segment belongs to GT.M it returns the database file name by the second argument.
 * Sets exit_stat to ERR_MUNOTALLSEC if appropriate.
 */
boolean_t validate_db_shm_entry(shm_parms *parm_buff, char *fname, int *exit_stat)
{
	boolean_t		remove_shmid;
	file_control		*fc;
	int			fname_len, save_errno, status, shmid;
	node_local_ptr_t	nl_addr;
	sm_uc_ptr_t		start_addr;
	struct stat		st_buff;
	struct shmid_ds		shmstat;
	sgmnt_data		tsd;
	unix_db_info		*udi;
	char			msgbuff[OUT_BUFF_SIZE];

	if (NULL == parm_buff)
		return FALSE;
	/* check for the bare minimum size of the shared memory segment that we expect
	 * (with no fileheader related information at hand) */
	if (MIN_NODE_LOCAL_SPACE + SHMPOOL_SECTION_SIZE > parm_buff->sgmnt_siz)
		return FALSE;
	if (IPC_PRIVATE != parm_buff->key)
		return FALSE;
	shmid = parm_buff->shmid;
	/* we do not need to lock the shm for reading the rundown information as
	 * the other rundowns (if any) can also be allowed to share reading the
	 * same info concurrently.
	 */
	if (-1 == (sm_long_t)(start_addr = (sm_uc_ptr_t) do_shmat(shmid, 0, SHM_RND)))
		return FALSE;
	nl_addr = (node_local_ptr_t)start_addr;
	memcpy(fname, nl_addr->fname, MAX_FN_LEN + 1);
	fname[MAX_FN_LEN] = '\0';			/* make sure the fname is null terminated */
	fname_len = STRLEN(fname);
	msgbuff[0] = '\0';
	if (memcmp(nl_addr->label, GDS_LABEL, GDS_LABEL_SZ - 1))
	{
		if (!memcmp(nl_addr->label, GDS_LABEL, GDS_LABEL_SZ - 3))
		{
			util_out_print("Cannot rundown shmid = !UL for database !AD as it has format !AD "
				"but this mupip uses format !AD", TRUE, shmid,
				fname_len, fname, GDS_LABEL_SZ - 1, nl_addr->label, GDS_LABEL_SZ - 1, GDS_LABEL);
			*exit_stat = ERR_MUNOTALLSEC;
		}
		shmdt((void *)start_addr);
		return FALSE;
	}
	if (-1 == shmctl(shmid, IPC_STAT, &shmstat))
	{
		save_errno = errno;
		assert(FALSE);/* we were able to attach to this shmid before so should be able to get stats on it */
		util_out_print("!AD -> Error with shmctl for shmid = !UL",
			TRUE, fname_len, fname, shmid);
		gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno);
		*exit_stat = ERR_MUNOTALLSEC;
		shmdt((void *)start_addr);
		return FALSE;
	}
	remove_shmid = FALSE;
	/* Check if db filename reported in shared memory still exists. If not, clean this shared memory section
	 * without even invoking "mu_rndwn_file" as that expects the db file to exist. Same case if shared memory
	 * points back to a database whose file header does not have this shmid.
	 */
	if (-1 == Stat(fname, &st_buff))
	{
		if (ENOENT == errno)
		{
			SNPRINTF(msgbuff, OUT_BUFF_SIZE, "File %s does not exist", fname);
			if (1 < shmstat.shm_nattch)
			{
				PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid);
				*exit_stat = ERR_MUNOTALLSEC;
				shmdt((void *)start_addr);
				return FALSE;
			}
			remove_shmid = TRUE;
		} else
		{	/* Stat errored out e.g. due to file permissions. Log that */
			save_errno = errno;
			util_out_print("Cannot rundown shmid !UL for database file !AD as stat() on the file"
				" returned the following error", TRUE, shmid, fname_len, fname);
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno);
			*exit_stat = ERR_MUNOTALLSEC;
			shmdt((void *)start_addr);
			return FALSE;
		}
	} else
	{
		mu_gv_cur_reg_init();
		gv_cur_region->dyn.addr->fname_len = strlen(fname);
		STRNCPY_STR(gv_cur_region->dyn.addr->fname, fname, gv_cur_region->dyn.addr->fname_len);
		fc = gv_cur_region->dyn.addr->file_cntl;
		fc->op = FC_OPEN;
		status = dbfilop(fc);
		if (SS_NORMAL != status)
		{
			util_out_print("!AD -> Error with dbfilop for shmid = !UL", TRUE, fname_len, fname, shmid);
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) status, 2, DB_LEN_STR(gv_cur_region), errno);
			*exit_stat = ERR_MUNOTALLSEC;
			shmdt((void *)start_addr);
			return FALSE;
		}
		udi = FILE_INFO(gv_cur_region);
		assert(!udi->fd_opened_with_o_direct);
		DB_LSEEKREAD(udi, udi->fd, 0, &tsd, SIZEOF(sgmnt_data), status);
		if (0 != status)
		{
			save_errno = errno;
			util_out_print("!AD -> Error with LSEEKREAD for shmid = !UL", TRUE, fname_len, fname, shmid);
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno);
			*exit_stat = ERR_MUNOTALLSEC;
			shmdt((void *)start_addr);
			return FALSE;
		}
		mu_gv_cur_reg_free();
		if (tsd.shmid != shmid)
		{
			if (tsd.read_only)
				return TRUE;
			SNPRINTF(msgbuff, OUT_BUFF_SIZE, "Shared memory ID (%d) in the DB file header does not match with the one"
					" reported by \"ipcs\" command (%d)", tsd.shmid, shmid);
			if (1 < shmstat.shm_nattch)
			{
				PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid);
				*exit_stat = ERR_MUNOTALLSEC;
				shmdt((void *)start_addr);
				return FALSE;
			}
			remove_shmid = TRUE;
		} else if (tsd.gt_shm_ctime.ctime != shmstat.shm_ctime)
		{
			SNPRINTF(msgbuff, OUT_BUFF_SIZE, "Shared memory creation time in the DB file header does not match with"
					" the one reported by shmctl");
			if (1 < shmstat.shm_nattch)
			{
				PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid);
				*exit_stat = ERR_MUNOTALLSEC;
				shmdt((void *)start_addr);
				return FALSE;
			}
			remove_shmid = TRUE;
		}
	}
	shmdt((void *)start_addr);
	if (remove_shmid)
	{
		assert('\0' != msgbuff[0]);
		if (0 != shm_rmid(shmid))
		{
			save_errno = errno;
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_DBFILERR, 2, fname_len, fname,
				   ERR_TEXT, 2, RTS_ERROR_TEXT("Error removing shared memory"));
			util_out_print("!AD -> Error removing shared memory for shmid = !UL", TRUE, fname_len, fname, shmid);
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno);
			*exit_stat = ERR_MUNOTALLSEC;
			return FALSE;
		}
		PRINT_AND_SEND_SHMREMOVED_MSG(msgbuff, fname_len, fname, shmid);
		*exit_stat = ERR_SHMREMOVED;
	} else
		*exit_stat = SS_NORMAL;
	return TRUE;
}

/* Takes an entry from 'ipcs -am' and checks for its validity to be a GT.M replication segment.
 * Returns TRUE if the shared memory segment is a valid GT.M replication segment
 * (based on a check on some fields in the shared memory) else FALSE.
 * If the segment belongs to GT.M, it returns the replication id of the segment
 * by the second argument.
 * Sets exit_stat to ERR_MUNOTALLSEC if appropriate.
 */
boolean_t validate_replpool_shm_entry(shm_parms *parm_buff, replpool_id_ptr_t replpool_id, int *exit_stat)
{
	boolean_t		remove_shmid, jnlpool_segment;
	int			fd;
	repl_inst_hdr		repl_instance;
	sm_uc_ptr_t		start_addr;
	int			save_errno, status, shmid;
	struct shmid_ds		shmstat;
	char			msgbuff[OUT_BUFF_SIZE], *instfilename;

	if (NULL == parm_buff)
		return FALSE;
	/* Check for the bare minimum size of the replic shared segment that we expect */
	/* if (parm_buff->sgmnt_siz < (SIZEOF(replpool_identifier) + MIN(MIN_JNLPOOL_SIZE, MIN_RECVPOOL_SIZE))) */
	if (parm_buff->sgmnt_siz < MIN(MIN_JNLPOOL_SIZE, MIN_RECVPOOL_SIZE))
		return FALSE;
	if (IPC_PRIVATE != parm_buff->key)
		return FALSE;
	shmid = parm_buff->shmid;
	/* we do not need to lock the shm for reading the rundown information as
	 * the other rundowns (if any) can also be allowed to share reading the
	 * same info concurrently.
	 */
	if (-1 == (sm_long_t)(start_addr = (sm_uc_ptr_t) do_shmat(shmid, 0, SHM_RND)))
		return FALSE;
	memcpy((void *)replpool_id, (void *)start_addr, SIZEOF(replpool_identifier));
	instfilename = replpool_id->instfilename;
	/* Even though we could be looking at a replication pool structure that has been created by an older version
	 * or newer version of GT.M, the format of the "replpool_identifier" structure is expected to be the same
	 * across all versions so we can safely dereference the "label" and "instfilename" fields in order to generate
	 * user-friendly error messages. Asserts for the layout are in "mu_rndwn_repl_instance" (not here) with a
	 * comment there as to why that location was chosen.
	 */
	if (memcmp(replpool_id->label, GDS_RPL_LABEL, GDS_LABEL_SZ - 1))
	{
		if (!memcmp(replpool_id->label, GDS_RPL_LABEL, GDS_LABEL_SZ - 3))
		{
			util_out_print("Cannot rundown replpool shmid = !UL as it has format !AD "
				"created by !AD but this mupip is version and uses format !AD",
				TRUE, shmid, GDS_LABEL_SZ - 1, replpool_id->label,
				LEN_AND_STR(replpool_id->now_running), gtm_release_name_len, gtm_release_name,
				GDS_LABEL_SZ - 1, GDS_RPL_LABEL);
			*exit_stat = ERR_MUNOTALLSEC;
		}
		shmdt((void *)start_addr);
		return FALSE;
	}
	assert(JNLPOOL_SEGMENT == replpool_id->pool_type || RECVPOOL_SEGMENT == replpool_id->pool_type);
	if(JNLPOOL_SEGMENT != replpool_id->pool_type && RECVPOOL_SEGMENT != replpool_id->pool_type)
	{
		shmdt((void *)start_addr);
		return FALSE;
	}
	jnlpool_segment = (JNLPOOL_SEGMENT == replpool_id->pool_type);
	if (-1 == shmctl(shmid, IPC_STAT, &shmstat))
	{
		save_errno = errno;
		assert(FALSE);/* we were able to attach to this shmid before so should be able to get stats on it */
		util_out_print("!AD -> Error with shmctl for shmid = !UL",
				TRUE, LEN_AND_STR(instfilename), shmid);
		gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno);
		*exit_stat = ERR_MUNOTALLSEC;
		shmdt((void *)start_addr);
		return FALSE;
	}
	/* Check if instance filename reported in shared memory still exists. If not, clean this
	 * shared memory section without even invoking "mu_rndwn_repl_instance" as that expects
	 * the instance file to exist. Same case if shared memory points back to an instance file
	 * whose file header does not have this shmid.
	 */
	OPENFILE(instfilename, O_RDONLY, fd);	/* check if we can open it */
	msgbuff[0] = '\0';
	remove_shmid = FALSE;
	if (FD_INVALID == fd)
	{
		if (ENOENT == errno)
		{
			SNPRINTF(msgbuff, OUT_BUFF_SIZE, "File %s does not exist", instfilename);
			if (1 < shmstat.shm_nattch)
			{
				PRINT_AND_SEND_REPLPOOL_FAILURE_MSG(msgbuff, replpool_id, shmid);
				*exit_stat = ERR_MUNOTALLSEC;
				shmdt((void *)start_addr);
				return FALSE;
			}
			remove_shmid = TRUE;
		} else
		{	/* open() errored out e.g. due to file permissions. Log that */
			save_errno = errno;
			util_out_print("Cannot rundown replpool shmid !UL for instance file"
				" !AD as open() on the file returned the following error",
				TRUE, shmid, LEN_AND_STR(instfilename));
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno);
			*exit_stat = ERR_MUNOTALLSEC;
			shmdt((void *)start_addr);
			return FALSE;
		}
	} else
	{
		LSEEKREAD(fd, 0, &repl_instance, SIZEOF(repl_inst_hdr), status);
		if (0 != status)
		{
			save_errno = errno;
			util_out_print("!AD -> Error with LSEEKREAD for shmid = !UL", TRUE,
				LEN_AND_STR(instfilename), shmid);
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno);
			*exit_stat = ERR_MUNOTALLSEC;
			shmdt((void *)start_addr);
			return FALSE;
		}
		if ((jnlpool_segment && (repl_instance.jnlpool_shmid != shmid))
			|| (!jnlpool_segment && (repl_instance.recvpool_shmid != shmid)))
		{
			SNPRINTF(msgbuff, OUT_BUFF_SIZE, "%s SHMID (%d) in the instance file header does not match with the"
					" one reported by \"ipcs\" command (%d)", jnlpool_segment ? "Journal Pool" : "Receive Pool",
					jnlpool_segment ? repl_instance.jnlpool_shmid : repl_instance.recvpool_shmid, shmid);
			if (1 < shmstat.shm_nattch)
			{
				PRINT_AND_SEND_REPLPOOL_FAILURE_MSG(msgbuff, replpool_id, shmid);
				*exit_stat = ERR_MUNOTALLSEC;
				shmdt((void *)start_addr);
				return FALSE;
			}
			remove_shmid = TRUE;
		}
		CLOSEFILE_RESET(fd, status);	/* resets "fd" to FD_INVALID */
	}
	shmdt((void *)start_addr);
	if (remove_shmid)
	{
		assert('\0' != msgbuff[0]);
		if (0 != shm_rmid(shmid))
		{
			save_errno = errno;
			util_out_print("!AD -> Error removing shared memory for shmid = !UL",
				TRUE, LEN_AND_STR(instfilename), shmid);
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno);
			*exit_stat = ERR_MUNOTALLSEC;
			return FALSE;
		}
		PRINT_AND_SEND_SHMREMOVED_MSG(msgbuff, STRLEN(instfilename), instfilename, shmid);
		*exit_stat = ERR_SHMREMOVED;
	} else
		*exit_stat = SS_NORMAL;
	return TRUE;
}

/* Gets all the required fields in shm_parms struct for a given shm entry */

shm_parms *get_shm_parm(char *entry)
{
	char 		*parm;
	shm_parms	*parm_buff;
	struct shmid_ds	shm_buf;

	parm_buff = (shm_parms *)malloc(SIZEOF(shm_parms));

	parm = parse_shm_entry(entry, SHMID);
	CONVERT_TO_NUM(shmid);
	parm = parse_shm_entry(entry, KEY);
	CONVERT_TO_NUM(key);

	/* get shm segment size directly from shmid_ds instead of parsing ipcs
	 * output (thus avoiding the -a option for ipcs in mu_rndwn_all()
	 */
	if (-1 == shmctl(parm_buff->shmid, IPC_STAT, &shm_buf))
	{
		free(parm_buff);
		return NULL;
	}
	parm_buff->sgmnt_siz = shm_buf.shm_segsz;
	return parm_buff;
}

/* Parses the output of 'IPCS_CMD_STR' command. Returns the value of the
 * specified field.
 */

/* NOTE : Even though the standard says that every column in the output
 * of 'ipcs' command should be separated by atleast one space, we have
 * observed a case where there is no space between the first (T) and
 * second (ID) fields on AIX under certain conditions.
 * The workaround is to always insert a blank space for all UNIX platforms
 * after the first (T field) character assuming the entry always starts with a
 * character describing the type of the ipc resource ('m' for shared memory).
 * On linux, the ipcs output starts with a KEY field (a hexadecimal number).
 * See the definition of IPCS_CMD_STR for this handling
 */
char *parse_shm_entry(char *entry, int which_field)
{
	char 	*parm;
	int 	iter, indx1 = 0, indx2 = 0;

	for(iter = 1; iter < which_field; iter++)
	{
		/* Strip leading spaces */
		while(entry[indx1] == ' ')
			indx1++;
		/* Accept until spaces or NULL */
		while(entry[indx1] && entry[indx1] != ' ')
			indx1++;
	}
	/* Strip leading spaces */
	while(entry[indx1] == ' ')
		indx1++;
	if ('\0' == entry[indx1])
	{
		assert(FALSE);
		return NULL;
	}
	parm = (char *)malloc(MAX_PARM_LEN);
	/* Copy value from entry until NULL or a space character */
	while(entry[indx1] && (entry[indx1] != ' ') && (indx2 < (MAX_PARM_LEN - 1)))
		parm[indx2++] = entry[indx1++];
	parm[indx2] = '\0';

	return parm;
}

int parse_sem_id(char *entry)
{
	char 	*parm;
	int 	iter, indx1 = 0, indx2;

	while(entry[indx1] == ' ')
		indx1++;
	while(entry[indx1] && entry[indx1] != ' ')
		indx1++;
	while(entry[indx1] == ' ')
		indx1++;
	if ('\0' == entry[indx1])
	{
		assert(FALSE);
		return -1;
	}
	indx2 = indx1;
	parm = &entry[indx1];
	while(entry[indx2] && entry[indx2] != ' ')
		indx2++;
	entry[indx2] = '\0';
	if (cli_is_dcm(parm))
		return (int)STRTOUL(parm, NULL, 10);
	else if (cli_is_hex(parm + 2))
		return (int)STRTOUL(parm, NULL, 16);
	else
	{
		assert(FALSE);
		return -1;
	}
}

int mu_rndwn_sem_all(void)
{
	int 			save_errno, exit_status = SS_NORMAL, semid;
	char			entry[MAX_ENTRY_LEN];
	FILE			*pf;
	char			fname[MAX_FN_LEN + 1], *fgets_res;
	boolean_t 		rem_sem;
	shm_parms		*parm_buff;

	if (NULL == (pf = POPEN(IPCS_SEM_CMD_STR ,"r")))
        {
		save_errno = errno;
		gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("POPEN()"), CALLFROM, save_errno);
                return ERR_MUNOTALLSEC;
        }
	while (NULL != (FGETS(entry, SIZEOF(entry), pf, fgets_res)) && entry[0] != '\n')
	{
		if (-1 != (semid = parse_sem_id(entry)))
		{
			if (is_orphaned_gtm_semaphore(semid))
			{	/* semval == 0 and corresponding shared memory has been removed */
				if (-1 != semctl(semid, 0, IPC_RMID))
				{
					gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SEMREMOVED, 1, semid);
					send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SEMREMOVED, 1, semid);
				}
			}
		}
	}
	pclose(pf);
	return exit_status;
}

boolean_t is_orphaned_gtm_semaphore(int semid)
{
	int			semno, semval;
	struct semid_ds		semstat;
	union semun		semarg;

	semarg.buf = &semstat;
	if (-1 != semctl(semid, 0, IPC_STAT, semarg))
	{
		if (-1 == (semval = semctl(semid, semarg.buf->sem_nsems - 1, GETVAL)) || GTM_ID != semval)
			return FALSE;
		else
		{
			/* Make sure all has value = 0 */
			for (semno = 0; semno < semarg.buf->sem_nsems - 1; semno++)
				if (-1 == (semval = semctl(semid, semno, GETVAL)) || semval)
					return FALSE;
		}
		/* We know that the semvals are 0, and this is a GTM semaphore. Now, we can remove it if it is not specifically
		 * meant to be kept.
		 */
		return  !in_keep_sems_list(semid);
	}
	return FALSE;
}

boolean_t in_keep_sems_list(int semid)
{
	semid_queue_elem	*iter;

	iter = keep_semids;
	while (iter)
	{
		if (iter->semid == semid)
			return TRUE;
		iter = iter->prev;
	}
	return FALSE;
}

void add_to_semids_list(int semid)
{
	semid_queue_elem	*new_semid_elem;

	new_semid_elem = (semid_queue_elem*) malloc(SIZEOF(semid_queue_elem));
	new_semid_elem->semid = semid;
	new_semid_elem->prev = keep_semids;
	keep_semids = new_semid_elem;
}
