summaryrefslogtreecommitdiff
path: root/src/tet3/tcc/sigtrap.c
blob: 7ebd0b0da1dbd62167ca1360b00068bd76ac00a9 (plain)
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
/*
 *	SCCS: @(#)sigtrap.c	1.5 (98/09/01)
 *
 *	UniSoft Ltd., London, England
 *
 * (C) Copyright 1996 X/Open Company Limited
 *
 * All rights reserved.  No part of this source code may be reproduced,
 * stored in a retrieval system, or transmitted, in any form or by any
 * means, electronic, mechanical, photocopying, recording or otherwise,
 * except as stated in the end-user licence agreement, without the prior
 * permission of the copyright owners.
 * A copy of the end-user licence agreement is contained in the file
 * Licence which accompanies this distribution.
 * 
 * X/Open and the 'X' symbol are trademarks of X/Open Company Limited in
 * the UK and other countries.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/************************************************************************

SCCS:   	@(#)sigtrap.c	1.5 98/09/01 TETware release 3.3
NAME:		sigtrap.c
PRODUCT:	TETware
AUTHOR:		Andrew Dingwall, UniSoft Ltd.
DATE CREATED:	September 1996

DESCRIPTION:
	tcc signal handling functions

MODIFICATIONS:
	Andrew Dingwall, UniSoft Ltd., May 1997
	corrected a bug in the WIN32 signal blocking emulation

	Andrew Dingwall, UniSoft Ltd., March 1998
	Arrange to interrupt looping directives on abort.


************************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#  include <unistd.h>
#include "dtmac.h"
#include "error.h"
#include "ltoa.h"
#include "scentab.h"
#include "proctab.h"
#include "tcc.h"
#include "tcclib.h"

/* signal dispositions on startup */
static void (*orig_sighup) PROTOLIST((int));
static void (*orig_sigquit) PROTOLIST((int));
static void (*orig_sigpipe) PROTOLIST((int));
static void (*orig_sigterm) PROTOLIST((int));

/* variables used by the WIN32 sigblock emulation */


/* static function declarations */
static int eng1_tcinterrupt PROTOLIST((struct proctab *));
static void engine_abort PROTOLIST((int));
static int engine_tcinterrupt PROTOLIST((int));
static void initial_sigtrap PROTOLIST((int));
static void (*install_handler PROTOLIST((int, void (*) PROTOLIST((int)))))
	PROTOLIST((int));
static int quick_killtc PROTOLIST((struct proctab *));
static void engine_sigterm PROTOLIST((int));
static void exec_sigprocmask PROTOLIST((int));
#  ifdef TET_LITE	/* -LITE-CUT-LINE */
static void tes2 PROTOLIST((int, void (*) PROTOLIST((int))));
#  endif /* TET_LITE */	/* -LITE-CUT-LINE */


/*
**	initsigtrap() - install initial signal traps
*/

void initsigtrap()
{
	orig_sighup = install_handler(SIGHUP, initial_sigtrap);
	orig_sigquit = install_handler(SIGQUIT, initial_sigtrap);
	orig_sigpipe = install_handler(SIGPIPE, initial_sigtrap);
	orig_sigterm = install_handler(SIGTERM, initial_sigtrap);
}

/*
**	initial_sigtrap() - signal handler for use before the execution
**		engine starts up
*/

static void initial_sigtrap(sig)
int sig;
{
	static char text[] = "TCC shutdown on signal";


	if (jnl_usable())
		(void) fprintf(stderr, "%s %d\n", text, sig);

	fatal(0, text, tet_i2a(sig));
}

/*
**	execsigtrap() - install signal traps for the execution engine
*/

void execsigtrap()
{
	exec_block_signals();
	(void) install_handler(SIGHUP, engine_sigterm);
	(void) install_handler(SIGQUIT, engine_abort);
	(void) install_handler(SIGPIPE, engine_sigterm);
	(void) install_handler(SIGTERM, engine_sigterm);
}

/*
**	exec_block_signals() - block signals while the execution engine
**		turns over
*/

void exec_block_signals()
{
	exec_sigprocmask(SIG_BLOCK);
}

void exec_unblock_signals()
{
	exec_sigprocmask(SIG_UNBLOCK);
}




/*
**	exec_sigprocmask() - block or unblock signals
*/

