/****************************************************************
 *								*
 *	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_string.h"

#include "gtm_stdio.h"
#include "util.h"
#include "cli.h"
#include "gdsroot.h"
#include "gt_timer.h"
#include "gtmmsg.h"

#if defined(UNIX)
#include "gtm_ipc.h"
#endif

#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "jnl.h"
#include "gdskill.h"
#include "gdscc.h"
#include "buddy_list.h"		/* needed for tp.h */
#include "hashtab_int4.h"	/* needed for tp.h */
#include "tp.h"
#include "min_max.h"		/* needed for init_root_gv.h */
#include "init_root_gv.h"
#include "dse.h"
#ifdef UNIX
# include "mutex.h"
#endif
#include "wcs_flu.h"
#include <signal.h>		/* for VSIG_ATOMIC_T */

GBLREF	VSIG_ATOMIC_T	util_interrupt;
GBLREF	block_id	patch_curr_blk;
GBLREF	gd_addr		*gd_header;
GBLREF	gd_region	*gv_cur_region;
GBLREF	sgmnt_addrs	*cs_addrs;
GBLREF	short		crash_count;
GBLREF	uint4		process_id;
GBLREF	gd_addr		*original_header;
GBLREF	boolean_t	dse_all_dump;		/* TRUE if DSE ALL -DUMP is specified */

error_def(ERR_DBRDONLY);
error_def(ERR_DSEWCINITCON);
error_def(ERR_FREEZE);
error_def(ERR_FREEZECTRL);

