Subversion Repositories tendra.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 7u83 1
/*
2
    		 Crown Copyright (c) 1997
3
 
4
    This TenDRA(r) Computer Program is subject to Copyright
5
    owned by the United Kingdom Secretary of State for Defence
6
    acting through the Defence Evaluation and Research Agency
7
    (DERA).  It is made available to Recipients with a
8
    royalty-free licence for its use, reproduction, transfer
9
    to other parties and amendment for any purpose not excluding
10
    product development provided that any such use et cetera
11
    shall be deemed to be acceptance of the following conditions:-
12
 
13
        (1) Its Recipients shall ensure that this Notice is
14
        reproduced upon any copies or amended versions of it;
15
 
16
        (2) Any amended version of it shall be clearly marked to
17
        show both the nature of and the organisation responsible
18
        for the relevant amendment or amendments;
19
 
20
        (3) Its onward transfer from a recipient to another
21
        party shall be deemed to be that party's acceptance of
22
        these conditions;
23
 
24
        (4) DERA gives no warranty or assurance as to its
25
        quality or suitability for any purpose and DERA accepts
26
        no liability whatsoever in relation to any use to which
27
        it may be put.
28
*/
29
 
30
 
31
#include "config.h"
32
#include "external.h"
33
#include "filename.h"
34
#include "list.h"
35
#include "archive.h"
36
#include "execute.h"
37
#include "flags.h"
38
#include "main.h"
39
#include "utility.h"
40
 
41
 
42
/*
43
    CURRENT COMMAND
44
 
45
    The current command is a variable sized array of strings, command.
46
    A complete command is terminated by a null string.
47
*/
48
 
49
static char **command = null ;
50
static int command_size = 0 ;
51
static int cmd_no = 0 ;
52
 
53
 
54
/*
55
    DELAY SIGNAL HANDLING
56
 
57
    Because the producer occasionally dies with a signal after it has
58
    output some useful errors it is benificial to run the tot even
59
    after the signal has been caught. These globals and the functions
60
    below for using them tell the execute function to delay calling the
61
    signal handler until after the tot is called.
62
*/
63
 
64
static char *last_signaled_cmd = null ;
65
static int last_signal = 0 ;
66
static int delay_signal_handling = 0 ;
67
 
68
void enable_delayed_signal
69
    PROTO_Z () 
70
{
71
    delay_signal_handling = 1 ;
72
    return ;
73
}
74
 
75
void disable_delayed_signal
76
    PROTO_Z ()
77
{
78
    delay_signal_handling = 0 ;
79
    return ;
80
}
81
 
82
void process_delayed_signal
83
    PROTO_Z ()
84
{
85
    if ( last_signal != 0 ) {
86
	last_command = last_signaled_cmd ;
87
	handler ( last_signal ) ;
88
    }
89
    return ;
90
}
91
 
92
 
93
/*
94
    ADD A STRING TO THE CURRENT COMMAND
95
 
96
    This routine adds the string s to the command array.  If s is null
97
    then the array counter is reset to the beginning.  The array counter
98
    is not advanced for empty strings.
99
*/
100
 
101
void cmd_string
102
    PROTO_N ( ( s ) )
103
    PROTO_T ( char *s )
104
{
105
    if ( cmd_no >= command_size ) {
106
	command_size += 1000 ;
107
	command = realloc_nof ( command, char *, command_size ) ;
108
    }
109
    command [ cmd_no ] = s ;
110
    if ( s == null ) {
111
	cmd_no = 0 ;
112
    } else if ( *s ) {
113
	cmd_no++ ;
114
    }
115
    return ;
116
}
117
 
118
 
119
/*
120
    ADD A FILENAME TO THE CURRENT COMMAND
121
 
122
    This routine adds the names of the files given by p to the command
123
    array.
124
*/
125
 
126
void cmd_filename
127
    PROTO_N ( ( p ) )
128
    PROTO_T ( filename *p )
129
{
130
    for ( ; p != null ; p = p->next ) cmd_string ( p->name ) ;
131
    return ;
132
}
133
 
134
 
135
/*
136
    ADD A LIST TO THE CURRENT COMMAND
137
 
138
    This routine adds the list of strings given by p to the command array.
139
*/
140
 
141
void cmd_list
142
    PROTO_N ( ( p ) )
143
    PROTO_T ( list *p )
144
{
145
    for ( ; p != null ; p = p->next ) cmd_string ( p->item ) ;
146
    return ;
147
}
148
 
149
 
150
/*
151
    OVERALL COMPILATION STATUS
152
 
153
    This flag is true is an execution error occurs.
154
*/
155
 
156
boolean exec_error = 0 ;
157
 
158
void reset_exec_error 
159
    PROTO_Z ()
160
{
161
    exec_error = 0 ;
162
    return ;
163
}
164
 
165
 
166
/*
167
    LAST COMMAND
168
 
169
    The name of the last command executed, and its return value (zero
170
    indicating success) are stored.
171
*/
172
 
173
char *last_command = null ;
174
int last_return = 0 ;
175
 
