/****************************************************************
 *								*
 * Copyright (c) 2003-2020 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 "gtm_stdio.h"
#include "gtm_string.h"

#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "jnl.h"
#include "cli.h"
#include "gdsblk.h"
#include "util.h"
#include "dse.h"
#include "stringpool.h"			/* for GET_CURR_TIME_IN_DOLLARH_AND_ZDATE macro */
#include "op.h"				/* for op_fnzdate and op_zhorolog prototype */
#include "wcs_recover.h"		/* for wcs_recover prototype */
#include "wcs_phase2_commit_wait.h"
#include "sleep_cnt.h"			/* for SIGNAL_WRITERS_TO_STOP/RESUME and WAIT_FOR_WRITERS_TO_STOP macro */
#include "memcoherency.h"		/* for SIGNAL_WRITERS_TO_STOP/RESUME and WAIT_FOR_WRITERS_TO_STOP macro */
#include "wcs_sleep.h"			/* for SIGNAL_WRITERS_TO_STOP/RESUME and WAIT_FOR_WRITERS_TO_STOP macro */

GBLREF gd_region	*gv_cur_region;
GBLREF gd_addr		*original_header;

error_def(ERR_SIZENOTVALID4);

#define	DB_ABS2REL(X)		(uintszofptr_t)((uintszofptr_t)(X) - (uintszofptr_t)csa->nl)
#define	CLEAN_VERIFY		"verification is clean"
#define	UNCLEAN_VERIFY		"verification is NOT clean (see operator log for details)"
#define	RECOVER_DONE		"recovery complete (see operator log for details)"
#define	RECOVER_NOT_APPLIC	"recovery not applicable with MM access method"
#define	OUT_LINE	256 + 1

error_def(ERR_SIZENOTVALID4);

