aboutsummaryrefslogtreecommitdiffhomepage
path: root/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'exec.c')
-rw-r--r--exec.c1285
1 files changed, 1285 insertions, 0 deletions
diff --git a/exec.c b/exec.c
new file mode 100644
index 00000000..e15ec9b0
--- /dev/null
+++ b/exec.c
@@ -0,0 +1,1285 @@
+/** \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"
+
+/**
+ 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 )
+ close( env_universal_server.fd );
+
+ 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 );
+ }
+
+
+ if( close(io->fd) == -1 )
+ {
+/* 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);
+ }
+ if( close( tmp ) == -1 )
+ {
+ debug( 1,
+ FD_ERROR,
+ io->fd );
+ wperror( L"close" );
+ exit(1);
+ }
+ }
+ 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 )
+ {
+ close( io->pipe_fd[0]);
+ close( io->pipe_fd[1]);
+ }
+ else
+ close( io->pipe_fd[fd_to_dup] );
+
+/*
+ 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. */
+
+ execve (wcs2str(p->actual_cmd), wcsv2strv( (const wchar_t **) p->argv), env_export_arr() );
+ 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.
+*/
+static void read_all( io_data_t *d )
+{
+
+//io_data_t *d, *prev=0;
+
+/*for( d = io; d; d=d->next )
+ {*/
+
+ if( d->io_mode == IO_BUFFER )
+ {
+
+ if( fcntl( d->pipe_fd[0], F_SETFL, 0 ) )
+ {
+ wperror( L"fcntl" );
+ return;
+ }
+
+ debug( 3, L"read_all: blocking read on fd %d", d->pipe_fd[0] );
+
+ while(1)
+ {
+ char b[4096];
+ int l;
+ l=read_blocked( d->pipe_fd[0], b, 4096 );
+ if( l==0 )
+ {
+
+/*if( prev )
+ {
+ prev->next = d->next;
+ }
+ else
+ {
+ j->io=d->next;
+ }
+ removed = true;*/
+ break;
+
+
+ }
+ else if( l<0 )
+ {
+ debug( 1,
+ L"An error occured while reading output from code block on fd %d", d->pipe_fd[0] );
+ wperror( L"read_all" );
+ break;
+ }
+ else
+ {
+ b_append( d->out_buffer, b, l );
+ }
+ }
+ }
+
+//if( !removed )
+//prev=d;
+//}
+
+//if( current_block->io )
+//fwprintf( stderr, L"read_all ended %ls\n", j->command );
+}
+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;
+ }
+ else if( fcntl( buffer_redirect->pipe_fd[0], F_SETFL, O_NONBLOCK ) )
+ {
+ 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 )
+{
+ if( close( io_buffer->pipe_fd[0] ) == -1)
+ {
+ debug( 1, PIPE_ERROR );
+ wperror( L"close" );
+
+ }
+
+ /*
+ Dont free fd for writing. This should already be free'd before calling read_all on the buffer
+ */
+// close( io_buffer->pipe_fd[1] );
+
+ 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;
+/*
+ fwprintf( stderr,
+ L"Replacing call to redirect %d to file '%ls' with call to redirect to fd %d\n",
+ in->fd,
+ in->filename,
+ 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:
+ if( close( out->old_fd ) == -1 )
+ {
+ debug( 1,
+ FILE_ERROR,
+ in->filename );
+ wperror( L"close" );
+ }
+ 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 we do not output to stdout, builtin IO will not appear.
+ I have no idea why...
+ */
+ //fwprintf( stdout, L"" );
+
+ 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 */
+ env_export_arr();
+
+ io_data_t pipe_read, pipe_write;
+ io_data_t *tmp;
+
+ io_data_t *io_buffer =0;
+
+// if( j->job_id > 3 )
+
+ debug( 2, L"Exec job %ls with id %d", j->command, j->job_id );
+
+ 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 )
+ {
+// fwprintf( stderr, L"Before\n" );
+// io_print( j->io );
+
+ if( j->io )
+ j->io = io_add( io_duplicate(block_io), j->io );
+ else
+ j->io=io_duplicate(block_io);
+
+// fwprintf( stderr, L"After\n" );
+// io_print( j->io );
+
+ }
+
+/*
+ if true;
+ read foo; read bar; read baz;
+ end <foo.txt
+*/
+
+ j->io = io_add( j->io, &pipe_write );
+
+ for (p = j->first_process; p; p = p->next)
+ {
+ mypipe[1]=-1;
+ skip_fork=0;
+
+ /*
+ Set up fd:s that will be used in the pipe
+ */
+// fwprintf( stderr, L"Create process %ls\n", p->actual_cmd );
+
+ 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" );
+ close( builtin_stdin );
+ }
+ 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 );
+
+ if( close( io_buffer->pipe_fd[1] ) == -1 )
+ {
+ debug( 1, PIPE_ERROR );
+ wperror( L"close" );
+
+ }
+
+ debug( 3, L"read_all on block '%ls'", p->argv[0] );
+
+ read_all( io_buffer );
+
+ 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 )
+ close( pipe_read.pipe_fd[0] );
+ /*
+ 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 )
+ {
+ if( close(mypipe[1]) != 0 )
+ {
+ debug( 1, PIPE_ERROR );
+ wperror( L"close" );
+ }
+ }
+ }
+
+// fwprintf( stderr, L"Job is constructedk\n" );
+
+ 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 );
+
+
+// assert( !job_is_stopped(j));
+ j->constructed = 1;
+// ggg( j->command, j->io );
+
+ 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 );
+ close( io_buffer->pipe_fd[1] );
+
+ debug( 4, L"read_all on cmdsub '%ls'", cmd );
+ read_all( io_buffer );
+
+ 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;
+}
+