1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
/****************************************************************
* *
* Copyright (c) 2001-2023 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_unistd.h"
#include "gtm_inet.h"
#include "gtm_signal.h"
#include <sys/shm.h>
#include "gdsroot.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "io.h"
#include "iosp.h"
#include "iotimer.h"
#include "error.h"
#include "gtm_stdio.h"
#include "repl_msg.h"
#include "gtmsource.h"
#include "gt_timer.h"
#include "mutex.h"
#include "fgncalsp.h"
#include "zcall_package.h"
#include "gtm_exit_handler.h"
#include "gv_rundown.h"
#include "mprof.h"
#include "print_exit_stats.h"
#include "invocation_mode.h"
#include "secshr_db_clnup.h"
#include "gtmcrypt.h"
#include "relinkctl.h"
#include "gvcst_protos.h"
#include "op.h"
GBLREF int4 exi_condition;
GBLREF uint4 dollar_tlevel;
GBLREF boolean_t need_core; /* Core file should be created */
GBLREF boolean_t created_core; /* Core file was created */
GBLREF unsigned int core_in_progress;
GBLREF boolean_t dont_want_core;
GBLREF boolean_t exit_handler_active;
GBLREF volatile int4 fast_lock_count;
GBLREF boolean_t skip_exit_handler;
GBLREF boolean_t is_tracing_on;
GBLREF uint4 process_id;
#ifdef DEBUG
GBLREF boolean_t stringpool_unusable;
GBLREF boolean_t stringpool_unexpandable;
#endif
enum rundown_state
{
rundown_state_lock,
rundown_state_mprof,
rundown_state_statsdb,
rundown_state_db,
rundown_state_io,
rundown_state_last
};
static enum rundown_state attempting;
#ifdef DEBUG
GBLREF int process_exiting;
#endif
LITREF mval literal_notimeout;
/* This macro is a framework to help perform ONE type of rundown (e.g. db or lock or io rundown etc.).
* "gtm_exit_handler" invokes this macro for each type of rundown that is needed and passes appropriate
* parameters to indicate the detail needed for each rundown.
* Note: This macro uses local variables "attempting", "error_seen" and "actual_exi_condition".
*/
#define RUNDOWN_STEP(THISSTATE, NEXTSTATE, ERRCODE, STMT) \
{ \
if (THISSTATE == attempting) \
{ \
if (!error_seen) \
{ \
STMT; \
} else \
{ \
if (!actual_exi_condition) \
actual_exi_condition = exi_condition; \
if (0 != ERRCODE) \
{ \
PRN_ERROR; \
dec_err(VARLSTCNT(1) ERRCODE); \
} \
} \
error_seen = FALSE; \
attempting++; \
} \
assert(NEXTSTATE == (THISSTATE + 1)); \
}
#define MPROF_RUNDOWN_MACRO \
{ \
if (is_tracing_on) \
turn_tracing_off(NULL); \
}
#define LOCK_RUNDOWN_MACRO \
{ \
SET_PROCESS_EXITING_TRUE; \
CANCEL_TIMERS; /* Cancel all unsafe timers - No unpleasant surprises */ \
/* Note we call secshr_db_clnup() with the flag NORMAL_TERMINATION even in an error condition \
* here because we know at this point that we aren't in the middle of a transaction commit but \
* crit may be held in one or more regions and/or other odds/ends to cleanup. \
*/ \
secshr_db_clnup(NORMAL_TERMINATION); \
zcall_halt(); \
op_unlock(); \
op_zdeallocate((mval *)&literal_notimeout); \
}
#define IO_RUNDOWN_MACRO \
{ \
/* Invoke cleanup routines for all the shared libraries loaded during external call initialisation. \
* The cleanup routines are not mandatory routines, but if defined, will be invoked before \
* closing the shared library. \
*/ \
for (package_ptr = TREF(extcall_package_root); package_ptr; package_ptr = package_ptr->next_package) \
{ \
if (package_ptr->package_clnup_rtn) \
package_ptr->package_clnup_rtn(); \
fgn_closepak(package_ptr->package_handle, INFO); \
} \
relinkctl_rundown(TRUE, TRUE); /* decrement relinkctl-attach & rtnobj-reference counts */ \
assert(process_exiting); \
if (MUMPS_CALLIN & invocation_mode) \
{ \
flush_pio(); \
io_rundown(RUNDOWN_EXCEPT_STD); \
} else \
io_rundown(NORMAL_RUNDOWN); \
GTMCRYPT_CLOSE; \
}
error_def(ERR_GVRUNDOWN);
error_def(ERR_LKRUNDOWN);
error_def(ERR_MPROFRUNDOWN);
error_def(ERR_PIDMISMATCH);
/* Function that is invoked at process exit time to do cleanup.
* The general flow here is to do various types of rundowns (e.g. db rundown, lock rundown, io rundown etc.).
* If one type of rundown encounters an error midway, we want to just move on to the next type of rundown.
* This way we do as much cleanup as possible before the process exists. Towards this, we use "exi_ch" as a
* condition handler and the RUNDOWN_STEP macro to take care of each type of rundown.
*/
void gtm_exit_handler(void)
{
struct sigaction act;
struct extcall_package_list *package_ptr;
boolean_t error_seen;
int4 actual_exi_condition;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
if (exit_handler_active || skip_exit_handler) /* Skip exit handling if specified or if exit handler already active */
return;
if (process_id != getpid())
{ /* DE476408 - Skip exit handling when there is a process_id mismatch(after FORK) to avoid a child
* process from removing the statsdb entry(gvcst_remove_statsDB_linkage) of its parent, which might
* cause database damage.
*/
SHORT_SLEEP(100);
if (process_id != getpid())
{ /* gtm8518 - a retry in order to make sure the mismatch is consistant before avoiding rundowns */
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_PIDMISMATCH, 2, process_id, getpid());
return;
}
}
exit_handler_active = TRUE;
attempting = rundown_state_lock;
actual_exi_condition = 0;
ESTABLISH_NORET(exi_ch, error_seen); /* "error_seen" is initialized inside this macro */
#ifdef DEBUG
if (WBTEST_ENABLED(WBTEST_CRASH_SHUTDOWN_EXPECTED) && (NO_STATS_OPTIN != TREF(statshare_opted_in)))
{ /* Forced to FALSE when killing processes and we may need to rundown statsdbs */
stringpool_unusable = FALSE;
stringpool_unexpandable = FALSE;
fast_lock_count = 0;
}
#endif
RUNDOWN_STEP(rundown_state_lock, rundown_state_mprof, ERR_LKRUNDOWN, LOCK_RUNDOWN_MACRO);
RUNDOWN_STEP(rundown_state_mprof, rundown_state_statsdb, ERR_MPROFRUNDOWN, MPROF_RUNDOWN_MACRO);
/* The condition handler used in the gvcst_remove_statsDB_linkage_all() path takes care of sending errors */
RUNDOWN_STEP(rundown_state_statsdb, rundown_state_db, 0, gvcst_remove_statsDB_linkage_all());
RUNDOWN_STEP(rundown_state_db, rundown_state_io, ERR_GVRUNDOWN, gv_rundown());
/* We pass 0 (not ERR_IORUNDOWN) below to avoid displaying any error if io_rundown fails. One reason we have
* seen an external filter M program fail is with a "SYSTEM-E-ENO32, Broken pipe" error if the source or receiver
* server (that is communicating with it through a pipe device) closes its end of the pipe and we do not want that
* to be treated as an error in rundown (it is how a pipe close happens normally).
*/
RUNDOWN_STEP(rundown_state_io, rundown_state_last, 0, IO_RUNDOWN_MACRO);
REVERT;
print_exit_stats();
if (need_core && !created_core && !dont_want_core) /* We needed to core */
{
++core_in_progress;
DUMP_CORE; /* This will not return */
}
if (actual_exi_condition && !(MUMPS_CALLIN & invocation_mode))
PROCDIE(actual_exi_condition);
}
|