void dse_cache(void)
{
	boolean_t		all_present, change_present, recover_present, show_present, verify_present, was_crit, is_clean;
	boolean_t		nocrit_present, offset_present, size_present, value_present;
	gd_region		*reg, *r_top;
	sgmnt_addrs		*csa;
	sgmnt_data_ptr_t	csd;
	mval			dollarh_mval, zdate_mval;
	int4			size;
	uint4			offset, value, old_value, lcnt;
	char			dollarh_buffer[MAXNUMLEN], zdate_buffer[SIZEOF(DSE_DMP_TIME_FMT)];
	char			temp_str[OUT_LINE], temp_str1[OUT_LINE];
	sm_uc_ptr_t		chng_ptr;
	cache_rec_ptr_t		cr_que_lo;
	boolean_t		is_mm, was_hold_onto_crit, wc_blocked_ok;
	uintszofptr_t		section_offset;

	all_present = (CLI_PRESENT == cli_present("ALL"));

	recover_present = (CLI_PRESENT == cli_present("RECOVER"));
	verify_present = (CLI_PRESENT == cli_present("VERIFY"));
	change_present = (CLI_PRESENT == cli_present("CHANGE"));
	show_present = (CLI_PRESENT == cli_present("SHOW"));

	offset_present = (CLI_PRESENT == cli_present("OFFSET"));
	size_present = (CLI_PRESENT == cli_present("SIZE"));
	value_present = (CLI_PRESENT == cli_present("VALUE"));

	nocrit_present = (CLI_NEGATED == cli_present("CRIT"));
	assert(!nocrit_present || show_present);	/* NOCRIT is only applicable for SHOW */
	if (offset_present && !cli_get_hex("OFFSET", &offset))
		return;
	if (size_present)
	{
		if (!cli_get_int("SIZE",   &size))
			return;
		if (!((SIZEOF(char) == size) || (SIZEOF(short) == size) || (SIZEOF(int4) == size)))
			rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_SIZENOTVALID4);
	}
	if (value_present  && !cli_get_hex("VALUE",  &value))
		return;
	assert(change_present || recover_present || show_present || verify_present);
	for (reg = original_header->regions, r_top = reg + original_header->n_regions; reg < r_top; reg++)
	{
		if (!all_present && (reg != gv_cur_region))
			continue;
		if (!reg->open || reg->was_open)
			continue;
		is_mm = (dba_mm == reg->dyn.addr->acc_meth);
		csa = &FILE_INFO(reg)->s_addrs;
		assert(is_mm || (csa->db_addrs[0] == (sm_uc_ptr_t)csa->nl));
		was_crit = csa->now_crit;
		DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, csa, reg);
		if (verify_present || recover_present)
		{
			GET_CURR_TIME_IN_DOLLARH_AND_ZDATE(dollarh_mval, dollarh_buffer, zdate_mval, zdate_buffer);
			if (verify_present)
			{	/* Before invoking wcs_verify, wait for any pending phase2 commits to finish. Need to wait as
				 * otherwise ongoing phase2 commits can result in cache verification returning FALSE (e.g. due to
				 * DBCRERR message indicating that cr->in_tend is non-zero).
				 * Also, need to wait for concurrent writers to stop to avoid wcs_verify from incorrectly concluding
				 * that there is a problem with the active queue.
				 */
				wc_blocked_ok = UNIX_ONLY(TRUE) VMS_ONLY(!is_mm); /* MM on VMS doesn't support wcs_recvoer */
				if (wc_blocked_ok)
					SIGNAL_WRITERS_TO_STOP(csa->nl); /* done sooner to avoid any new writers starting up */
				if (csa->nl->wcs_phase2_commit_pidcnt && !is_mm)
				{	/* No need to check return value since even if it fails, we want to do cache verification */
					wcs_phase2_commit_wait(csa, NULL);
				}
				if (wc_blocked_ok)
					WAIT_FOR_WRITERS_TO_STOP(csa->nl, lcnt, MAXWTSTARTWAIT / 4); /* reduced wait time for DSE */
				is_clean = wcs_verify(reg, FALSE, FALSE); /* expect_damage is FALSE, caller_is_wcs_recover is
									   * FALSE */
				if (wc_blocked_ok)
					SIGNAL_WRITERS_TO_RESUME(csa->nl);
			} else
			{
				if (UNIX_ONLY(TRUE)VMS_ONLY(!is_mm))
				{
					SET_TRACEABLE_VAR(csa->nl->wc_blocked, WC_BLOCK_RECOVER);
					/* No need to invoke function "wcs_phase2_commit_wait" as "wcs_recover" does that anyways */
					wcs_recover(reg);
					assert(WC_UNBLOCK == csa->nl->wc_blocked);	/* wcs_recover() should have cleared this */
				}
			}
			assert(20 == STR_LIT_LEN(DSE_DMP_TIME_FMT)); /* if they are not the same, the !20AD below should change */
			util_out_print("Time !20AD : Region !12AD : Cache !AZ", TRUE, zdate_mval.str.len, zdate_mval.str.addr,
				       REG_LEN_STR(reg), verify_present ? (is_clean ? CLEAN_VERIFY : UNCLEAN_VERIFY)
				       	: UNIX_ONLY(RECOVER_DONE)
				          VMS_ONLY(is_mm ? RECOVER_NOT_APPLIC : RECOVER_DONE));
		} else if (offset_present)
		{
			if ((csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE)) < (offset + size))
				util_out_print("Region !12AD : Error: offset + size is greater than region's max_offset = 0x!XL",
						TRUE, REG_LEN_STR(reg), (csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE)));
			else
			{
				chng_ptr = (sm_uc_ptr_t)csa->nl + offset;
				if (SIZEOF(char) == size)
				{
					SNPRINTF(temp_str, OUT_LINE, "!UB [0x!XB]");
					old_value = *(sm_uc_ptr_t)chng_ptr;
				} else if (SIZEOF(short) == size)
				{
					SNPRINTF(temp_str, OUT_LINE, "!UW [0x!XW]");
					old_value = *(sm_ushort_ptr_t)chng_ptr;
				} else if (SIZEOF(int4) == size)
				{
					SNPRINTF(temp_str, OUT_LINE, "!UL [0x!XL]");
					old_value = *(sm_uint_ptr_t)chng_ptr;
				}
				if (value_present)
				{
					if (SIZEOF(char) == size)
						*(sm_uc_ptr_t)chng_ptr = value;
					else if (SIZEOF(short) == size)
						*(sm_ushort_ptr_t)chng_ptr = value;
					else if (SIZEOF(int4) == size)
						*(sm_uint_ptr_t)chng_ptr = value;
				} else
					value = old_value;
				if (show_present)
				{
					SNPRINTF(temp_str1, OUT_LINE, "Region !12AD : Location !UL [0x!XL] : Value = %s :"
						" Size = !UB [0x!XB]", temp_str);
					util_out_print(temp_str1, TRUE, REG_LEN_STR(reg), offset, offset, value, value, size, size);
				} else
				{
					SNPRINTF(temp_str1, OUT_LINE, "Region !12AD : Location !UL [0x!XL] : Old Value = %s : "
						"New Value = %s : Size = !UB [0x!XB]", temp_str, temp_str);
					util_out_print(temp_str1, TRUE, REG_LEN_STR(reg), offset, offset,
						old_value, old_value, value, value, size, size);
				}
			}
		} else
		{
			assert(show_present);	/* this should be a DSE CACHE -SHOW command with no other qualifiers */
			util_out_print("Region !AD : Shared_memory       = 0x!XJ",
				TRUE, REG_LEN_STR(reg), csa->nl);
			util_out_print("Region !AD :  node_local         = 0x!XJ",
				TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->nl));
			util_out_print("Region !AD :  critical           = 0x!XJ",
				TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->critical));
			if (JNL_ALLOWED(csa))
			{
				util_out_print("Region !AD :  jnl_buffer_struct  = 0x!XJ",
					TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->jnl->jnl_buff));
				util_out_print("Region !AD :  jnl_buffer_data    = 0x!XJ", TRUE, REG_LEN_STR(reg),
					DB_ABS2REL(&csa->jnl->jnl_buff->buff[csa->jnl->jnl_buff->buff_off]));
			}
			util_out_print("Region !AD :  shmpool_buffer     = 0x!XJ",
				TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->shmpool_buffer));
			util_out_print("Region !AD :  lock_space         = 0x!XJ",
				TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->mlkctl));
			csd = csa->hdr;
			if (!is_mm)
			{
				util_out_print("Region !AD :  cache_queues_state = 0x!XJ",
					TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->acc_meth.bg.cache_state));
				cr_que_lo = &csa->acc_meth.bg.cache_state->cache_array[0];
				util_out_print("Region !AD :  cache_que_header   = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL",
					TRUE, REG_LEN_STR(reg), DB_ABS2REL(cr_que_lo), csd->bt_buckets, SIZEOF(cache_rec));
				util_out_print("Region !AD :  cache_record       = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL",
					TRUE, REG_LEN_STR(reg), DB_ABS2REL(cr_que_lo + csd->bt_buckets), csd->n_bts,
					SIZEOF(cache_rec));
				section_offset = ROUND_UP2(DB_ABS2REL(cr_que_lo + csd->bt_buckets + csd->n_bts), OS_PAGE_SIZE);
				util_out_print("Region !AD :  global_buffer      = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL",
					TRUE, REG_LEN_STR(reg), section_offset, csd->n_bts, csd->blk_size);
				if (USES_ENCRYPTION(csd->is_encrypted))
				{
					section_offset += (gtm_uint64_t)csd->n_bts * csd->blk_size;
					/* In case of an encrypted database, bp_top is actually the beginning of the encrypted
					 * global buffer array (an array maintained parallely with the regular unencrypted global
					 * buffer array).
					 */
					util_out_print("Region !AD :  encrypted_globuff  = 0x!XJ : Numelems = 0x!XL : Elemsize = "
						"0x!XL", TRUE, REG_LEN_STR(reg), section_offset, csd->n_bts, csd->blk_size);
				}
				util_out_print("Region !AD :  db_file_header     = 0x!XJ", TRUE,
					REG_LEN_STR(reg), DB_ABS2REL(csd));
				util_out_print("Region !AD :  bt_que_header      = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL",
					TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->bt_header), csd->bt_buckets, SIZEOF(bt_rec));
				util_out_print("Region !AD :  th_base            = 0x!XJ",
					TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->th_base));
				util_out_print("Region !AD :  bt_record          = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL",
					TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->bt_base), csd->n_bts, SIZEOF(bt_rec));
				util_out_print("Region !AD :  shared_memory_size = 0x!XJ",
					TRUE, REG_LEN_STR(reg), csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE));
			} else
			{
				util_out_print("Region !AD :  shared_memory_size = 0x!XJ",
					TRUE, REG_LEN_STR(reg), csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE));
				util_out_print("Region !AD :  db_file_header     = 0x!XJ", TRUE, REG_LEN_STR(reg), csd);
			}
		}
		DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, csa, reg);
	}
	return;
}