176
 
177
/*
178
    THE CURRENT PROCESS
179
 
180
    When a process is active, its pid is stored as running_pid.  The
181
    value -1 is used to indicate that no process is active.
182
*/
183
 
184
#if FS_FORK
185
static long running_pid = -1 ;
186
#endif
187
 
188
 
189
/*
190
    KILL ANY STRAY PROCESSES
191
 
192
    Occasionally a runaway process may occur.  This routine is indended
193
    to deal with these by sending the signal SIGTERM to the process.
194
    This routine is POSIX compliant.
195
*/
196
 
197
void kill_stray
198
    PROTO_Z ()
199
{
200
#if FS_FORK
201
    if ( running_pid == -1 ) return ;
202
    IGNORE kill ( ( pid_t ) running_pid, SIGTERM ) ;
203
    running_pid = -1 ;
204
#endif
205
    return ;
206
}
207
 
208
 
209
/*
210
    LIST OF FILES TO BE REMOVED BY REMOVE_JUNK
211
 
212
    This gives the list of the files which are to be removed if an
213
    error occurs.
214
*/
215
 
216
static filename *junk = null ;
217
 
218
 
219
/*
220
    REMOVE ANY INCOMPLETE OUTPUT FILES
221
 
222
    Any files which are being created when an error occurs should be
223
    removed.
224
*/
225
 
226
void remove_junk
227
    PROTO_Z ()
228
{
229
    if ( !dry_run && !flag_keep_err ) {
230
	filename *p ;
231
	for ( p = junk ; p != null ; p = p->next ) {
232
	    if ( p->storage == OUTPUT_FILE ) IGNORE remove ( p->name ) ;
233
	}
234
    }
235
    junk = null ;
236
    return ;
237
}
238
 
239
 
240
/*
241
    PRINT COMMAND INTO BUFFER
242
 
243
    This routine prints the current command into a buffer and returns
244
    a pointer to the result.
245
*/
246
 
247
static void print_cmd
248
    PROTO_N ( ( b ) )
249
    PROTO_T ( char *b )
250
{
251
    char **s ;
252
    for ( s = command ; *s != null ; s++ ) {
253
	*b = ' ' ;
254
	IGNORE strcpy ( b + 1, *s ) ;
255
	b += strlen ( b ) ;
256
    }
257
    return ;
258
}
259
 
260
 
261
/*
262
    EXECUTE THE CURRENT COMMAND
263
 
264
    This routine executes the command given by the command array.  It
265
    returns either output, the list of all output files, if successful,
266
    or null, otherwise.  The routine is POSIX compliant.  It uses fork
267
    and execv from unistd.h to fork a process and various routines from
268
    sys/wait.h to analyse the result.  The interface with sys/wait.h
269
    has been abstracted to also allow the BSD implementation.
270
*/
271
 
272
filename *execute
273
    PROTO_N ( ( input, output ) )
274
    PROTO_T ( filename *input X filename *output )
