361 lines
15 KiB
C
361 lines
15 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2003, 2011 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 "gtm_time.h"
|
|
|
|
#include <math.h> /* needed for handling of epoch_interval (EPOCH_SECOND2SECOND macro uses ceil) */
|
|
|
|
#include "gdsroot.h"
|
|
#include "gdsbt.h"
|
|
#include "gtm_facility.h"
|
|
#include "fileinfo.h"
|
|
#include "gdsfhead.h"
|
|
#include "filestruct.h"
|
|
#include "jnl.h"
|
|
#include "buddy_list.h"
|
|
#include "hashtab_int4.h" /* needed for muprec.h */
|
|
#include "hashtab_int8.h" /* needed for muprec.h */
|
|
#include "hashtab_mname.h" /* needed for muprec.h */
|
|
#include "muprec.h"
|
|
#include "cli.h"
|
|
#include "util.h"
|
|
#ifdef VMS
|
|
#include <descrip.h>
|
|
#include <jpidef.h>
|
|
#endif
|
|
#include "real_len.h" /* for real_len() prototype */
|
|
|
|
GBLREF mur_opt_struct mur_options;
|
|
GBLREF reg_ctl_list *mur_ctl;
|
|
GBLREF mur_gbls_t murgbl;
|
|
|
|
LITREF char *jrt_label[JRT_RECTYPES];
|
|
|
|
static const char statistics_fao[] = " !5AZ !10UL";
|
|
static const char statistics_header[] = "!/Record type Count";
|
|
static const char dashes_fao[] = "!#*-";
|
|
|
|
#define DOUBLE_ARG(X) X,X
|
|
|
|
#define PRINT_SHOW_HEADER(jctl) \
|
|
{ \
|
|
util_out_print("!/-------------------------------------------------------------------------------", TRUE); \
|
|
util_out_print("SHOW output for journal file !AD", TRUE, jctl->jnl_fn_len, jctl->jnl_fn); \
|
|
util_out_print("-------------------------------------------------------------------------------", TRUE); \
|
|
}
|
|
|
|
#define ZERO_TIME_LITERAL " 0"
|
|
|
|
#if defined(VMS)
|
|
/* Headings and FAO specs, etc. */
|
|
static const char proc_header[] =
|
|
"PID NODE USER TERM JPV_TIME PNAME IMGCNT MODE LOGIN_TIME ";
|
|
static const char proc_fao[] =
|
|
"!XL !8AD !12AD !8AD !20AD !15AD !XL !5AD !20AD";
|
|
|
|
#define TIME_DISPLAY_FAO "!20AD"
|
|
|
|
/* Convert a time value to a string in the TIME_FORMAT_STRING's format. this routine currently does not handle $h printing */
|
|
int format_time(jnl_proc_time proc_time, char *string, int string_len, int time_format)
|
|
{
|
|
jnl_proc_time long_time;
|
|
|
|
if (SHORT_TIME_FORMAT == time_format)
|
|
JNL_WHOLE_FROM_SHORT_TIME(long_time, proc_time);
|
|
else
|
|
long_time = proc_time;
|
|
GET_LONG_TIME_STR(long_time, string, string_len);
|
|
assert(LENGTH_OF_TIME >= strlen(string));
|
|
return strlen(string);
|
|
}
|
|
|
|
static void mur_show_jpv(jnl_process_vector *pv, boolean_t print_header)
|
|
{
|
|
int jpv_time_len, node_len, user_len, term_len, proc_len, login_time_len;
|
|
char *mode_str, login_time_str[LENGTH_OF_TIME + 1], jpv_time_str[LENGTH_OF_TIME + 1];
|
|
|
|
jpv_time_len = format_time(pv->jpv_time, jpv_time_str, SIZEOF(jpv_time_str), LONG_TIME_FORMAT);
|
|
login_time_len = format_time(pv->jpv_login_time, login_time_str, SIZEOF(login_time_str), LONG_TIME_FORMAT);
|
|
node_len = real_len(JPV_LEN_NODE, (uchar_ptr_t)pv->jpv_node);
|
|
user_len = real_len(JPV_LEN_USER, (uchar_ptr_t)pv->jpv_user);
|
|
proc_len = real_len(JPV_LEN_PRCNAM, (uchar_ptr_t)pv->jpv_prcnam);
|
|
term_len = real_len(JPV_LEN_TERMINAL, (uchar_ptr_t)pv->jpv_terminal);
|
|
switch (pv->jpv_mode)
|
|
{
|
|
case JPI$K_DETACHED: mode_str = "Detch"; break;
|
|
case JPI$K_NETWORK: mode_str = "Netwk"; break;
|
|
case JPI$K_BATCH: mode_str = "Batch"; break;
|
|
case JPI$K_LOCAL: mode_str = "Local"; break;
|
|
case JPI$K_DIALUP: mode_str = "Dialu"; break;
|
|
case JPI$K_REMOTE: mode_str = "Remot"; break;
|
|
default: mode_str = "UNKWN";
|
|
}
|
|
if (print_header)
|
|
{
|
|
util_out_print(proc_header, TRUE);
|
|
util_out_print(dashes_fao, TRUE, SIZEOF(proc_header) - 1);
|
|
}
|
|
util_out_print(proc_fao, TRUE, pv->jpv_pid, node_len, pv->jpv_node, user_len, pv->jpv_user,
|
|
term_len, pv->jpv_terminal, jpv_time_len, jpv_time_str,
|
|
proc_len, pv->jpv_prcnam, pv->jpv_image_count, 5, mode_str, login_time_len, login_time_str);
|
|
}
|
|
|
|
#elif defined(UNIX)
|
|
|
|
static const char proc_header[] =
|
|
"PID NODE USER TERM JPV_TIME ";
|
|
static const char proc_fao[] =
|
|
"!10ZL !12AD !8AD !4AD !38AD";
|
|
|
|
#define TIME_DISPLAY_FAO " !19AD"
|
|
|
|
/* Convert a time value to a string in the TIME_FORMAT_STRING's format */
|
|
int format_time(jnl_proc_time proc_time, char *string, int string_len, int time_format)
|
|
{
|
|
time_t short_time, seconds;
|
|
struct tm *tsp;
|
|
uint4 days;
|
|
int len;
|
|
|
|
if (LONG_TIME_FORMAT == time_format)
|
|
short_time = MID_TIME(proc_time);
|
|
else
|
|
short_time = (time_t)proc_time;
|
|
tsp = localtime((const time_t *)&short_time);
|
|
SPRINTF(string, "%04d/%02d/%02d %02d:%02d:%02d", (1900 + tsp->tm_year), (1 + tsp->tm_mon), tsp->tm_mday,
|
|
tsp->tm_hour, tsp->tm_min, tsp->tm_sec);
|
|
assert(LENGTH_OF_TIME >= strlen(string));
|
|
return STRLEN(string);
|
|
}
|
|
|
|
static void mur_show_jpv(jnl_process_vector *pv, boolean_t print_header)
|
|
{
|
|
int jpv_time_len, node_len, user_len, term_len;
|
|
char jpv_time_str[LENGTH_OF_TIME + 1];
|
|
|
|
jpv_time_len = format_time(pv->jpv_time, jpv_time_str, SIZEOF(jpv_time_str), LONG_TIME_FORMAT);
|
|
node_len = real_len(JPV_LEN_NODE, (uchar_ptr_t)pv->jpv_node);
|
|
user_len = real_len(JPV_LEN_USER, (uchar_ptr_t)pv->jpv_user);
|
|
term_len = real_len(JPV_LEN_TERMINAL, (uchar_ptr_t)pv->jpv_terminal);
|
|
if (print_header)
|
|
{
|
|
util_out_print((caddr_t)proc_header, TRUE);
|
|
util_out_print((caddr_t)dashes_fao, TRUE, SIZEOF(proc_header) - 1);
|
|
}
|
|
util_out_print((caddr_t)proc_fao, TRUE, pv->jpv_pid, node_len, pv->jpv_node, user_len, pv->jpv_user,
|
|
term_len, pv->jpv_terminal, jpv_time_len, jpv_time_str);
|
|
}
|
|
|
|
#endif
|
|
|
|
void mur_show_header(jnl_ctl_list * jctl)
|
|
{
|
|
jnl_file_header *hdr;
|
|
int time_len, idx;
|
|
char time_str[LENGTH_OF_TIME + 1];
|
|
char outbuf[GTMCRYPT_HASH_HEX_LEN + 1];
|
|
|
|
hdr = jctl->jfh;
|
|
util_out_print("!/Journal file name !AD", TRUE, jctl->jnl_fn_len, jctl->jnl_fn);
|
|
util_out_print("Journal file label !AD", TRUE, SIZEOF(JNL_LABEL_TEXT) - 1, hdr->label);
|
|
util_out_print("Database file name !AD", TRUE, hdr->data_file_name_length, hdr->data_file_name);
|
|
util_out_print(" Prev journal file name !AD", TRUE, hdr->prev_jnl_file_name_length, hdr->prev_jnl_file_name);
|
|
util_out_print(" Next journal file name !AD", TRUE, hdr->next_jnl_file_name_length, hdr->next_jnl_file_name);
|
|
if (hdr->before_images)
|
|
util_out_print("!/ Before-image journal ENABLED", TRUE);
|
|
else
|
|
util_out_print("!/ Before-image journal DISABLED", TRUE);
|
|
util_out_print(" Journal file header size !8UL [0x!XL]", TRUE, DOUBLE_ARG(JNL_HDR_LEN));
|
|
util_out_print(" Virtual file size !8UL [0x!XL] blocks", TRUE, DOUBLE_ARG(hdr->virtual_size));
|
|
util_out_print(" Journal file checksum seed !10UL [0x!XL]", TRUE, DOUBLE_ARG(hdr->checksum));
|
|
util_out_print(" Crash !AD", TRUE, 5, (hdr->crash ? " TRUE" : "FALSE"));
|
|
util_out_print(" Recover interrupted !AD", TRUE, 5, (hdr->recover_interrupted ? " TRUE" : "FALSE"));
|
|
/* Since we are defining GTM_CRYPT only for IA64, x86_64, i386, AIX and Solaris, the below dump might not happen
|
|
* for VMS, Tru64, Solaris 32 and other encryption-unsupported platforms. So, do the display unconditionally. */
|
|
util_out_print(" Journal file encrypted !AD", TRUE, 5, (hdr->is_encrypted ? " TRUE" : "FALSE"));
|
|
GET_HASH_IN_HEX(hdr->encryption_hash, outbuf, GTMCRYPT_HASH_HEX_LEN);
|
|
util_out_print(" Journal file hash !AD", TRUE, GTMCRYPT_HASH_HEX_LEN, outbuf);
|
|
util_out_print(" Blocks to Upgrade Adjustment !10UL [0x!XL]", TRUE,
|
|
DOUBLE_ARG(hdr->prev_recov_blks_to_upgrd_adjust));
|
|
util_out_print(" End of Data !10UL [0x!XL]", TRUE, DOUBLE_ARG(hdr->end_of_data));
|
|
util_out_print(" Prev Recovery End of Data !10UL [0x!XL]", TRUE, DOUBLE_ARG(hdr->prev_recov_end_of_data));
|
|
util_out_print(" Endian Format !AD", TRUE, STR_LIT_LEN(ENDIANTHISJUSTIFY), ENDIANTHISJUSTIFY);
|
|
time_len = format_time(hdr->bov_timestamp, time_str, SIZEOF(time_str), SHORT_TIME_FORMAT);
|
|
util_out_print(" Journal Creation Time "TIME_DISPLAY_FAO, TRUE, time_len, time_str);
|
|
time_len = format_time(hdr->eov_timestamp, time_str, SIZEOF(time_str), SHORT_TIME_FORMAT);
|
|
util_out_print(" Time of last update "TIME_DISPLAY_FAO, TRUE, time_len, time_str);
|
|
util_out_print(" Begin Transaction !20@UQ [0x!16@XQ]", TRUE, DOUBLE_ARG(&hdr->bov_tn));
|
|
util_out_print(" End Transaction !20@UQ [0x!16@XQ]", TRUE, DOUBLE_ARG(&hdr->eov_tn));
|
|
util_out_print(" Align size !16UL [0x!XL] bytes", TRUE, DOUBLE_ARG(hdr->alignsize));
|
|
util_out_print(" Epoch Interval !8UL", TRUE, EPOCH_SECOND2SECOND(hdr->epoch_interval));
|
|
assert(!REPL_WAS_ENABLED(hdr));
|
|
util_out_print(" Replication State !8AD", TRUE, 8,
|
|
(hdr->repl_state == repl_closed ? " CLOSED" : (hdr->repl_state == repl_open ? " OPEN" : "WAS_OPEN")));
|
|
#ifdef VMS
|
|
util_out_print(" Updates Disabled on Secondary !AD", TRUE, 5, (hdr->update_disabled ? " TRUE" : "FALSE"));
|
|
#endif
|
|
util_out_print(" Jnlfile SwitchLimit !16UL [0x!XL] blocks", TRUE, DOUBLE_ARG(hdr->autoswitchlimit));
|
|
util_out_print(" Jnlfile Allocation !16UL [0x!XL] blocks", TRUE, DOUBLE_ARG(hdr->jnl_alq));
|
|
util_out_print(" Jnlfile Extension !16UL [0x!XL] blocks", TRUE, DOUBLE_ARG(hdr->jnl_deq));
|
|
util_out_print(" Maximum Journal Record Length !16UL [0x!XL]", TRUE, DOUBLE_ARG(hdr->max_jrec_len));
|
|
util_out_print(" Turn Around Point Offset !10UL [0x!XL]", TRUE, DOUBLE_ARG(hdr->turn_around_offset));
|
|
if (hdr->turn_around_time)
|
|
time_len = format_time(hdr->turn_around_time, time_str, SIZEOF(time_str), SHORT_TIME_FORMAT);
|
|
else
|
|
{
|
|
time_len = STR_LIT_LEN(ZERO_TIME_LITERAL);
|
|
MEMCPY_LIT(time_str, ZERO_TIME_LITERAL);
|
|
}
|
|
util_out_print(" Turn Around Point Time !20AD", TRUE, time_len, time_str);
|
|
util_out_print(" Start Region Sequence Number !20@UQ [0x!16@XQ]", TRUE, &hdr->start_seqno, &hdr->start_seqno);
|
|
util_out_print(" End Region Sequence Number !20@UQ [0x!16@XQ]", TRUE, &hdr->end_seqno, &hdr->end_seqno);
|
|
/* Dump stream seqnos for upto 16 streams if any are non-zero.
|
|
*/
|
|
for (idx = 0; idx < MAX_SUPPL_STRMS; idx++)
|
|
{ /* Dump stream seqnos. Dont dump them unconditionally as they will swamp the output.
|
|
* We usually expect 1 or 2 streams to have non-zero values so dump it only if non-zero.
|
|
* Note that in case the journal file is created for the first time as part of a supplementary instance,
|
|
* the stream seqno would be 0 so the start_seqno in the jnl file header could be 0 whereas the end seqno
|
|
* could be non-zero. In that case, use the end-seqno to determine if the stream seqno needs to be
|
|
* dumped or not. In pro be safe and use both values and dump both if one of them is non-zero.
|
|
*/
|
|
assert(!hdr->strm_start_seqno[idx] || hdr->strm_end_seqno[idx]);
|
|
if (hdr->strm_start_seqno[idx] || hdr->strm_end_seqno[idx])
|
|
{
|
|
VMS_ONLY(assert(FALSE);) /* we expect this field to be unused in VMS */
|
|
util_out_print(" Stream !2UL : Start RegSeqno !20@UQ [0x!16@XQ]", TRUE,
|
|
idx, &hdr->strm_start_seqno[idx], &hdr->strm_start_seqno[idx]);
|
|
util_out_print(" Stream !2UL : End RegSeqno !20@UQ [0x!16@XQ]", TRUE,
|
|
idx, &hdr->strm_end_seqno[idx], &hdr->strm_end_seqno[idx]);
|
|
}
|
|
}
|
|
util_out_print("!/Process That Created the Journal File:!/", TRUE);
|
|
mur_show_jpv(&hdr->who_created, TRUE);
|
|
util_out_print("!/Process That Last Wrote to the Journal File:!/", TRUE);
|
|
mur_show_jpv(&hdr->who_opened, TRUE);
|
|
util_out_print("", TRUE);
|
|
}
|
|
|
|
void mur_output_show()
|
|
{
|
|
reg_ctl_list *rctl, *rctl_top;
|
|
jnl_ctl_list *jctl;
|
|
int rectype, size;
|
|
pini_list_struct *plst;
|
|
ht_ent_int4 *tabent, *topent;
|
|
boolean_t first_time;
|
|
|
|
assert(mur_options.show);
|
|
for (rctl = mur_ctl, rctl_top = mur_ctl + murgbl.reg_total; rctl < rctl_top; rctl++)
|
|
{
|
|
jctl = (NULL == rctl->jctl_turn_around) ? rctl->jctl_head : rctl->jctl_turn_around;
|
|
while (jctl)
|
|
{
|
|
if (CLI_PRESENT == cli_present("SHOW"))
|
|
PRINT_SHOW_HEADER(jctl); /* print show-header unconditionally only if SHOW was specified */
|
|
if (mur_options.show & SHOW_HEADER)
|
|
{
|
|
assert(CLI_PRESENT == cli_present("SHOW"));
|
|
mur_show_header(jctl);
|
|
}
|
|
size = jctl->pini_list.size;
|
|
if (mur_options.show & SHOW_BROKEN
|
|
|| mur_options.show & SHOW_ACTIVE_PROCESSES
|
|
|| mur_options.show & SHOW_ALL_PROCESSES)
|
|
{
|
|
first_time = TRUE;
|
|
for (tabent = jctl->pini_list.base, topent = jctl->pini_list.top; tabent < topent; tabent++)
|
|
{
|
|
if (HTENT_VALID_INT4(tabent, pini_list_struct, plst))
|
|
{
|
|
if (BROKEN_PROC == plst->state)
|
|
{
|
|
if (first_time)
|
|
{ /* print show-header in case SHOW=BROKEN was not explicitly
|
|
* specified but implicitly assumed due to mur_options.update
|
|
*/
|
|
if (CLI_PRESENT != cli_present("SHOW"))
|
|
PRINT_SHOW_HEADER(jctl);
|
|
util_out_print("!/Process(es) with BROKEN transactions in this "
|
|
"journal:!/", TRUE);
|
|
}
|
|
mur_show_jpv(&plst->jpv, first_time);
|
|
first_time = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (mur_options.show & SHOW_ACTIVE_PROCESSES
|
|
|| mur_options.show & SHOW_ALL_PROCESSES)
|
|
{
|
|
assert(CLI_PRESENT == cli_present("SHOW"));
|
|
first_time = TRUE;
|
|
for (tabent = jctl->pini_list.base, topent = jctl->pini_list.top; tabent < topent; tabent++)
|
|
{
|
|
if (HTENT_VALID_INT4(tabent, pini_list_struct, plst))
|
|
{
|
|
if (ACTIVE_PROC == plst->state)
|
|
{
|
|
if (first_time)
|
|
util_out_print("!/Process(es) that are still ACTIVE in this "
|
|
"journal:!/", TRUE);
|
|
mur_show_jpv(&plst->jpv, first_time);
|
|
first_time = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (mur_options.show & SHOW_ALL_PROCESSES)
|
|
{
|
|
assert(CLI_PRESENT == cli_present("SHOW"));
|
|
first_time = TRUE;
|
|
for (tabent = jctl->pini_list.base, topent = jctl->pini_list.top; tabent < topent; tabent++)
|
|
{
|
|
if (HTENT_VALID_INT4(tabent, pini_list_struct, plst))
|
|
{
|
|
if (FINISHED_PROC == plst->state)
|
|
{
|
|
if (first_time)
|
|
util_out_print("!/Process(es) that are COMPLETE in this journal:!/",
|
|
TRUE);
|
|
mur_show_jpv(&plst->jpv, first_time);
|
|
first_time = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (mur_options.show & SHOW_STATISTICS)
|
|
{
|
|
assert(CLI_PRESENT == cli_present("SHOW"));
|
|
util_out_print((caddr_t)statistics_header, TRUE);
|
|
util_out_print((caddr_t)dashes_fao, TRUE, STR_LIT_LEN(statistics_header));
|
|
for (rectype = JRT_BAD; rectype < JRT_RECTYPES; ++rectype)
|
|
{
|
|
if ((JRT_TRIPLE == rectype) || (JRT_HISTREC == rectype))
|
|
{
|
|
assert(0 == jctl->jnlrec_cnt[rectype]);
|
|
continue;
|
|
}
|
|
util_out_print((caddr_t)statistics_fao, TRUE,
|
|
jrt_label[rectype], jctl->jnlrec_cnt[rectype]);
|
|
}
|
|
}
|
|
jctl = jctl->next_gen;
|
|
if ((CLI_PRESENT == cli_present("SHOW")) || !first_time)
|
|
util_out_print("", TRUE);
|
|
}
|
|
}
|
|
}
|