File: gtm_exit_handler.c

package info (click to toggle)
fis-gtm 7.1-006-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 32,908 kB
  • sloc: ansic: 344,906; asm: 5,184; csh: 4,859; sh: 2,000; awk: 294; makefile: 73; sed: 13
file content (221 lines) | stat: -rwxr-xr-x 7,830 bytes parent folder | download | duplicates (2)
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);
}