2005-09-20 21:26:39 +08:00
/** \file exec.c
Functions for executing a program .
Some of the code in this file is based on code from the Glibc
manual , though I the changes performed have been massive .
*/
# include <stdlib.h>
# include <stdio.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <termios.h>
# include <unistd.h>
# include <fcntl.h>
# include <errno.h>
# include <wchar.h>
# include <string.h>
# include <limits.h>
# include <signal.h>
# include <sys/wait.h>
# include <assert.h>
# include <dirent.h>
# include "config.h"
# include "util.h"
# include "common.h"
# include "wutil.h"
# include "proc.h"
# include "exec.h"
# include "parser.h"
# include "builtin.h"
# include "function.h"
# include "env.h"
# include "wildcard.h"
# include "sanity.h"
# include "expand.h"
# include "env_universal.h"
/**
Prototype for the getpgid library function . The prototype for this
function seems to be missing in glibc , at least I ' ve not found any
combination of # includes , macros and compiler switches that will
include it .
*/
pid_t getpgid ( pid_t pid ) ;
/**
file descriptor redirection error message
*/
# define FD_ERROR L"An error occurred while redirecting file descriptor %d"
/**
file redirection error message
*/
# define FILE_ERROR L"An error occurred while redirecting file '%ls'"
/**
pipe redirection error message
*/
# define PIPE_ERROR L"An error occurred while setting up pipe"
/**
fork error message
*/
# define FORK_ERROR L"Could not create child process - exiting"
2005-09-23 21:10:31 +08:00
static void close_loop ( int fd )
{
while ( close ( fd ) = = - 1 )
{
if ( errno ! = EINTR )
{
debug ( 1 , FD_ERROR , fd ) ;
wperror ( L " close " ) ;
}
}
}
2005-09-20 21:26:39 +08:00
/**
Make sure the fd used by this redirection is not used by i . e . a pipe .
*/
void free_fd ( io_data_t * io , int fd )
{
if ( ! io )
return ;
if ( io - > io_mode = = IO_PIPE )
{
int i ;
for ( i = 0 ; i < 2 ; i + + )
{
if ( io - > pipe_fd [ i ] = = fd )
{
while ( 1 )
{
if ( ( io - > pipe_fd [ i ] = dup ( fd ) ) = = - 1 )
{
if ( errno ! = EINTR )
{
debug ( 1 ,
FD_ERROR ,
fd ) ;
wperror ( L " dup " ) ;
exit ( 1 ) ;
}
}
else
break ;
}
}
}
}
}
static void handle_child_io ( io_data_t * io )
{
if ( env_universal_server . fd > = 0 )
2005-09-23 21:10:31 +08:00
close_loop ( env_universal_server . fd ) ;
2005-09-20 21:26:39 +08:00
for ( ; io ; io = io - > next )
{
int tmp ;
if ( io - > io_mode = = IO_FD & & io - > fd = = io - > old_fd )
{
continue ;
}
if ( io - > fd > 2 )
{
/*
Make sure the fd used by this redirection is not used by i . e . a pipe .
*/
free_fd ( io , io - > fd ) ;
}
2005-09-23 21:10:31 +08:00
while ( close ( io - > fd ) = = - 1 )
2005-09-20 21:26:39 +08:00
{
2005-09-23 21:10:31 +08:00
if ( errno ! = EINTR )
break ;
2005-09-20 21:26:39 +08:00
/* debug( 1,
FD_ERROR ,
io - > fd ) ;
wperror ( L " close " ) ;
exit ( 1 ) ; */
}
switch ( io - > io_mode )
{
case IO_CLOSE :
break ;
case IO_FILE :
if ( ( tmp = wopen ( io - > filename , io - > flags , 0666 ) ) = = - 1 )
{
debug ( 1 ,
FILE_ERROR ,
io - > filename ) ;
wperror ( L " open " ) ;
exit ( 1 ) ;
}
else if ( tmp ! = io - > fd )
{
if ( dup2 ( tmp , io - > fd ) = = - 1 )
{
debug ( 1 ,
FD_ERROR ,
io - > fd ) ;
wperror ( L " dup2 " ) ;
exit ( 1 ) ;
}
2005-09-23 21:10:31 +08:00
close_loop ( tmp ) ;
2005-09-20 21:26:39 +08:00
}
break ;
case IO_FD :
/* debug( 3, L"Redirect fd %d in process %ls (%d) from fd %d",
io - > fd ,
p - > actual_cmd ,
p - > pid ,
io - > old_fd ) ;
*/
if ( dup2 ( io - > old_fd , io - > fd ) = = - 1 )
{
debug ( 1 ,
FD_ERROR ,
io - > fd ) ;
wperror ( L " dup2 " ) ;
exit ( 1 ) ;
}
break ;
case IO_BUFFER :
case IO_PIPE :
{
/* debug( 3, L"Pipe fd %d in process %ls (%d) (Through fd %d)",
io - > fd ,
p - > actual_cmd ,
p - > pid ,
io - > pipe_fd [ io - > fd ] ) ;
*/
int fd_to_dup = io - > fd ; //io->io_mode==IO_BUFFER?1:(io->fd?1:0);
if ( dup2 ( io - > pipe_fd [ fd_to_dup ] , io - > fd ) = = - 1 )
{
debug ( 1 , PIPE_ERROR ) ;
wperror ( L " dup2 " ) ;
exit ( 1 ) ;
}
if ( fd_to_dup )
{
2005-09-23 21:10:31 +08:00
close_loop ( io - > pipe_fd [ 0 ] ) ;
close_loop ( io - > pipe_fd [ 1 ] ) ;
2005-09-20 21:26:39 +08:00
}
else
2005-09-23 21:10:31 +08:00
close_loop ( io - > pipe_fd [ fd_to_dup ] ) ;
2005-09-20 21:26:39 +08:00
/*
if ( close ( io [ i ] . pipe_fd [ io - > fd ] ) = = - 1 )
{
wperror ( L " close " ) ;
exit ( 1 ) ;
}
*/
/* if( close( io[i].pipe_fd[1] ) == -1 )
{
wperror ( L " close " ) ;
exit ( 1 ) ;
}
*/
/* fprintf( stderr, "fd %d points to fd %d\n", i, io[i].fd[i>0?1:0] );*/
break ;
}
}
}
}
static void setup_child_process ( job_t * j )
{
if ( is_interactive & & ! is_subshell & & ! is_block )
{
pid_t pid ;
/*
Put the process into the process group and give the process group
the terminal , if appropriate .
This has to be done both by the shell and in the individual
child processes because of potential race conditions .
*/
pid = getpid ( ) ;
if ( j - > pgid = = 0 )
j - > pgid = pid ;
/* Wait till shell puts os in our own group */
while ( getpgrp ( ) ! = j - > pgid )
sleep ( 0 ) ;
/* Wait till shell gives us stdin */
if ( j - > fg )
{
while ( tcgetpgrp ( 0 ) ! = j - > pgid )
sleep ( 0 ) ;
}
}
/* Set the handling for job control signals back to the default. */
signal ( SIGINT , SIG_DFL ) ;
signal ( SIGQUIT , SIG_DFL ) ;
signal ( SIGTSTP , SIG_DFL ) ;
signal ( SIGTTIN , SIG_DFL ) ;
signal ( SIGTTOU , SIG_DFL ) ;
signal ( SIGCHLD , SIG_DFL ) ;
sigset_t chldset ;
sigemptyset ( & chldset ) ;
sigaddset ( & chldset , SIGCHLD ) ;
sigprocmask ( SIG_UNBLOCK , & chldset , 0 ) ;
handle_child_io ( j - > io ) ;
}
/**
This function is executed by the child process created by a call to
fork ( ) . This function begins by updating FDs as specified by the io
parameter . If the command requested for this process is not a
builtin , execv is then called with the appropriate parameters . If
the command is a builtin , the contents of out and err are printed .
*/
static void launch_process ( process_t * p )
{
/* Set the standard input/output channels of the new process. */
2005-09-23 04:16:52 +08:00
execve ( wcs2str ( p - > actual_cmd ) , wcsv2strv ( ( const wchar_t * * ) p - > argv ) , env_export_arr ( 0 ) ) ;
2005-09-20 21:26:39 +08:00
debug ( 0 ,
L " Failed to execute process %ls " ,
p - > actual_cmd ) ;
wperror ( L " execve " ) ;
exit ( 1 ) ;
}
/**
Check if the IO redirection chains contains redirections for the specified file descriptor
*/
static int has_fd ( io_data_t * d , int fd )
{
return io_get ( d , fd ) ! = 0 ;
}
/**
Read from descriptors until they are empty .
*/
2005-09-20 22:51:00 +08:00
void exec_read_io_buffer ( io_data_t * d )
2005-09-20 21:26:39 +08:00
{
2005-09-20 22:51:00 +08:00
2005-09-23 21:10:31 +08:00
close_loop ( d - > pipe_fd [ 1 ] ) ;
2005-09-20 21:26:39 +08:00
if ( d - > io_mode = = IO_BUFFER )
2005-09-23 21:10:31 +08:00
{
/* if( fcntl( d->pipe_fd[0], F_SETFL, 0 ) )
2005-09-20 21:26:39 +08:00
{
wperror ( L " fcntl " ) ;
return ;
}
2005-09-23 21:10:31 +08:00
*/
2005-09-20 22:51:00 +08:00
debug ( 3 , L " exec_read_io_buffer: blocking read on fd %d " , d - > pipe_fd [ 0 ] ) ;
2005-09-20 21:26:39 +08:00
while ( 1 )
{
char b [ 4096 ] ;
int l ;
2005-09-23 21:10:31 +08:00
l = read_blocked ( d - > pipe_fd [ 0 ] , b , 4096 ) ;
2005-09-20 21:26:39 +08:00
if ( l = = 0 )
{
break ;
}
else if ( l < 0 )
{
2005-09-23 21:10:31 +08:00
/*
exec_read_io_buffer is only called on jobs that have
exited , and will therefore never block . But a broken
pipe seems to cause some flags to reset , causing the
EOF flag to not be set . Therefore , EAGAIN is ignored
and we exit anyway .
*/
if ( errno ! = EAGAIN )
{
debug ( 1 ,
L " An error occured while reading output from code block on fd %d " ,
d - > pipe_fd [ 0 ] ) ;
wperror ( L " exec_read_io_buffer " ) ;
}
2005-09-20 21:26:39 +08:00
break ;
}
else
{
b_append ( d - > out_buffer , b , l ) ;
}
}
}
}
2005-09-20 22:51:00 +08:00
2005-09-20 21:26:39 +08:00
io_data_t * exec_make_io_buffer ( )
{
io_data_t * buffer_redirect = malloc ( sizeof ( io_data_t ) ) ;
buffer_redirect - > io_mode = IO_BUFFER ;
buffer_redirect - > next = 0 ;
buffer_redirect - > out_buffer = malloc ( sizeof ( buffer_t ) ) ;
b_init ( buffer_redirect - > out_buffer ) ;
buffer_redirect - > fd = 1 ;
if ( pipe ( buffer_redirect - > pipe_fd ) = = - 1 )
{
debug ( 1 , PIPE_ERROR ) ;
wperror ( L " pipe " ) ;
free ( buffer_redirect - > out_buffer ) ;
free ( buffer_redirect ) ;
return 0 ;
}
2005-09-23 21:10:31 +08:00
else if ( fcntl ( buffer_redirect - > pipe_fd [ 0 ] ,
F_SETFL ,
O_NONBLOCK ) )
2005-09-20 21:26:39 +08:00
{
debug ( 1 , PIPE_ERROR ) ;
wperror ( L " fcntl " ) ;
free ( buffer_redirect - > out_buffer ) ;
free ( buffer_redirect ) ;
return 0 ;
}
return buffer_redirect ;
}
void exec_free_io_buffer ( io_data_t * io_buffer )
{
2005-09-23 21:10:31 +08:00
close_loop ( io_buffer - > pipe_fd [ 0 ] ) ;
2005-09-20 21:26:39 +08:00
/*
2005-09-20 22:51:00 +08:00
Dont free fd for writing . This should already be free ' d before calling exec_read_io_buffer on the buffer
2005-09-20 21:26:39 +08:00
*/
b_destroy ( io_buffer - > out_buffer ) ;
free ( io_buffer - > out_buffer ) ;
free ( io_buffer ) ;
}
/**
Make a copy of the specified io redirection chain , but change file redirection into fd redirection .
*/
static io_data_t * io_transmogrify ( io_data_t * in )
{
io_data_t * out ;
if ( ! in )
return 0 ;
out = malloc ( sizeof ( io_data_t ) ) ;
if ( ! out )
die_mem ( ) ;
out - > fd = in - > fd ;
out - > io_mode = IO_FD ;
out - > close_old = 1 ;
switch ( in - > io_mode )
{
/*
These redirections don ' t need transmogrification . They can be passed through .
*/
case IO_FD :
case IO_CLOSE :
case IO_BUFFER :
case IO_PIPE :
{
memcpy ( out , in , sizeof ( io_data_t ) ) ;
break ;
}
/*
Transmogrify file redirections
*/
case IO_FILE :
{
int fd ;
if ( ( fd = wopen ( in - > filename , in - > flags , 0666 ) ) = = - 1 )
{
debug ( 1 ,
FILE_ERROR ,
in - > filename ) ;
wperror ( L " open " ) ;
exit ( 1 ) ;
}
out - > old_fd = fd ;
break ;
}
}
out - > next = io_transmogrify ( in - > next ) ;
return out ;
}
/**
Free a transmogrified io chain .
*/
static void io_untransmogrify ( io_data_t * in , io_data_t * out )
{
if ( ! in )
return ;
io_untransmogrify ( in - > next , out - > next ) ;
switch ( in - > io_mode )
{
case IO_FILE :
2005-09-23 21:10:31 +08:00
close_loop ( out - > old_fd ) ;
2005-09-20 21:26:39 +08:00
break ;
}
free ( out ) ;
}
/**
Morph an io redirection chain into redirections suitable for
passing to eval , call eval , and clean up morphed redirections .
\ param def the code to evaluate
\ param block_type the type of block to push on evaluation
\ param io the io redirections to be performed on this block
*/
static int internal_exec_helper ( const wchar_t * def ,
int block_type ,
io_data_t * io )
{
int res = 0 ;
io_data_t * io_internal = io_transmogrify ( io ) ;
int is_block_old = is_block ;
is_block = 1 ;
eval ( def , io_internal , block_type ) ;
/*
io_data_t * buff = io_get ( io , 1 ) ;
if ( buff & & buff - > io_mode = = IO_BUFFER )
fwprintf ( stderr , L " block %ls produced %d bytes of output \n " ,
def ,
buff - > out_buffer - > used ) ;
*/
io_untransmogrify ( io , io_internal ) ;
job_do_notification ( ) ;
is_block = is_block_old ;
return res ;
}
/*
static void io_print ( io_data_t * io )
{
if ( ! io )
{
fwprintf ( stderr , L " \n " ) ;
return ;
}
fwprintf ( stderr , L " IO fd %d, type " ,
io - > fd ) ;
switch ( io - > io_mode )
{
case IO_PIPE :
fwprintf ( stderr , L " PIPE, data %d \n " , io - > pipe_fd [ io - > fd ? 1 : 0 ] ) ;
break ;
case IO_FD :
fwprintf ( stderr , L " FD, copy %d \n " , io - > old_fd ) ;
break ;
case IO_BUFFER :
fwprintf ( stderr , L " BUFFER \n " ) ;
break ;
default :
fwprintf ( stderr , L " OTHER \n " ) ;
}
io_print ( io - > next ) ;
}
*/
static int handle_new_child ( job_t * j , process_t * p )
{
if ( is_interactive & & ! is_subshell & & ! is_block )
{
int new_pgid = 0 ;
if ( ! j - > pgid )
{
new_pgid = 1 ;
j - > pgid = p - > pid ;
}
if ( setpgid ( p - > pid , j - > pgid ) )
{
if ( getpgid ( p - > pid ) ! = j - > pgid )
{
debug ( 1 ,
L " Could not send process %d from group %d to group %d " ,
p - > pid ,
getpgid ( p - > pid ) ,
j - > pgid ) ;
wperror ( L " setpgid " ) ;
}
}
if ( j - > fg )
{
while ( 1 )
{
if ( tcsetpgrp ( 0 , j - > pgid ) )
{
if ( errno ! = EINTR )
{
debug ( 1 , L " Could not send job %d ('%ls')to foreground " ,
j - > job_id ,
j - > command ) ;
wperror ( L " tcsetpgrp " ) ;
return - 1 ;
}
}
else
break ;
}
}
if ( j - > fg & & new_pgid )
{
while ( 1 )
{
if ( tcsetpgrp ( 0 , j - > pgid ) )
{
if ( errno ! = EINTR )
{
debug ( 1 , L " Could not send job %d ('%ls')to foreground " ,
j - > job_id ,
j - > command ) ;
wperror ( L " tcsetpgrp " ) ;
return - 1 ;
}
}
else
break ;
}
}
}
else
{
j - > pgid = getpid ( ) ;
}
return 0 ;
}
void exec ( job_t * j )
{
process_t * p ;
pid_t pid ;
int mypipe [ 2 ] ;
sigset_t chldset ;
sigemptyset ( & chldset ) ;
sigaddset ( & chldset , SIGCHLD ) ;
int skip_fork ;
/* This call is used so the global environment variable array is regenerated, if needed, before the fork. That way, we avoid a lot of duplicate work where EVERY child would need to generate it */
io_data_t pipe_read , pipe_write ;
io_data_t * tmp ;
io_data_t * io_buffer = 0 ;
2005-09-23 21:10:31 +08:00
debug ( 3 , L " Exec job %ls with id %d " , j - > command , j - > job_id ) ;
2005-09-20 21:26:39 +08:00
if ( j - > first_process - > type = = INTERNAL_EXEC )
{
/*
Do a regular launch - but without forking first . . .
*/
setup_child_process ( j ) ;
launch_process ( j - > first_process ) ;
/*
launch_process _never_ returns . . .
*/
}
pipe_read . fd = 0 ;
pipe_write . fd = 1 ;
pipe_read . io_mode = IO_PIPE ;
pipe_read . pipe_fd [ 0 ] = - 1 ;
pipe_write . io_mode = IO_PIPE ;
pipe_read . next = 0 ;
pipe_write . next = 0 ;
//fwprintf( stderr, L"Run command %ls\n", j->command );
if ( block_io )
{
if ( j - > io )
j - > io = io_add ( io_duplicate ( block_io ) , j - > io ) ;
else
j - > io = io_duplicate ( block_io ) ;
}
j - > io = io_add ( j - > io , & pipe_write ) ;
for ( p = j - > first_process ; p ; p = p - > next )
{
mypipe [ 1 ] = - 1 ;
skip_fork = 0 ;
2005-09-23 04:16:52 +08:00
if ( p - > type = = EXTERNAL )
env_export_arr ( 1 ) ;
2005-09-20 21:26:39 +08:00
/*
Set up fd : s that will be used in the pipe
*/
if ( p = = j - > first_process - > next )
{
j - > io = io_add ( j - > io , & pipe_read ) ;
}
if ( p - > next )
{
if ( pipe ( mypipe ) = = - 1 )
{
debug ( 1 , PIPE_ERROR ) ;
wperror ( L " pipe " ) ;
exit ( 1 ) ;
}
/* fwprintf( stderr,
L " Make pipe from %ls to %ls using fds %d and %d \n " ,
p - > actual_cmd ,
p - > next - > actual_cmd ,
mypipe [ 0 ] ,
mypipe [ 1 ] ) ;
*/
memcpy ( pipe_write . pipe_fd , mypipe , sizeof ( int ) * 2 ) ;
}
else
{
/*
This is the last element of the pipeline .
*/
/*
Remove the io redirection for pipe output .
*/
j - > io = io_remove ( j - > io , & pipe_write ) ;
}
switch ( p - > type )
{
case INTERNAL_FUNCTION :
{
wchar_t * * arg ;
int i ;
string_buffer_t sb ;
const wchar_t * def = function_get_definition ( p - > argv [ 0 ] ) ;
// fwprintf( stderr, L"run function %ls\n", argv[0] );
if ( def = = 0 )
{
debug ( 0 , L " Unknown function %ls " , p - > argv [ 0 ] ) ;
break ;
}
parser_push_block ( FUNCTION_CALL ) ;
if ( builtin_count_args ( p - > argv ) > 1 )
{
sb_init ( & sb ) ;
for ( i = 1 , arg = p - > argv + 1 ; * arg ; i + + , arg + + )
{
if ( i ! = 1 )
sb_append ( & sb , ARRAY_SEP_STR ) ;
sb_append ( & sb , * arg ) ;
}
env_set ( L " argv " , ( wchar_t * ) sb . buff , ENV_LOCAL ) ;
sb_destroy ( & sb ) ;
}
parser_forbid_function ( p - > argv [ 0 ] ) ;
if ( p - > next )
{
// fwprintf( stderr, L"Function %ls\n", def );
io_buffer = exec_make_io_buffer ( ) ;
j - > io = io_add ( j - > io , io_buffer ) ;
}
internal_exec_helper ( def , TOP , j - > io ) ;
parser_allow_function ( ) ;
parser_pop_block ( ) ;
break ;
}
case INTERNAL_BLOCK :
{
if ( p - > next )
{
// fwprintf( stderr, L"Block %ls\n", p->argv[0] );
io_buffer = exec_make_io_buffer ( ) ;
j - > io = io_add ( j - > io , io_buffer ) ;
}
internal_exec_helper ( p - > argv [ 0 ] , TOP , j - > io ) ;
break ;
}
case INTERNAL_BUILTIN :
{
int builtin_stdin = 0 ;
int fg ;
int close_stdin = 0 ;
if ( p = = j - > first_process )
{
io_data_t * in = io_get ( j - > io , 0 ) ;
if ( in )
{
switch ( in - > io_mode )
{
case IO_FD :
{
builtin_stdin = in - > old_fd ;
/* fwprintf( stderr,
L " Input redirection for builtin '%ls' from fd %d \n " ,
p - > argv [ 0 ] ,
in - > old_fd ) ;
*/
break ;
}
case IO_PIPE :
{
builtin_stdin = in - > pipe_fd [ 0 ] ;
break ;
}
case IO_FILE :
{
int new_fd ;
/*
fwprintf ( stderr ,
L " Input redirection for builtin from file %ls \n " ,
in - > filename ) ;
*/
new_fd = wopen ( in - > filename , in - > flags , 0666 ) ;
if ( new_fd = = - 1 )
{
wperror ( L " wopen " ) ;
}
else
{
builtin_stdin = new_fd ;
close_stdin = 1 ;
}
break ;
}
default :
{
debug ( 1 ,
L " Unknown input redirection type %d " ,
in - > io_mode ) ;
break ;
}
}
}
}
else
{
builtin_stdin = pipe_read . pipe_fd [ 0 ] ;
}
builtin_push_io ( builtin_stdin ) ;
/*
Since this may be the foreground job , and since a
builtin may execute another foreground job , we need to
pretend to suspend this job while running the builtin .
*/
builtin_out_redirect = has_fd ( j - > io , 1 ) ;
builtin_err_redirect = has_fd ( j - > io , 2 ) ;
fg = j - > fg ;
j - > fg = 0 ;
p - > status = builtin_run ( p - > argv ) ;
/*
Restore the fg flag , which is temporarily set to
false during builtin execution so as not to confuse
some job - handling builtins .
*/
j - > fg = fg ;
/* if( is_interactive )
fwprintf ( stderr , L " Builtin '%ls' finished \n " , j - > command ) ;
*/
if ( close_stdin )
{
// fwprintf( stderr, L"Close builtin_stdin\n" );
2005-09-23 21:10:31 +08:00
close_loop ( builtin_stdin ) ;
2005-09-20 21:26:39 +08:00
}
break ;
}
}
switch ( p - > type )
{
case INTERNAL_BLOCK :
case INTERNAL_FUNCTION :
{
int status = proc_get_last_status ( ) ;
/*
Handle output from a block or function . This usually
means do nothing , but in the case of pipes , we have
to buffer such io , since otherwisethe internal pipe
buffer might overflow .
*/
if ( ! io_buffer )
{
p - > completed = 1 ;
break ;
}
j - > io = io_remove ( j - > io , io_buffer ) ;
2005-09-20 22:51:00 +08:00
debug ( 3 , L " exec_read_io_buffer on block '%ls' " , p - > argv [ 0 ] ) ;
2005-09-20 21:26:39 +08:00
2005-09-20 22:51:00 +08:00
exec_read_io_buffer ( io_buffer ) ;
2005-09-20 21:26:39 +08:00
if ( io_buffer - > out_buffer - > used ! = 0 )
{
/*Temporary signal block for SIGCHLD */
sigprocmask ( SIG_BLOCK , & chldset , 0 ) ;
pid = fork ( ) ;
if ( pid = = 0 )
{
/*
This is the child process . Write out the contents of the pipeline .
*/
p - > pid = getpid ( ) ;
setup_child_process ( j ) ;
write ( io_buffer - > fd ,
io_buffer - > out_buffer - > buff ,
io_buffer - > out_buffer - > used ) ;
exit ( status ) ;
}
else if ( pid < 0 )
{
/* The fork failed. */
debug ( 0 , FORK_ERROR ) ;
wperror ( L " fork " ) ;
exit ( 1 ) ;
}
else
{
/*
This is the parent process . Store away
information on the child , and possibly fice
it control over the terminal .
*/
p - > pid = pid ;
if ( handle_new_child ( j , p ) )
return ;
}
}
exec_free_io_buffer ( io_buffer ) ;
io_buffer = 0 ;
break ;
}
case INTERNAL_BUILTIN :
{
int skip_fork = 0 ;
/*
If a builtin didn ' t produce any output , and it is not inside a pipeline , there is no need to fork
*/
skip_fork =
( ! sb_out - > used ) & &
( ! sb_err - > used ) & &
( ! p - > next ) ;
/*
If the output of a builtin is to be sent to an internal
buffer , there is no need to fork . This helps out the
performance quite a bit in complex completion code .
*/
io_data_t * io = io_get ( j - > io , 1 ) ;
int buffer_stdout = io & & io - > io_mode = = IO_BUFFER ;
if ( ( ! sb_err - > used ) & &
( ! p - > next ) & &
( sb_out - > used ) & &
( buffer_stdout ) )
{
// fwprintf( stderr, L"Skip fork of %ls\n", j->command );
char * res = wcs2str ( ( wchar_t * ) sb_out - > buff ) ;
b_append ( io - > out_buffer , res , strlen ( res ) ) ;
skip_fork = 1 ;
free ( res ) ;
}
if ( skip_fork )
{
p - > completed = 1 ;
if ( p - > next = = 0 )
{
debug ( 3 , L " Set status of %ls to %d using short circut " , j - > command , p - > status ) ;
proc_set_last_status ( p - > status ) ;
proc_set_last_status ( j - > negate ? ( p - > status ? 0 : 1 ) : p - > status ) ;
}
break ;
}
/*Temporary signal block for SIGCHLD */
sigprocmask ( SIG_BLOCK , & chldset , 0 ) ;
pid = fork ( ) ;
if ( pid = = 0 )
{
/*
This is the child process .
*/
p - > pid = getpid ( ) ;
setup_child_process ( j ) ;
if ( sb_out - > used )
fwprintf ( stdout , L " %ls " , sb_out - > buff ) ;
if ( sb_err - > used )
fwprintf ( stderr , L " %ls " , sb_err - > buff ) ;
exit ( p - > status ) ;
}
else if ( pid < 0 )
{
/* The fork failed. */
debug ( 0 , FORK_ERROR ) ;
wperror ( L " fork " ) ;
exit ( 1 ) ;
}
else
{
/*
This is the parent process . Store away
information on the child , and possibly fice
it control over the terminal .
*/
p - > pid = pid ;
if ( handle_new_child ( j , p ) )
return ;
}
break ;
}
case EXTERNAL :
{
/*Temporary signal block for SIGCHLD */
sigprocmask ( SIG_BLOCK , & chldset , 0 ) ;
// fwprintf( stderr,
// L"fork on %ls\n", j->command );
pid = fork ( ) ;
if ( pid = = 0 )
{
/*
This is the child process .
*/
p - > pid = getpid ( ) ;
setup_child_process ( j ) ;
launch_process ( p ) ;
/*
launch_process _never_ returns . . .
*/
}
else if ( pid < 0 )
{
/* The fork failed. */
debug ( 0 , FORK_ERROR ) ;
wperror ( L " fork " ) ;
exit ( 1 ) ;
}
else
{
/*
This is the parent process . Store away
information on the child , and possibly fice
it control over the terminal .
*/
p - > pid = pid ;
if ( handle_new_child ( j , p ) )
return ;
}
break ;
}
}
if ( p - > type = = INTERNAL_BUILTIN )
builtin_pop_io ( ) ;
sigprocmask ( SIG_UNBLOCK , & chldset , 0 ) ;
/*
Close the pipe the current process uses to read from the previous process_t
*/
if ( pipe_read . pipe_fd [ 0 ] > = 0 )
2005-09-23 21:10:31 +08:00
close_loop ( pipe_read . pipe_fd [ 0 ] ) ;
2005-09-20 21:26:39 +08:00
/*
Set up the pipe the next process uses to read from the current process_t
*/
if ( p - > next )
pipe_read . pipe_fd [ 0 ] = mypipe [ 0 ] ;
/*
If there is a next process , close the output end of the
pipe ( the child subprocess already has a copy of the pipe ) .
*/
if ( p - > next )
{
2005-09-23 21:10:31 +08:00
close_loop ( mypipe [ 1 ] ) ;
2005-09-20 21:26:39 +08:00
}
}
2005-09-23 21:10:31 +08:00
debug ( 3 , L " Job is constructed " ) ;
2005-09-20 21:26:39 +08:00
j - > io = io_remove ( j - > io , & pipe_read ) ;
j - > io = io_remove ( j - > io , & pipe_write ) ;
for ( tmp = block_io ; tmp ; tmp = tmp - > next )
j - > io = io_remove ( j - > io , tmp ) ;
j - > constructed = 1 ;
if ( ! j - > fg )
{
proc_last_bg_pid = j - > pgid ;
}
job_continue ( j , 0 ) ;
debug ( 3 , L " End of exec() " ) ;
}
int exec_subshell ( const wchar_t * cmd ,
array_list_t * l )
{
char * begin , * end ;
char z = 0 ;
int prev_subshell = is_subshell ;
int status , prev_status ;
io_data_t * io_buffer ;
if ( ! cmd )
{
debug ( 1 , L " Sent null command to subshell " ) ;
return 0 ;
}
is_subshell = 1 ;
io_buffer = exec_make_io_buffer ( ) ;
prev_status = proc_get_last_status ( ) ;
eval ( cmd , io_buffer , SUBST ) ;
2005-09-20 22:51:00 +08:00
debug ( 4 , L " exec_read_io_buffer on cmdsub '%ls' " , cmd ) ;
exec_read_io_buffer ( io_buffer ) ;
2005-09-20 21:26:39 +08:00
status = proc_get_last_status ( ) ;
proc_set_last_status ( prev_status ) ;
is_subshell = prev_subshell ;
b_append ( io_buffer - > out_buffer , & z , 1 ) ;
begin = end = io_buffer - > out_buffer - > buff ;
if ( l )
{
while ( 1 )
{
switch ( * end )
{
case 0 :
if ( begin ! = end )
{
wchar_t * el = str2wcs ( begin ) ;
if ( el )
al_push ( l , el ) ;
}
exec_free_io_buffer ( io_buffer ) ;
return status ;
case ' \n ' :
{
wchar_t * el ;
* end = 0 ;
el = str2wcs ( begin ) ;
al_push ( l , el ) ;
begin = end + 1 ;
break ;
}
}
end + + ;
}
}
exec_free_io_buffer ( io_buffer ) ;
return status ;
}