static void exec_sigprocmask(how)
int how;
{
	sigset_t mask;

	(void) sigemptyset(&mask);
	(void) sigaddset(&mask, SIGHUP);
	(void) sigaddset(&mask, SIGQUIT);
	(void) sigaddset(&mask, SIGPIPE);
	(void) sigaddset(&mask, SIGTERM);

	if (sigprocmask(how, &mask, (sigset_t *) 0) < 0)
		fatal(errno, "sigprocmask() failed: how =", tet_i2a(how));
}




/*
**	engine_sigterm() - SIGHUP and SIGTERM signal handler for use once
**		the execution engine is running
*/

static void engine_sigterm(sig)
int sig;
{
	TRACE2(TET_MAX(tet_Ttcc, tet_Texec), 4, "engine_sigterm(): signal = %s",
		tet_i2a(sig));

	initial_sigtrap(sig);
}



/*
**	engine_abort() - SIGQUIT (or SIGBREAK) signal handler for use once
**		the execution engine is running
*/

static void engine_abort(sig)
int sig;
{
	register struct proctab *prp;


	(void) fprintf(stderr, "TCC: user abort called\n");
	(void) fflush(stderr);

	/*
	** this flag tells the execution engine not to start processing
	** any more test cases
	*/
	tcc_modes |= TCC_ABORT;

	/* arrange to interrupt each directive on the run queue */
	for (prp = runq; prp; prp = prp->pr_rqforw)
		if (prp->pr_scen->sc_type == SC_DIRECTIVE)
			prp->pr_modes |= TCC_ABORT;

	/*
	** tell the execution engine to interrupt all the currently
	** running test cases; the combination of these two actions
	** causes tcc to exit normally as soon as any currently running
	** test cases have terminated and any save files and journal
	** processing has completed
	*/
	(void) engine_tcinterrupt(sig);

}

/*
**	engine_tcinterrupt() - tell the execution engine to interrupt all the
**		currently running test case(s)
**
**	return the number of test cases currently running
**
**	this function is called from a signal handler, which itself
**	should only be called while the execution engine is turning over
**
**	note that the test cases are not actually interrupted until
**	control returns to the execution engine
*/

#ifdef NOTRACE
/* ARGSUSED */
#endif
static int engine_tcinterrupt(sig)
int sig;
{
	register struct proctab *prp;
	register int count = 0;

	TRACE2(TET_MAX(tet_Ttcc, tet_Texec), 4,
		"engine_tcinterrupt(): signal = %s", tet_i2a(sig));

	/* arrange to interrupt each testcase on the run queue */
	for (prp = runq; prp; prp = prp->pr_rqforw)
		if (prp->pr_scen->sc_type == SC_TESTCASE) {
			prp->pr_modes |= TCC_ABORT;
			if (prp->pr_state == PRS_WAIT) {
				count++;
				(void) RUN_PROCTABS(prp, eng1_tcinterrupt);
				prp->pr_flags |= PRF_ATTENTION;
			}
		}

	return(count);
}

/*
**	eng1_tcinterrupt() - extend the engine_tcinterrupt() processing for a
**		testcase or tool running on a single system
**
**	always returns 0
*/

static int eng1_tcinterrupt(prp)
register struct proctab *prp;
{
	TRACE3(TET_MAX(tet_Ttcc, tet_Texec), 6,
		"eng1_tcinterrupt(%s): toolstate = %s",
		tet_i2x(prp), prtoolstate(prp->pr_toolstate));

	if (prp->pr_toolstate == PTS_RUNNING)
		prp->pr_toolstate = PTS_ABORT;

	return(0);
}

/*
**	engine_shutdown() - kill left-over test cases and remove lock
**		files when tcc is shutting down
**
**	this function is called during tcc's orderly shutdown processing;
**	control should not return to the execution engine after this
**	function has been called
**
**	when this function is called, there should only be left-over
**	test cases and lock files if the shutdown is being performed
**	under the control of a signal handler (eg: for SIGHUP or SIGTERM)
**
**	since control is not returned to the execution engine, any pending
**	save files and journal processing is not performed;
**	also, test cases are not waited for - if a test case does not
**	respond to SIGTERM, it is left running
**	(however, in fully-featured TETware, when tcc logs off each tccd,
**	tccd sends a SIGHUP to any left-over executed processes)
*/