275
{
276
    char *cmd ;
277
    int err = 0 ;
278
    boolean filled_buff = 0 ;
279
    char buff [ buffer_size ] ;   
280
 
281
    cmd_string ( ( char * ) null ) ;
282
    cmd = command [0] ;
283
    if ( cmd == null ) {
284
	error ( INTERNAL, "Empty command" ) ;
285
	return ( null ) ;
286
    }
287
    last_command = cmd ;
288
    last_return = 0 ;
289
    junk = output ;
290
 
291
    if ( taciturn ) {
292
	/* Print input files if in taciturn mode */
293
	filename *p ;
294
	for ( p = input ; p != null ; p = p->next ) {
295
	    if ( p->storage == INPUT_FILE ) {
296
		comment ( 1, "%s:\n", p->name ) ;
297
	    }
298
	}
299
    }
300
 
301
    if ( verbose ) {
302
	/* Print command if in verbose mode */
303
	print_cmd ( buff ) ;
304
	filled_buff = 1 ;
305
	comment ( 1, "%s\n", buff + 1 ) ;
306
    }
307
 
308
    if ( cmd && strneq ( cmd, "builtin/", 8 ) ) {
309
	/* Check built in commands */
310
	cmd += 8 ;
311
	switch ( *cmd ) {
312
	    case 'b' : {
313
		if ( streq ( cmd, "build_archive" ) ) {
314
		    err = build_archive ( command [1], command + 2 ) ;
315
		    goto execute_error ;
316
		}
317
		break ;
318
	    }
319
	    case 'c' : {
320
		if ( streq ( cmd, "cat" ) ) {
321
		    err = cat_file ( command [1] ) ;
322
		    goto execute_error ;
323
		}
324
		break ;
325
	    }
326
	    case 'm' : {
327
		if ( streq ( cmd, "mkdir" ) ) {
328
		    err = make_dir ( command [1] ) ;
329
		    goto execute_error ;
330
		}
331
		if ( streq ( cmd, "move" ) ) {
332
		    err = move_file ( command [1], command [2] ) ;
333
		    goto execute_error ;
334
		}
335
		break ;
336
	    }
337
	    case 'r' : {
338
		if ( streq ( cmd, "remove" ) ) {
339
		    err = remove_file ( command [1] ) ;
340
		    goto execute_error ;
341
		}
342
		break ;
343
	    }
344
	    case 's' : {
345
		if ( streq ( cmd, "split_archive" ) ) {
346
		    err = split_archive ( command [1], &output ) ;
347
		    goto execute_error ;
348
		}
349
		break ;
350
	    }
351
	    case 't' : {
352
		if ( streq ( cmd, "touch" ) ) {
353
		    err = touch_file ( command [1], command [2] ) ;
354
		    goto execute_error ;
355
		}
356
		break ;
357
	    }
358
	    case 'u' : {
359
		if ( streq ( cmd, "undef" ) ) {
360
		    int sev ;
361
		    if ( dry_run ) {
362
			sev = WARNING ;
363
		    } else {
364
			sev = INTERNAL ;
365
			err = 1 ;
366
		    }
367
		    cmd = command [1] ;
368
		    error ( sev, "The tool '%s' is not available", cmd ) ;
369
		    goto execute_error ;
370
		}
371
		break ;
372
	    }
373
	}
374
	error ( SERIOUS, "Built-in '%s' command not implemented", cmd ) ;
375
	err = 1 ;
376
 
377
    } else if ( !dry_run ) {
378
	/* Call system commands */
379
#if FS_FORK
380
	{
381
	    pid_t pid = fork () ;
382
	    if ( pid == ( pid_t ) -1 ) {
383
		error ( SERIOUS, "Can't fork process" ) ;
384
		err = 1 ;
385
	    } else {
386
		if ( pid ) {
387
		    wait_type status ;
388
		    running_pid = ( long ) pid ;
389
		    while ( process_wait ( &status ) != pid ) /* empty */ ;
390
		    running_pid = -1 ;
391
		    if ( process_exited ( status ) ) {
392
			err = process_exit_value ( status ) ;
393
			/* This only returns if there was no remembered
394
			   signal. */
395
			process_delayed_signal () ;
396
		    } else {
397
			if ( process_signaled ( status ) ) {
398
			    /* delay_signal_handling is a global that tells us
399
			       that it is ok to let the next call to execute
400
			       report that the command received a signal.
401
			       This supports the way that the producer is called. */
402
			    int sig = process_signal_value ( status ) ;
403
			    if ( delay_signal_handling && last_signal == 0 ) {
404
				last_signaled_cmd = string_copy ( cmd ) ;
405
				last_signal = sig ;
406
			    } else {
407
				handler ( sig ) ;
408
			    }					
409
			}
410
			err = 1 ;
411
		    }
412
		    goto execute_error ;
413
		}
414
		IGNORE execve ( cmd, command, environment ) ;
415
		running_pid = -1 ;
416
		error ( SERIOUS, "Can't execute '%s'", cmd ) ;
417
		exit ( 2 ) ;
418
	    }
419
	}
420
#else
421
	{
422
	    wait_type status ;
423
	    if ( !filled_buff ) {
424
		print_cmd ( buff ) ;
425
		filled_buff = 1 ;
426
	    }
427
	    err = system ( buff + 1 ) ;
428
	    process_return ( status, err ) ;
429
	    if ( process_exited ( status ) ) {
430
		err = process_exit_value ( status ) ;
431
		process_delayed_signal () ;
432
	    } else {
433
		if ( process_signaled ( status ) ) {
434
		    /* delay_signal_handling is a global that tells us
435
		       that it is ok to let the next call to execute
436
		       report that the command received a signal.
437
		       This supports the way that the producer is called. */
438
		    int sig = process_signal_value ( status ) ;
439
		    if ( delay_signal_handling && last_signal == 0 ) {
440
			last_signaled_cmd = string_copy ( cmd ) ;
441
			last_signal = sig ;
442
		    } else {
443
			handler ( sig ) ;
444
		    }
445
		}
446
		err = 1 ;
447
	    }
448
	}
449
#endif
450
    }
451
 
452
    /* Deal with errors */
453
    execute_error : {      
454
        disable_delayed_signal () ;
455
	last_return = err ;
456
	if ( tidy_up ) {
457
	    /* Remove unneeded files */
458
	    filename *p ;
459
	    for ( p = input ; p != null ; p = p->next ) {
460
		if ( p->storage == TEMP_FILE && p->type != BINARY_OBJ ) {
461
		    IGNORE remove ( p->name ) ;
462
		}
463
	    }
464
	}
465
	if ( err ) {
466
	    exec_error = 1 ;
467
	    exit_status = EXIT_FAILURE ;
468
	    if ( show_errors ) {
469
		/* Show when the error occurred */
470
		if ( !filled_buff ) print_cmd ( buff ) ;
471
		error ( INFO, "Error in '%s'", buff + 1 ) ;
472
	    }
473
	    remove_junk () ;
474
	    return ( null ) ;
475
	}
476
	junk = null ;
477
	return ( output ) ;
478
    }
479
}