]>
git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/build/src/engine/execunix.c
2 * Copyright 1993, 1995 Christopher Seiwald.
3 * Copyright 2007 Noel Belcourt.
5 * This file is part of Jam - see jam.c for Copyright information.
19 #include <unistd.h> /* vfork(), _exit(), STDOUT_FILENO and such */
20 #include <sys/resource.h>
21 #include <sys/times.h>
25 #if defined(sun) || defined(__sun)
31 #include <sys/times.h>
33 #if defined(__APPLE__)
38 #define vfork() fork()
43 * execunix.c - execute a shell script on UNIX/OS2/AmigaOS
45 * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp(). The
46 * default is: /bin/sh -c
48 * In $(JAMSHELL), % expands to the command string and ! expands to the slot
49 * number (starting at 1) for multiprocess (-j) invocations. If $(JAMSHELL) does
50 * not include a %, it is tacked on as the last argument.
52 * Each word must be an individual element in a jam variable value.
54 * Do not just set JAMSHELL to /bin/sh - it will not work!
57 * exec_check() - preprocess and validate the command.
58 * exec_cmd() - launch an async command execution.
59 * exec_wait() - wait for any of the async command processes to terminate.
62 /* find a free slot in the running commands table */
63 static int get_free_cmdtab_slot();
65 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
69 /* We hold stdout & stderr child process information in two element arrays
77 int pid
; /* on win32, a real process handle */
78 int fd
[ 2 ]; /* file descriptors for stdout and stderr */
79 FILE * stream
[ 2 ]; /* child's stdout and stderr file streams */
80 clock_t start_time
; /* start time of child process */
81 int exit_reason
; /* termination status */
82 char * buffer
[ 2 ]; /* buffers to hold stdout and stderr, if any */
83 int buf_size
[ 2 ]; /* buffer sizes in bytes */
84 timestamp start_dt
; /* start of command timestamp */
88 /* Function called when the command completes. */
91 /* Opaque data passed back to the 'func' callback. */
94 static int cmdtab_size
= 0;
96 /* Contains both stdin and stdout of all processes.
97 * The length is either globs.jobs or globs.jobs * 2
98 * depending on globs.pipe_action.
100 struct pollfd
* wait_fds
= NULL
;
101 #define WAIT_FDS_SIZE ( globs.jobs * ( globs.pipe_action ? 2 : 1 ) )
102 #define GET_WAIT_FD( job_idx ) ( wait_fds + ( ( job_idx * ( globs.pipe_action ? 2 : 1 ) ) ) )
105 * exec_init() - global initialization
107 void exec_init( void )
110 if ( globs
.jobs
> cmdtab_size
)
112 cmdtab
= BJAM_REALLOC( cmdtab
, globs
.jobs
* sizeof( *cmdtab
) );
113 memset( cmdtab
+ cmdtab_size
, 0, ( globs
.jobs
- cmdtab_size
) * sizeof( *cmdtab
) );
114 wait_fds
= BJAM_REALLOC( wait_fds
, WAIT_FDS_SIZE
* sizeof ( *wait_fds
) );
115 for ( i
= cmdtab_size
; i
< globs
.jobs
; ++i
)
117 GET_WAIT_FD( i
)[ OUT
].fd
= -1;
118 GET_WAIT_FD( i
)[ OUT
].events
= POLLIN
;
119 if ( globs
.pipe_action
)
121 GET_WAIT_FD( i
)[ ERR
].fd
= -1;
122 GET_WAIT_FD( i
)[ ERR
].events
= POLLIN
;
125 cmdtab_size
= globs
.jobs
;
129 void exec_done( void )
132 BJAM_FREE( wait_fds
);
136 * exec_check() - preprocess and validate the command.
141 string
const * command
,
144 int * error_max_length
147 int const is_raw_cmd
= is_raw_command_request( *pShell
);
149 /* We allow empty commands for non-default shells since we do not really
150 * know what they are going to do with such commands.
152 if ( !command
->size
&& ( is_raw_cmd
|| list_empty( *pShell
) ) )
153 return EXEC_CHECK_NOOP
;
157 : check_cmd_for_too_long_lines( command
->value
, MAXLINE
, error_length
,
163 * exec_cmd() - launch an async command execution.
166 /* We hold file descriptors for pipes used to communicate with child processes
167 * in two element arrays indexed as follows.
169 #define EXECCMD_PIPE_READ 0
170 #define EXECCMD_PIPE_WRITE 1
174 string
const * command
,
176 ExecCmdCallback func
,
181 struct sigaction ignore
, saveintr
, savequit
;
182 sigset_t chldmask
, savemask
;
184 int const slot
= get_free_cmdtab_slot();
188 char const * argv
[ MAXARGC
+ 1 ]; /* +1 for NULL */
190 /* Initialize default shell. */
191 static LIST
* default_shell
;
192 if ( !default_shell
)
193 default_shell
= list_push_back( list_new(
194 object_new( "/bin/sh" ) ),
195 object_new( "-c" ) );
197 if ( list_empty( shell
) )
198 shell
= default_shell
;
200 /* Forumulate argv. If shell was defined, be prepared for % and ! subs.
201 * Otherwise, use stock /bin/sh.
203 argv_from_shell( argv
, shell
, command
->value
, slot
);
208 out_printf( "Using shell: " );
211 for ( i
= 0; argv
[ i
]; ++i
)
212 out_printf( " argv[%d] = '%s'\n", i
, argv
[ i
] );
215 /* Create pipes for collecting child output. */
216 if ( pipe( out
) < 0 || ( globs
.pipe_action
&& pipe( err
) < 0 ) )
222 /* Start the command */
224 timestamp_current( &cmdtab
[ slot
].start_dt
);
226 if ( 0 < globs
.timeout
)
228 /* Handle hung processes by manually tracking elapsed time and signal
229 * process when time limit expires.
232 cmdtab
[ slot
].start_time
= times( &buf
);
234 /* Make a global, only do this once. */
235 if ( !tps
) tps
= sysconf( _SC_CLK_TCK
);
238 /* Child does not need the read pipe ends used by the parent. */
239 fcntl( out
[ EXECCMD_PIPE_READ
], F_SETFD
, FD_CLOEXEC
);
240 if ( globs
.pipe_action
)
241 fcntl( err
[ EXECCMD_PIPE_READ
], F_SETFD
, FD_CLOEXEC
);
243 /* ignore SIGINT and SIGQUIT */
244 ignore
.sa_handler
= SIG_IGN
;
245 sigemptyset(&ignore
.sa_mask
);
247 if (sigaction(SIGINT
, &ignore
, &saveintr
) < 0)
249 if (sigaction(SIGQUIT
, &ignore
, &savequit
) < 0)
253 sigemptyset(&chldmask
);
254 sigaddset(&chldmask
, SIGCHLD
);
255 if (sigprocmask(SIG_BLOCK
, &chldmask
, &savemask
) < 0)
258 if ( ( cmdtab
[ slot
].pid
= vfork() ) == -1 )
264 if ( cmdtab
[ slot
].pid
== 0 )
269 int const pid
= getpid();
271 /* restore previous signals */
272 sigaction(SIGINT
, &saveintr
, NULL
);
273 sigaction(SIGQUIT
, &savequit
, NULL
);
274 sigprocmask(SIG_SETMASK
, &savemask
, NULL
);
276 /* Redirect stdout and stderr to pipes inherited from the parent. */
277 dup2( out
[ EXECCMD_PIPE_WRITE
], STDOUT_FILENO
);
278 dup2( globs
.pipe_action
? err
[ EXECCMD_PIPE_WRITE
] :
279 out
[ EXECCMD_PIPE_WRITE
], STDERR_FILENO
);
280 close( out
[ EXECCMD_PIPE_WRITE
] );
281 if ( globs
.pipe_action
)
282 close( err
[ EXECCMD_PIPE_WRITE
] );
284 /* Make this process a process group leader so that when we kill it, all
285 * child processes of this process are terminated as well. We use
286 * killpg( pid, SIGKILL ) to kill the process group leader and all its
289 if ( 0 < globs
.timeout
)
291 struct rlimit r_limit
;
292 r_limit
.rlim_cur
= globs
.timeout
;
293 r_limit
.rlim_max
= globs
.timeout
;
294 setrlimit( RLIMIT_CPU
, &r_limit
);
296 if (0 != setpgid( pid
, pid
)) {
297 perror("setpgid(child)");
298 /* exit( EXITBAD ); */
300 execvp( argv
[ 0 ], (char * *)argv
);
309 /* redundant call, ignore return value */
310 setpgid(cmdtab
[ slot
].pid
, cmdtab
[ slot
].pid
);
312 /* Parent not need the write pipe ends used by the child. */
313 close( out
[ EXECCMD_PIPE_WRITE
] );
314 if ( globs
.pipe_action
)
315 close( err
[ EXECCMD_PIPE_WRITE
] );
317 /* Set both pipe read file descriptors to non-blocking. */
318 fcntl( out
[ EXECCMD_PIPE_READ
], F_SETFL
, O_NONBLOCK
);
319 if ( globs
.pipe_action
)
320 fcntl( err
[ EXECCMD_PIPE_READ
], F_SETFL
, O_NONBLOCK
);
322 /* Parent reads from out[ EXECCMD_PIPE_READ ]. */
323 cmdtab
[ slot
].fd
[ OUT
] = out
[ EXECCMD_PIPE_READ
];
324 cmdtab
[ slot
].stream
[ OUT
] = fdopen( cmdtab
[ slot
].fd
[ OUT
], "rb" );
325 if ( !cmdtab
[ slot
].stream
[ OUT
] )
331 /* Parent reads from err[ EXECCMD_PIPE_READ ]. */
332 if ( globs
.pipe_action
)
334 cmdtab
[ slot
].fd
[ ERR
] = err
[ EXECCMD_PIPE_READ
];
335 cmdtab
[ slot
].stream
[ ERR
] = fdopen( cmdtab
[ slot
].fd
[ ERR
], "rb" );
336 if ( !cmdtab
[ slot
].stream
[ ERR
] )
343 GET_WAIT_FD( slot
)[ OUT
].fd
= out
[ EXECCMD_PIPE_READ
];
344 if ( globs
.pipe_action
)
345 GET_WAIT_FD( slot
)[ ERR
].fd
= err
[ EXECCMD_PIPE_READ
];
347 cmdtab
[ slot
].flags
= flags
;
349 /* Save input data into the selected running commands table slot. */
350 cmdtab
[ slot
].func
= func
;
351 cmdtab
[ slot
].closure
= closure
;
353 /* restore previous signals */
354 sigaction(SIGINT
, &saveintr
, NULL
);
355 sigaction(SIGQUIT
, &savequit
, NULL
);
356 sigprocmask(SIG_SETMASK
, &savemask
, NULL
);
359 #undef EXECCMD_PIPE_READ
360 #undef EXECCMD_PIPE_WRITE
363 /* Returns 1 if file descriptor is closed, or 0 if it is still alive.
365 * i is index into cmdtab
367 * s (stream) indexes:
368 * - cmdtab[ i ].stream[ s ]
369 * - cmdtab[ i ].buffer[ s ]
370 * - cmdtab[ i ].fd [ s ]
373 static int read_descriptor( int i
, int s
)
376 char buffer
[ BUFSIZ
];
378 while ( 0 < ( ret
= fread( buffer
, sizeof( char ), BUFSIZ
- 1,
379 cmdtab
[ i
].stream
[ s
] ) ) )
383 /* Copy it to our output if appropriate */
384 if ( ! ( cmdtab
[ i
].flags
& EXEC_CMD_QUIET
) )
386 if ( s
== OUT
&& ( globs
.pipe_action
!= 2 ) )
388 else if ( s
== ERR
&& ( globs
.pipe_action
& 2 ) )
392 if ( !cmdtab
[ i
].buffer
[ s
] )
394 /* Never been allocated. */
395 if ( globs
.max_buf
&& ret
> globs
.max_buf
)
400 cmdtab
[ i
].buf_size
[ s
] = ret
+ 1;
401 cmdtab
[ i
].buffer
[ s
] = (char*)BJAM_MALLOC_ATOMIC( ret
+ 1 );
402 memcpy( cmdtab
[ i
].buffer
[ s
], buffer
, ret
+ 1 );
406 /* Previously allocated. */
407 if ( cmdtab
[ i
].buf_size
[ s
] < globs
.max_buf
|| !globs
.max_buf
)
409 char * tmp
= cmdtab
[ i
].buffer
[ s
];
410 int const old_len
= cmdtab
[ i
].buf_size
[ s
] - 1;
411 int const new_len
= old_len
+ ret
+ 1;
412 cmdtab
[ i
].buf_size
[ s
] = new_len
;
413 cmdtab
[ i
].buffer
[ s
] = (char*)BJAM_MALLOC_ATOMIC( new_len
);
414 memcpy( cmdtab
[ i
].buffer
[ s
], tmp
, old_len
);
415 memcpy( cmdtab
[ i
].buffer
[ s
] + old_len
, buffer
, ret
+ 1 );
421 /* If buffer full, ensure last buffer char is newline so that jam log
422 * contains the command status at beginning of it own line instead of
423 * appended to end of the previous output.
425 if ( globs
.max_buf
&& globs
.max_buf
<= cmdtab
[ i
].buf_size
[ s
] )
426 cmdtab
[ i
].buffer
[ s
][ cmdtab
[ i
].buf_size
[ s
] - 2 ] = '\n';
428 return feof( cmdtab
[ i
].stream
[ s
] );
433 * close_streams() - Close the stream and pipe descriptor.
436 static void close_streams( int const i
, int const s
)
438 fclose( cmdtab
[ i
].stream
[ s
] );
439 cmdtab
[ i
].stream
[ s
] = 0;
441 close( cmdtab
[ i
].fd
[ s
] );
442 cmdtab
[ i
].fd
[ s
] = 0;
444 GET_WAIT_FD( i
)[ s
].fd
= -1;
449 * exec_wait() - wait for any of the async command processes to terminate.
451 * May register more than one terminated child process but will exit as soon as
452 * at least one has been registered.
459 /* Process children that signaled. */
464 struct timeval
* ptv
= NULL
;
465 int select_timeout
= globs
.timeout
;
467 /* Check for timeouts:
468 * - kill children that already timed out
469 * - decide how long until the next one times out
471 if ( globs
.timeout
> 0 )
474 clock_t const current
= times( &buf
);
475 for ( i
= 0; i
< globs
.jobs
; ++i
)
476 if ( cmdtab
[ i
].pid
)
478 clock_t const consumed
=
479 ( current
- cmdtab
[ i
].start_time
) / tps
;
480 if ( consumed
>= globs
.timeout
)
482 killpg( cmdtab
[ i
].pid
, SIGKILL
);
483 cmdtab
[ i
].exit_reason
= EXIT_TIMEOUT
;
485 else if ( globs
.timeout
- consumed
< select_timeout
)
486 select_timeout
= globs
.timeout
- consumed
;
489 /* If nothing else causes our select() call to exit, force it after
490 * however long it takes for the next one of our child processes to
491 * crossed its alloted processing time so we can terminate it.
493 tv
.tv_sec
= select_timeout
;
498 /* select() will wait for I/O on a descriptor, a signal, or timeout. */
500 /* disable child termination signals while in select */
503 sigemptyset(&sigmask
);
504 sigaddset(&sigmask
, SIGCHLD
);
505 sigprocmask(SIG_BLOCK
, &sigmask
, NULL
);
506 while ( ( ret
= poll( wait_fds
, WAIT_FDS_SIZE
, select_timeout
* 1000 ) ) == -1 )
507 if ( errno
!= EINTR
)
509 /* restore original signal mask by unblocking sigchld */
510 sigprocmask(SIG_UNBLOCK
, &sigmask
, NULL
);
515 for ( i
= 0; i
< globs
.jobs
; ++i
)
519 if ( GET_WAIT_FD( i
)[ OUT
].revents
)
520 out_done
= read_descriptor( i
, OUT
);
522 if ( globs
.pipe_action
&& ( GET_WAIT_FD( i
)[ ERR
].revents
) )
523 err_done
= read_descriptor( i
, ERR
);
525 /* If feof on either descriptor, we are done. */
526 if ( out_done
|| err_done
)
531 timing_info time_info
;
532 struct rusage cmd_usage
;
534 /* We found a terminated child process - our search is done. */
537 /* Close the stream and pipe descriptors. */
538 close_streams( i
, OUT
);
539 if ( globs
.pipe_action
)
540 close_streams( i
, ERR
);
542 /* Reap the child and release resources. */
543 while ( ( pid
= wait4( cmdtab
[ i
].pid
, &status
, 0, &cmd_usage
) ) == -1 )
544 if ( errno
!= EINTR
)
546 if ( pid
!= cmdtab
[ i
].pid
)
548 err_printf( "unknown pid %d with errno = %d\n", pid
, errno
);
552 /* Set reason for exit if not timed out. */
553 if ( WIFEXITED( status
) )
554 cmdtab
[ i
].exit_reason
= WEXITSTATUS( status
)
559 time_info
.system
= ((double)(cmd_usage
.ru_stime
.tv_sec
)*1000000.0+(double)(cmd_usage
.ru_stime
.tv_usec
))/1000000.0;
560 time_info
.user
= ((double)(cmd_usage
.ru_utime
.tv_sec
)*1000000.0+(double)(cmd_usage
.ru_utime
.tv_usec
))/1000000.0;
561 timestamp_copy( &time_info
.start
, &cmdtab
[ i
].start_dt
);
562 timestamp_current( &time_info
.end
);
565 /* Drive the completion. */
567 rstat
= EXEC_CMD_INTR
;
569 rstat
= EXEC_CMD_FAIL
;
573 /* Call the callback, may call back to jam rule land. */
574 (*cmdtab
[ i
].func
)( cmdtab
[ i
].closure
, rstat
, &time_info
,
575 cmdtab
[ i
].buffer
[ OUT
], cmdtab
[ i
].buffer
[ ERR
],
576 cmdtab
[ i
].exit_reason
);
578 /* Clean up the command's running commands table slot. */
579 BJAM_FREE( cmdtab
[ i
].buffer
[ OUT
] );
580 cmdtab
[ i
].buffer
[ OUT
] = 0;
581 cmdtab
[ i
].buf_size
[ OUT
] = 0;
583 BJAM_FREE( cmdtab
[ i
].buffer
[ ERR
] );
584 cmdtab
[ i
].buffer
[ ERR
] = 0;
585 cmdtab
[ i
].buf_size
[ ERR
] = 0;
588 cmdtab
[ i
].func
= 0;
589 cmdtab
[ i
].closure
= 0;
590 cmdtab
[ i
].start_time
= 0;
598 * Find a free slot in the running commands table.
601 static int get_free_cmdtab_slot()
604 for ( slot
= 0; slot
< globs
.jobs
; ++slot
)
605 if ( !cmdtab
[ slot
].pid
)
607 err_printf( "no slots for child!\n" );
611 # endif /* USE_EXECUNIX */