1 /*
2  * Copyright 1993, 1995 Christopher Seiwald.
3  * Copyright 2007 Noel Belcourt.
4  *
5  * This file is part of Jam - see jam.c for Copyright information.
6  */
7 
8 #include "jam.h"
9 #include "execcmd.h"
10 
11 #include "lists.h"
12 #include "output.h"
13 #include "jam_strings.h"
14 
15 #include <errno.h>
16 #include <signal.h>
17 #include <stdio.h>
18 #include <time.h>
19 #include <unistd.h>  /* vfork(), _exit(), STDOUT_FILENO and such */
20 #include <sys/resource.h>
21 #include <sys/times.h>
22 #include <sys/wait.h>
23 #include <poll.h>
24 
25 #if defined(sun) || defined(__sun)
26     #include <wait.h>
27 #endif
28 
29 #ifdef USE_EXECUNIX
30 
31 #include <sys/times.h>
32 
33 #if defined(__APPLE__)
34     #define NO_VFORK
35 #endif
36 
37 #ifdef NO_VFORK
38     #define vfork() fork()
39 #endif
40 
41 
42 /*
43  * execunix.c - execute a shell script on UNIX/OS2/AmigaOS
44  *
45  * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp(). The
46  * default is: /bin/sh -c
47  *
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.
51  *
52  * Each word must be an individual element in a jam variable value.
53  *
54  * Do not just set JAMSHELL to /bin/sh - it will not work!
55  *
56  * External routines:
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.
60  */
61 
62 /* find a free slot in the running commands table */
63 static int get_free_cmdtab_slot();
64 
65 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
66 
67 static clock_t tps;
68 
69 /* We hold stdout & stderr child process information in two element arrays
70  * indexed as follows.
71  */
72 #define OUT 0
73 #define ERR 1
74 
75 static struct cmdtab_t
76 {
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 */
85 
86     int flags;
87 
88     /* Function called when the command completes. */
89     ExecCmdCallback func;
90 
91     /* Opaque data passed back to the 'func' callback. */
92     void * closure;
93 } * cmdtab = NULL;
94 static int cmdtab_size = 0;
95 
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.
99  */
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 ) ) ) )
103 
104 /*
105  * exec_init() - global initialization
106  */
exec_init(void)107 void exec_init( void )
108 {
109     int i;
110     if ( globs.jobs > cmdtab_size )
111     {
112         cmdtab = (cmdtab_t*)BJAM_REALLOC( cmdtab, globs.jobs * sizeof( *cmdtab ) );
113         memset( cmdtab + cmdtab_size, 0, ( globs.jobs - cmdtab_size ) * sizeof( *cmdtab ) );
114         wait_fds = (pollfd*)BJAM_REALLOC( wait_fds, WAIT_FDS_SIZE * sizeof ( *wait_fds ) );
115         for ( i = cmdtab_size; i < globs.jobs; ++i )
116         {
117             GET_WAIT_FD( i )[ OUT ].fd = -1;
118             GET_WAIT_FD( i )[ OUT ].events = POLLIN;
119             if ( globs.pipe_action )
120             {
121                 GET_WAIT_FD( i )[ ERR ].fd = -1;
122                 GET_WAIT_FD( i )[ ERR ].events = POLLIN;
123             }
124         }
125         cmdtab_size = globs.jobs;
126     }
127 }
128 
exec_done(void)129 void exec_done( void )
130 {
131     BJAM_FREE( cmdtab );
132     BJAM_FREE( wait_fds );
133 }
134 
135 /*
136  * exec_check() - preprocess and validate the command.
137  */
138 
exec_check(string const * command,LIST ** pShell,int * error_length,int * error_max_length)139 int exec_check
140 (
141     string const * command,
142     LIST * * pShell,
143     int * error_length,
144     int * error_max_length
145 )
146 {
147     int const is_raw_cmd = is_raw_command_request( *pShell );
148 
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.
151      */
152     if ( !command->size && ( is_raw_cmd || list_empty( *pShell ) ) )
153         return EXEC_CHECK_NOOP;
154 
155     return is_raw_cmd
156         ? EXEC_CHECK_OK
157         : check_cmd_for_too_long_lines( command->value, MAXLINE, error_length,
158             error_max_length );
159 }
160 
161 
162 /*
163  * exec_cmd() - launch an async command execution.
164  */
165 
166 /* We hold file descriptors for pipes used to communicate with child processes
167  * in two element arrays indexed as follows.
168  */
169 #define EXECCMD_PIPE_READ 0
170 #define EXECCMD_PIPE_WRITE 1
171 
exec_cmd(string const * command,int flags,ExecCmdCallback func,void * closure,LIST * shell)172 void exec_cmd
173 (
174     string const * command,
175     int flags,
176     ExecCmdCallback func,
177     void * closure,
178     LIST * shell
179 )
180 {
181     struct sigaction ignore, saveintr, savequit;
182     sigset_t chldmask, savemask;
183 
184     int const slot = get_free_cmdtab_slot();
185     int out[ 2 ];
186     int err[ 2 ];
187     char const * argv[ MAXARGC + 1 ];  /* +1 for NULL */
188 
189     /* Initialize default shell. */
190     static LIST * default_shell;
191     if ( !default_shell )
192         default_shell = list_push_back( list_new(
193             object_new( "/bin/sh" ) ),
194             object_new( "-c" ) );
195 
196     if ( list_empty( shell ) )
197         shell = default_shell;
198 
199     /* Forumulate argv. If shell was defined, be prepared for % and ! subs.
200      * Otherwise, use stock /bin/sh.
201      */
202     argv_from_shell( argv, shell, command->value, slot );
203 
204     if ( DEBUG_EXECCMD )
205     {
206         int i;
207         out_printf( "Using shell: " );
208         list_print( shell );
209         out_printf( "\n" );
210         for ( i = 0; argv[ i ]; ++i )
211             out_printf( "    argv[%d] = '%s'\n", i, argv[ i ] );
212     }
213 
214     /* Create pipes for collecting child output. */
215     if ( pipe( out ) < 0 || ( globs.pipe_action && pipe( err ) < 0 ) )
216     {
217         perror( "pipe" );
218         exit( EXITBAD );
219     }
220 
221     /* Start the command */
222 
223     timestamp_current( &cmdtab[ slot ].start_dt );
224 
225     if ( 0 < globs.timeout )
226     {
227         /* Handle hung processes by manually tracking elapsed time and signal
228          * process when time limit expires.
229          */
230         struct tms buf;
231         cmdtab[ slot ].start_time = times( &buf );
232 
233         /* Make a global, only do this once. */
234         if ( !tps ) tps = sysconf( _SC_CLK_TCK );
235     }
236 
237     /* Child does not need the read pipe ends used by the parent. */
238     fcntl( out[ EXECCMD_PIPE_READ ], F_SETFD, FD_CLOEXEC );
239     if ( globs.pipe_action )
240         fcntl( err[ EXECCMD_PIPE_READ ], F_SETFD, FD_CLOEXEC );
241 
242     /* ignore SIGINT and SIGQUIT */
243     ignore.sa_handler = SIG_IGN;
244     sigemptyset(&ignore.sa_mask);
245     ignore.sa_flags = 0;
246     if (sigaction(SIGINT, &ignore, &saveintr) < 0)
247         return;
248     if (sigaction(SIGQUIT, &ignore, &savequit) < 0)
249         return;
250 
251     /* block SIGCHLD */
252     sigemptyset(&chldmask);
253     sigaddset(&chldmask, SIGCHLD);
254     if (sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0)
255         return;
256 
257     if ( ( cmdtab[ slot ].pid = vfork() ) == -1 )
258     {
259         perror( "vfork" );
260         exit( EXITBAD );
261     }
262 
263     if ( cmdtab[ slot ].pid == 0 )
264     {
265         /*****************/
266         /* Child process */
267         /*****************/
268         int const pid = getpid();
269 
270         /* restore previous signals */
271         sigaction(SIGINT, &saveintr, NULL);
272         sigaction(SIGQUIT, &savequit, NULL);
273         sigprocmask(SIG_SETMASK, &savemask, NULL);
274 
275         /* Redirect stdout and stderr to pipes inherited from the parent. */
276         dup2( out[ EXECCMD_PIPE_WRITE ], STDOUT_FILENO );
277         dup2( globs.pipe_action ? err[ EXECCMD_PIPE_WRITE ] :
278             out[ EXECCMD_PIPE_WRITE ], STDERR_FILENO );
279         close( out[ EXECCMD_PIPE_WRITE ] );
280         if ( globs.pipe_action )
281             close( err[ EXECCMD_PIPE_WRITE ] );
282 
283         /* Make this process a process group leader so that when we kill it, all
284          * child processes of this process are terminated as well. We use
285          * killpg( pid, SIGKILL ) to kill the process group leader and all its
286          * children.
287          */
288         if ( 0 < globs.timeout )
289         {
290             struct rlimit r_limit;
291             r_limit.rlim_cur = globs.timeout;
292             r_limit.rlim_max = globs.timeout;
293             setrlimit( RLIMIT_CPU, &r_limit );
294         }
295         if (0 != setpgid( pid, pid )) {
296             perror("setpgid(child)");
297             /* exit( EXITBAD ); */
298         }
299         execvp( argv[ 0 ], (char * *)argv );
300         perror( "execvp" );
301         _exit( 127 );
302     }
303 
304     /******************/
305     /* Parent process */
306     /******************/
307 
308     /* redundant call, ignore return value */
309     setpgid(cmdtab[ slot ].pid, cmdtab[ slot ].pid);
310 
311     /* Parent not need the write pipe ends used by the child. */
312     close( out[ EXECCMD_PIPE_WRITE ] );
313     if ( globs.pipe_action )
314         close( err[ EXECCMD_PIPE_WRITE ] );
315 
316     /* Set both pipe read file descriptors to non-blocking. */
317     fcntl( out[ EXECCMD_PIPE_READ ], F_SETFL, O_NONBLOCK );
318     if ( globs.pipe_action )
319         fcntl( err[ EXECCMD_PIPE_READ ], F_SETFL, O_NONBLOCK );
320 
321     /* Parent reads from out[ EXECCMD_PIPE_READ ]. */
322     cmdtab[ slot ].fd[ OUT ] = out[ EXECCMD_PIPE_READ ];
323     cmdtab[ slot ].stream[ OUT ] = fdopen( cmdtab[ slot ].fd[ OUT ], "rb" );
324     if ( !cmdtab[ slot ].stream[ OUT ] )
325     {
326         perror( "fdopen" );
327         exit( EXITBAD );
328     }
329 
330     /* Parent reads from err[ EXECCMD_PIPE_READ ]. */
331     if ( globs.pipe_action )
332     {
333         cmdtab[ slot ].fd[ ERR ] = err[ EXECCMD_PIPE_READ ];
334         cmdtab[ slot ].stream[ ERR ] = fdopen( cmdtab[ slot ].fd[ ERR ], "rb" );
335         if ( !cmdtab[ slot ].stream[ ERR ] )
336         {
337             perror( "fdopen" );
338             exit( EXITBAD );
339         }
340     }
341 
342     GET_WAIT_FD( slot )[ OUT ].fd = out[ EXECCMD_PIPE_READ ];
343     if ( globs.pipe_action )
344         GET_WAIT_FD( slot )[ ERR ].fd = err[ EXECCMD_PIPE_READ ];
345 
346     cmdtab[ slot ].flags = flags;
347 
348     /* Save input data into the selected running commands table slot. */
349     cmdtab[ slot ].func = func;
350     cmdtab[ slot ].closure = closure;
351 
352     /* restore previous signals */
353     sigaction(SIGINT, &saveintr, NULL);
354     sigaction(SIGQUIT, &savequit, NULL);
355     sigprocmask(SIG_SETMASK, &savemask, NULL);
356 }
357 
358 #undef EXECCMD_PIPE_READ
359 #undef EXECCMD_PIPE_WRITE
360 
361 
362 /* Returns 1 if file descriptor is closed, or 0 if it is still alive.
363  *
364  * i is index into cmdtab
365  *
366  * s (stream) indexes:
367  *  - cmdtab[ i ].stream[ s ]
368  *  - cmdtab[ i ].buffer[ s ]
369  *  - cmdtab[ i ].fd    [ s ]
370  */
371 
read_descriptor(int i,int s)372 static int read_descriptor( int i, int s )
373 {
374     int ret;
375     char buffer[ BUFSIZ ];
376 
377     while ( 0 < ( ret = fread( buffer, sizeof( char ), BUFSIZ - 1,
378         cmdtab[ i ].stream[ s ] ) ) )
379     {
380         buffer[ ret ] = 0;
381 
382         /* Copy it to our output if appropriate */
383         if ( ! ( cmdtab[ i ].flags & EXEC_CMD_QUIET ) )
384         {
385             if ( s == OUT && ( globs.pipe_action != 2 ) )
386                 out_data( buffer );
387             else if ( s == ERR && ( globs.pipe_action & 2 ) )
388                 err_data( buffer );
389         }
390 
391         if ( !cmdtab[ i ].buffer[ s ] )
392         {
393             /* Never been allocated. */
394             if ( globs.max_buf && ret > globs.max_buf )
395             {
396                 ret = globs.max_buf;
397                 buffer[ ret ] = 0;
398             }
399             cmdtab[ i ].buf_size[ s ] = ret + 1;
400             cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( ret + 1 );
401             memcpy( cmdtab[ i ].buffer[ s ], buffer, ret + 1 );
402         }
403         else
404         {
405             /* Previously allocated. */
406             if ( cmdtab[ i ].buf_size[ s ] < globs.max_buf || !globs.max_buf )
407             {
408                 char * tmp = cmdtab[ i ].buffer[ s ];
409                 int const old_len = cmdtab[ i ].buf_size[ s ] - 1;
410                 int const new_len = old_len + ret + 1;
411                 cmdtab[ i ].buf_size[ s ] = new_len;
412                 cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( new_len );
413                 memcpy( cmdtab[ i ].buffer[ s ], tmp, old_len );
414                 memcpy( cmdtab[ i ].buffer[ s ] + old_len, buffer, ret + 1 );
415                 BJAM_FREE( tmp );
416             }
417         }
418     }
419 
420     /* If buffer full, ensure last buffer char is newline so that jam log
421      * contains the command status at beginning of it own line instead of
422      * appended to end of the previous output.
423      */
424     if ( globs.max_buf && globs.max_buf <= cmdtab[ i ].buf_size[ s ] )
425         cmdtab[ i ].buffer[ s ][ cmdtab[ i ].buf_size[ s ] - 2 ] = '\n';
426 
427     return feof( cmdtab[ i ].stream[ s ] );
428 }
429 
430 
431 /*
432  * close_streams() - Close the stream and pipe descriptor.
433  */
434 
close_streams(int const i,int const s)435 static void close_streams( int const i, int const s )
436 {
437     fclose( cmdtab[ i ].stream[ s ] );
438     cmdtab[ i ].stream[ s ] = 0;
439 
440     close( cmdtab[ i ].fd[ s ] );
441     cmdtab[ i ].fd[ s ] = 0;
442 
443     GET_WAIT_FD( i )[ s ].fd = -1;
444 }
445 
446 
447 /*
448  * exec_wait() - wait for any of the async command processes to terminate.
449  *
450  * May register more than one terminated child process but will exit as soon as
451  * at least one has been registered.
452  */
453 
exec_wait()454 void exec_wait()
455 {
456     int finished = 0;
457 
458     /* Process children that signaled. */
459     while ( !finished )
460     {
461         int i;
462         int select_timeout = globs.timeout;
463 
464         /* Check for timeouts:
465          *   - kill children that already timed out
466          *   - decide how long until the next one times out
467          */
468         if ( globs.timeout > 0 )
469         {
470             struct tms buf;
471             clock_t const current = times( &buf );
472             for ( i = 0; i < globs.jobs; ++i )
473                 if ( cmdtab[ i ].pid )
474                 {
475                     clock_t const consumed =
476                         ( current - cmdtab[ i ].start_time ) / tps;
477                     if ( consumed >= globs.timeout )
478                     {
479                         killpg( cmdtab[ i ].pid, SIGKILL );
480                         cmdtab[ i ].exit_reason = EXIT_TIMEOUT;
481                     }
482                     else if ( globs.timeout - consumed < select_timeout )
483                         select_timeout = globs.timeout - consumed;
484                 }
485         }
486 
487         /* select() will wait for I/O on a descriptor, a signal, or timeout. */
488         {
489             /* disable child termination signals while in select */
490             int ret;
491             int timeout;
492             sigset_t sigmask;
493             sigemptyset(&sigmask);
494             sigaddset(&sigmask, SIGCHLD);
495             sigprocmask(SIG_BLOCK, &sigmask, NULL);
496 
497             /* If no timeout is specified, pass -1 (which means no timeout,
498              * wait indefinitely) to poll, to prevent busy-looping.
499              */
500             timeout = select_timeout? select_timeout * 1000 : -1;
501             while ( ( ret = poll( wait_fds, WAIT_FDS_SIZE, timeout ) ) == -1 )
502                 if ( errno != EINTR )
503                     break;
504             /* restore original signal mask by unblocking sigchld */
505             sigprocmask(SIG_UNBLOCK, &sigmask, NULL);
506             if ( ret <= 0 )
507                 continue;
508         }
509 
510         for ( i = 0; i < globs.jobs; ++i )
511         {
512             int out_done = 0;
513             int err_done = 0;
514             if ( GET_WAIT_FD( i )[ OUT ].revents )
515                 out_done = read_descriptor( i, OUT );
516 
517             if ( globs.pipe_action && ( GET_WAIT_FD( i )[ ERR ].revents ) )
518                 err_done = read_descriptor( i, ERR );
519 
520             /* If feof on either descriptor, we are done. */
521             if ( out_done || err_done )
522             {
523                 int pid;
524                 int status;
525                 int rstat;
526                 timing_info time_info;
527                 struct rusage cmd_usage;
528 
529                 /* We found a terminated child process - our search is done. */
530                 finished = 1;
531 
532                 /* Close the stream and pipe descriptors. */
533                 close_streams( i, OUT );
534                 if ( globs.pipe_action )
535                     close_streams( i, ERR );
536 
537                 /* Reap the child and release resources. */
538                 while ( ( pid = wait4( cmdtab[ i ].pid, &status, 0, &cmd_usage ) ) == -1 )
539                     if ( errno != EINTR )
540                         break;
541                 if ( pid != cmdtab[ i ].pid )
542                 {
543                     err_printf( "unknown pid %d with errno = %d\n", pid, errno );
544                     exit( EXITBAD );
545                 }
546 
547                 /* Set reason for exit if not timed out. */
548                 if ( WIFEXITED( status ) )
549                     cmdtab[ i ].exit_reason = WEXITSTATUS( status )
550                         ? EXIT_FAIL
551                         : EXIT_OK;
552 
553                 {
554                     time_info.system = ((double)(cmd_usage.ru_stime.tv_sec)*1000000.0+(double)(cmd_usage.ru_stime.tv_usec))/1000000.0;
555                     time_info.user   = ((double)(cmd_usage.ru_utime.tv_sec)*1000000.0+(double)(cmd_usage.ru_utime.tv_usec))/1000000.0;
556                     timestamp_copy( &time_info.start, &cmdtab[ i ].start_dt );
557                     timestamp_current( &time_info.end );
558                 }
559 
560                 /* Drive the completion. */
561                 if ( interrupted() )
562                     rstat = EXEC_CMD_INTR;
563                 else if ( status )
564                     rstat = EXEC_CMD_FAIL;
565                 else
566                     rstat = EXEC_CMD_OK;
567 
568                 /* Call the callback, may call back to jam rule land. */
569                 (*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, &time_info,
570                     cmdtab[ i ].buffer[ OUT ], cmdtab[ i ].buffer[ ERR ],
571                     cmdtab[ i ].exit_reason );
572 
573                 /* Clean up the command's running commands table slot. */
574                 BJAM_FREE( cmdtab[ i ].buffer[ OUT ] );
575                 cmdtab[ i ].buffer[ OUT ] = 0;
576                 cmdtab[ i ].buf_size[ OUT ] = 0;
577 
578                 BJAM_FREE( cmdtab[ i ].buffer[ ERR ] );
579                 cmdtab[ i ].buffer[ ERR ] = 0;
580                 cmdtab[ i ].buf_size[ ERR ] = 0;
581 
582                 cmdtab[ i ].pid = 0;
583                 cmdtab[ i ].func = 0;
584                 cmdtab[ i ].closure = 0;
585                 cmdtab[ i ].start_time = 0;
586             }
587         }
588     }
589 }
590 
591 
592 /*
593  * Find a free slot in the running commands table.
594  */
595 
get_free_cmdtab_slot()596 static int get_free_cmdtab_slot()
597 {
598     int slot;
599     for ( slot = 0; slot < globs.jobs; ++slot )
600         if ( !cmdtab[ slot ].pid )
601             return slot;
602     err_printf( "no slots for child!\n" );
603     exit( EXITBAD );
604 }
605 
606 # endif /* USE_EXECUNIX */
607