void dse_all(void)
{
	gd_region	*ptr;
	tp_region	*region_list, *rg, *rg_last, *rg_new;	/* A handy structure for maintaining a list of regions */
	int		i, j;
	sgmnt_addrs	*old_addrs, *csa;
	gd_region	*old_region;
	block_id	old_block;
	int4		stat;
	boolean_t	ref = FALSE;
	boolean_t	crit = FALSE;
	boolean_t	wc = FALSE;
	boolean_t	flush = FALSE;
	boolean_t	freeze = FALSE;
	boolean_t	nofreeze = FALSE;
	boolean_t	seize = FALSE;
	boolean_t	release = FALSE;
	boolean_t	dump = FALSE;
	boolean_t	override = FALSE;
	boolean_t	was_crit;
	gd_addr         *temp_gdaddr;
	gd_binding      *map;
	UNIX_ONLY(char	*fgets_res;)

	old_addrs = cs_addrs;
	old_region = gv_cur_region;
	old_block = patch_curr_blk;
	temp_gdaddr = gd_header;
	gd_header = original_header;
	if (cli_present("RENEW") == CLI_PRESENT)
	{
		crit = ref = wc = nofreeze = TRUE;
		GET_CONFIRM_AND_HANDLE_NEG_RESPONSE;
	} else
	{
		if (cli_present("CRITINIT") == CLI_PRESENT)
			crit = TRUE;
		if (cli_present("REFERENCE") == CLI_PRESENT)
			ref = TRUE;
		if (cli_present("WCINIT") == CLI_PRESENT)
		{
			GET_CONFIRM_AND_HANDLE_NEG_RESPONSE;
			wc = TRUE;
		}
		if (cli_present("BUFFER_FLUSH") == CLI_PRESENT)
			flush = TRUE;
		if (cli_present("SEIZE") == CLI_PRESENT)
			seize = TRUE;
		if (cli_present("RELEASE") == CLI_PRESENT)
			release = TRUE;
		stat = cli_present("FREEZE");
		if (stat == CLI_NEGATED)
			nofreeze = TRUE;
		else if (stat == CLI_PRESENT)
		{
			freeze = TRUE;
			nofreeze = FALSE;
		}
                if (cli_present("OVERRIDE") == CLI_PRESENT)
                        override = TRUE;
                if (cli_present("DUMP") == CLI_PRESENT)
			dump = TRUE;
	}
        if (!dump && gv_cur_region->read_only)
                rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region));
	region_list = NULL;
	for (i = 0, ptr = gd_header->regions; i < gd_header->n_regions; i++, ptr++)
	{
		if (ptr->dyn.addr->acc_meth != dba_bg && ptr->dyn.addr->acc_meth != dba_mm)
		{
			util_out_print("Skipping region !AD: not BG or MM access",TRUE,ptr->rname_len,&ptr->rname[0]);
			continue;
		}
		if (!ptr->open)
		{
			util_out_print("Skipping region !AD as it is not bound to any namespace.", TRUE,
				ptr->rname_len, &ptr->rname[0]);
			continue;
		}
		if (dump)
		{
			gv_cur_region = ptr;
			cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs;
			dse_all_dump = TRUE;
			dse_dmp_fhead();
			assert(!dse_all_dump);	/* should have been reset by "dse_dmp_fhead" */
		} else
		{
			/* put on region list in order of ftok value so processed in same order that crits are obtained */
			csa = &FILE_INFO(ptr)->s_addrs;
			insert_region(ptr, &(region_list), NULL, SIZEOF(tp_region));
		}
	}
	if (!dump)
	{	/* Now run the list of regions in the sorted ftok order to execute the desired commands */
		for (rg = region_list; NULL != rg; rg = rg->fPtr)
		{
			gv_cur_region = rg->reg;
			switch(gv_cur_region->dyn.addr->acc_meth)
			{
			case dba_mm:
			case dba_bg:
				cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs;
				break;
			default:
				GTMASSERT;
			}
			patch_curr_blk = get_dir_root();

			if (crit)
			{
				UNIX_ONLY(gtm_mutex_init(gv_cur_region, NUM_CRIT_ENTRY, TRUE);)
				VMS_ONLY(mutex_init(cs_addrs->critical, NUM_CRIT_ENTRY, TRUE);)
				cs_addrs->nl->in_crit = 0;
				cs_addrs->hold_onto_crit = FALSE;	/* reset this just before cs_addrs->now_crit is reset */
				cs_addrs->now_crit = FALSE;
			}
			if (cs_addrs->critical)
				crash_count = cs_addrs->critical->crashcnt;
			if (freeze)
			{
				while (REG_ALREADY_FROZEN == region_freeze(gv_cur_region, TRUE, override, FALSE))
				{
					hiber_start(1000);
					if (util_interrupt)
					{
						gtm_putmsg(VARLSTCNT(1) ERR_FREEZECTRL);
                        	                break;
					}
				}
				if (freeze != !(cs_addrs->hdr->freeze))
					util_out_print("Region !AD is now FROZEN", TRUE, REG_LEN_STR(gv_cur_region));
			}
			was_crit = cs_addrs->now_crit;
			if (seize)
			{
				if (!was_crit)
					grab_crit(gv_cur_region);	/* no point seizing crit if WE already have it held */
				cs_addrs->hold_onto_crit = TRUE; /* need to do this AFTER grab_crit */
				cs_addrs->dse_crit_seize_done = TRUE;
			}
			if (wc)
			{
				if (!was_crit && !seize)
					grab_crit(gv_cur_region);
				DSE_WCREINIT(cs_addrs);
				if (!was_crit && (!seize || release))
					rel_crit(gv_cur_region);
			}
			if (flush)
				wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_SYNC_EPOCH);
			if (release)
			{	/* user wants crit to be released unconditionally so "was_crit" not checked like everywhere else */
				if (cs_addrs->now_crit)
				{
					cs_addrs->dse_crit_seize_done = FALSE;
					cs_addrs->hold_onto_crit = FALSE; /* need to do this BEFORE rel_crit */
					rel_crit(gv_cur_region);
				}
				else
				{
					assert(!cs_addrs->hold_onto_crit && !cs_addrs->dse_crit_seize_done);
					util_out_print("Current process does not own the Region: !AD.",
						TRUE, REG_LEN_STR(gv_cur_region));
				}
			}
			if (nofreeze)
			{
				if (REG_ALREADY_FROZEN == region_freeze(gv_cur_region,FALSE, override, FALSE))
					util_out_print("Region: !AD is frozen by another user, not releasing freeze",TRUE,
						REG_LEN_STR(gv_cur_region));
				else
					util_out_print("Region !AD is now UNFROZEN", TRUE, REG_LEN_STR(gv_cur_region));
			}
			if (ref)
				cs_addrs->nl->ref_cnt = 1;
		}
	}
	cs_addrs = old_addrs;
	gv_cur_region = old_region;
	patch_curr_blk = old_block;
	GET_SAVED_GDADDR(gd_header, temp_gdaddr, map, gv_cur_region);
	return;
}