void engine_shutdown()
{
	register struct proctab *prp;
	register int count = 0;
	static int been_here = 0;

	TRACE3(TET_MAX(tet_Ttcc, tet_Texec), 4,
		"engine_shutdown(): been_here = %s, runq = %s",
		tet_i2a(been_here), tet_i2x(runq));

	/* guard against multiple calls and recursive calls */
	if (been_here++) {
		TRACE1(TET_MAX(tet_Ttcc, tet_Texec), 4,
			"engine_shutdown() quick RETURN");
		return;
	}

	/* kill each running testcase or tool */
	for (prp = runq; prp; prp = prp->pr_rqforw)
		if (prp->pr_scen->sc_type == SC_TESTCASE) {
			count++;
			(void) RUN_PROCTABS(prp, quick_killtc);
		}

	/* give the tools a chance to terminate */
	if (count)
		SLEEP(2);

	/* remove lock files */
	for (prp = runq; prp; prp = prp->pr_rqforw)
		if (prp->pr_scen->sc_type == SC_TESTCASE)
			switch (prp->pr_currmode) {
			case TCC_BUILD:
			case TCC_EXEC:
			case TCC_CLEAN:
				prp->pr_tcstate = TCS_END;
				proc_testcase(prp);
				break;
			}

	TRACE1(TET_MAX(tet_Ttcc, tet_Texec), 4,
		"engine_shutdown() normal RETURN");
}

/*
**	quick_killtc() - kill a test case or tool quickly on a single system
**		without waiting for the tool to terminate or checking for
**		errors
**
**	always returns 0
*/

static int quick_killtc(prp)
struct proctab *prp;
{
	TRACE3(TET_MAX(tet_Ttcc, tet_Texec), 6,
		"quick_killtc(%s): toolstate = %s",
		tet_i2x(prp), prtoolstate(prp->pr_toolstate));

	if (prp->pr_toolstate == PTS_RUNNING) {
		(void) tcc_kill(*prp->pr_sys, prp->pr_remid, SIGTERM);
		prp->pr_toolstate = PTS_EXITED;
	}

	return(0);
}

/*
**	install_handler() - install a signal handler
*/

static void (*install_handler(sig, func))()
int sig;
void (*func) PROTOLIST((int));
{
	void (*rc) PROTOLIST((int));


	struct sigaction sa;

	if (sigaction(sig, (struct sigaction *) 0, &sa) < 0)
		fatal(errno, "can't get disposition for signal", tet_i2a(sig));

	if ((rc = sa.sa_handler) != SIG_IGN) {
		sa.sa_handler = func;
		sa.sa_flags = 0;
		(void) sigemptyset(&sa.sa_mask); 
		if (sigaction(sig, &sa, (struct sigaction *) 0) < 0)
			fatal(errno, "can't install handler for signal",
				tet_i2a(sig));
	}


	return(rc);
}


#  ifdef TET_LITE	/* -LITE-CUT-LINE- */

/*
**	tcc_exec_signals() - restore original signal dispositions
**		in the child process before an exec
**
**	this function is called from the tcclib function tcf_exec()
*/

void tcc_exec_signals()
{
	tes2(SIGHUP, orig_sighup);
	tes2(SIGQUIT, orig_sigquit);
	tes2(SIGPIPE, orig_sigpipe);
	tes2(SIGTERM, orig_sigterm);
}

/*
**	tes2() - extend the tcc_exec_signals() processing for a
**		single signal
*/
static void tes2(sig, func)
int sig;
void (*func) PROTOLIST((int));
{
	struct sigaction sa;

	/* ignore the signal */
	sa.sa_handler = SIG_IGN;
	sa.sa_flags = 0;
	(void) sigemptyset(&sa.sa_mask); 
	if (sigaction(sig, &sa, (struct sigaction *) 0) < 0)
		fatal(errno, "sigaction(SIG_IGN) failed for signal",
			tet_i2a(sig));

	/* unblock the signal */
	(void) sigemptyset(&sa.sa_mask);
	(void) sigaddset(&sa.sa_mask, sig);
	if (sigprocmask(SIG_UNBLOCK, &sa.sa_mask, (sigset_t *) 0) < 0)
		fatal(errno, "sigprocmask(SIG_UNBLOCK) failed for signal",
			tet_i2a(sig));

	/* restore the original signal disposition if not SIG_IGN */
	if (func != SIG_IGN) {
		sa.sa_handler = func;
		sa.sa_flags = 0;
		(void) sigemptyset(&sa.sa_mask); 
		if (sigaction(sig, &sa, (struct sigaction *) 0) < 0)
			fatal(errno, "sigaction() failed for signal",
				tet_i2a(sig));

	}
}

#  endif /* TET_LITE */	/* -LITE-CUT-LINE- */