bool thread_buffer_t::stop_thread() { if( m_thread_started ) { if( int code=m_thread_control.set( exec_stream_t::s_child, 0 ) ) { throw os_error_t( "thread_buffer_t::stop_thread: unable to set thread termination event", code ); } wait_result_t wait_result=m_thread_responce.wait( exec_stream_t::s_child, m_thread_termination_timeout, 0 ); if( !wait_result.ok() && !wait_result.timed_out() ) { throw os_error_t( "thread_buffer_t::stop_thread: wait for m_thread_stopped failed", wait_result.error_code() ); } if( wait_result.ok() ) { void * thread_result; if( int code=pthread_join( m_thread, &thread_result ) ) { throw os_error_t( "thread_buffer_t::stop_thread: pthread_join failed", code ); } m_thread_started=false; // check for any errors encountered in the thread if( m_error_code!=0 ) { throw os_error_t( m_error_prefix, m_error_code ); } return true; }else { return false; } } return true; }
void thread_buffer_t::start() { if( m_thread_started ) { throw exec_stream_t::error_t( "thread_buffer_t::start: thread already started" ); } m_in_buffer.clear(); m_out_buffer.clear(); m_err_buffer.clear(); int code; if( (code=m_thread_control.reset( ~0u, 0 )) || (code=m_thread_control.set( exec_stream_t::s_out|exec_stream_t::s_err, 0 ) ) ) { throw os_error_t( "thread_buffer_t::start: unable to initialize m_thread_control event", code ); } if( (code=m_thread_responce.reset( ~0u, 0 )) || (code=m_thread_responce.set( exec_stream_t::s_in, 0 )) ) { throw os_error_t( "thread_buffer_t::start: unable to initialize m_thread_responce event", code ); } m_error_prefix=""; m_error_code=0; if( int code=pthread_create( &m_thread, 0, &thread_func, this ) ) { throw os_error_t( "exec_stream_therad_t::start: pthread_create failed", code ); } m_thread_started=true; m_in_closed=false; m_in_bad=false; }
void exec_stream_t::kill() { if( m_impl->m_child_pid!=-1 ) { if( ::kill( m_impl->m_child_pid, SIGKILL )==-1 ) { throw os_error_t( "exec_stream_t::kill: kill failed" ); } // Updated via Sourceforge sansay0 comment as of 2010-08-20 // The correct way to handle killing a child process is to wait for the OS. // to release the child process resources before assigning -1 to m_child_pid.. // Assigning -1 to m_child_pid without waiting will result in a "defunct" process. // (aka zombie process) remaining in the process table.. pid_t code=waitpid( m_impl->m_child_pid, &m_impl->m_exit_code, 0 ); if( code == m_impl->m_child_pid ) { m_impl->m_child_pid=-1; m_impl->m_exit_code=0; } else if( code == -1 ) { throw os_error_t( "exec_stream_t::kill: waitpid failed" ); } } }
void thread_buffer_t::get( exec_stream_t::stream_kind_t kind, char * dst, std::size_t & size, bool & no_more ) { if( !m_thread_started ) { throw exec_stream_t::error_t( "thread_buffer_t::get: thread was not started" ); } unsigned long timeout= kind==exec_stream_t::s_out ? m_out_wait_timeout : m_err_wait_timeout; int eof_kind= kind==exec_stream_t::s_out ? s_out_eof : s_err_eof; buffer_list_t & buffer= kind==exec_stream_t::s_out ? m_out_buffer : m_err_buffer; wait_result_t wait_result=m_thread_responce.wait( kind|exec_stream_t::s_child|eof_kind, timeout, 0 ); if( !wait_result.ok() ) { throw os_error_t( "thread_buffer_t::get: wait for got_data failed", wait_result.error_code() ); } if( wait_result.is_signaled( exec_stream_t::s_child ) ) { // thread stopped - no need to synchronize if( !buffer.empty() ) { // we have data - deliver it first // when thread terminated, there is no need to synchronize buffer.get( dst, size ); no_more=false; }else { // thread terminated and we have no more data to return - report errors, if any if( m_error_code!=0 ) { throw os_error_t( m_error_prefix, m_error_code ); } // if terminated without error - signal eof size=0; no_more=true; } }else if( wait_result.is_signaled( kind|eof_kind ) ) { // thread got some data for us - grab them grab_mutex_t grab_mutex( m_mutex, 0 ); if( !grab_mutex.ok() ) { throw os_error_t( "thread_buffer_t::get: wait for mutex failed", grab_mutex.error_code() ); } if( !buffer.empty() ) { buffer.get( dst, size ); no_more=false; }else { size=0; no_more=wait_result.is_signaled( eof_kind ); } // if no data left - make the next get() wait until it arrives if( buffer.empty() ) { if( int code=m_thread_responce.reset( kind, 0 ) ) { throw os_error_t( "thread_buffer_t::get: unable to reset got_data event", code ); } } // if buffer is not too long tell the thread we want more data std::size_t buffer_limit= kind==exec_stream_t::s_out ? m_out_buffer_limit : m_err_buffer_limit; if( !buffer.full( buffer_limit ) ) { if( int code=m_thread_control.set( kind, 0 ) ) { throw os_error_t( "thread_buffer_t::get: unable to set want_data event", code ); } } } }
// set_stdhandle_t set_stdhandle_t::set_stdhandle_t( DWORD kind, HANDLE handle ) : m_kind( kind ), m_save_handle( GetStdHandle( kind ) ) { if( m_save_handle==INVALID_HANDLE_VALUE ) throw os_error_t( "set_stdhandle_t::set_stdhandle_t: GetStdHandle() failed" ); if( !SetStdHandle( kind, handle ) ) throw os_error_t( "set_stdhandle_t::set_stdhandle_t: SetStdHandle() failed" ); }
void thread_buffer_t::put( char * src, std::size_t & size, bool & no_more ) { if( !m_thread_started ) { throw exec_stream_t::error_t( "thread_buffer_t::put: thread was not started" ); } if( m_in_closed || m_in_bad ) { size=0; no_more=true; return; } // wait for both m_want_data and m_mutex wait_result_t wait_result=m_thread_responce.wait( exec_stream_t::s_in|exec_stream_t::s_child, m_in_wait_timeout, 0 ); if( !wait_result.ok() ) { // workaround for versions of libstdc++ (at least in gcc 3.1 pre) that do not intercept exceptions in operator<<( std::ostream, std::string ) m_in_bad=true; if( m_in.exceptions()&std::ios_base::badbit ) { throw os_error_t( "thread_buffer_t::put: wait for want_data failed", wait_result.error_code() ); }else { m_in.setstate( std::ios_base::badbit ); size=0; no_more=true; return; } } if( wait_result.is_signaled( exec_stream_t::s_child ) ) { // thread stopped - check for errors if( m_error_code!=0 ) { throw os_error_t( m_error_prefix, m_error_code ); } // if terminated without error - signal eof, since no one will ever write our data size=0; no_more=true; }else if( wait_result.is_signaled( exec_stream_t::s_in ) ) { // thread wants some data from us - stuff them grab_mutex_t grab_mutex( m_mutex, 0 ); if( !grab_mutex.ok() ) { throw os_error_t( "thread_buffer_t::put: wait for mutex failed", grab_mutex.error_code() ); } no_more=false; m_in_buffer.put( src, size ); // if the buffer is too long - make the next put() wait until it shrinks if( m_in_buffer.full( m_in_buffer_limit ) ) { if( int code=m_thread_responce.reset( exec_stream_t::s_in, 0 ) ) { throw os_error_t( "thread_buffer_t::put: unable to reset want_data event", code ); } } // tell the thread we got data if( !m_in_buffer.empty() ) { if( int code=m_thread_control.set( exec_stream_t::s_in, 0 ) ) { throw os_error_t( "thread_buffer_t::put: unable to set got_data event", code ); } } } }
bool exec_stream_t::close() { if( !close_in() ) { // need to close child's stdin no matter what, because otherwise "usual" child will run forever // And before closing child's stdin the writer thread should be stopped no matter what, // because it may be blocked on Write to m_in_pipe, and in that case closing m_in_pipe may block. if( !m_impl->m_in_thread.abort_thread() ) { throw exec_stream_t::error_t( "exec_stream_t::close: waiting till in_thread stops exceeded timeout" ); } // when thread is terminated abnormally, it may left child's stdin open // try to close it here CloseHandle( m_impl->m_in_pipe ); m_impl->m_in_pipe=0; } if( !m_impl->m_out_thread.stop_thread() ) { if( !m_impl->m_out_thread.abort_thread() ) { throw exec_stream_t::error_t( "exec_stream_t::close: waiting till out_thread stops exceeded timeout" ); } } if( !m_impl->m_err_thread.stop_thread() ) { if( !m_impl->m_err_thread.abort_thread() ) { throw exec_stream_t::error_t( "exec_stream_t::close: waiting till err_thread stops exceeded timeout" ); } } if( m_impl->m_out_pipe!=0 ) { if( !CloseHandle( m_impl->m_out_pipe ) ) { throw os_error_t( "exec_stream_t::close: unable to close out_pipe handle" ); } m_impl->m_out_pipe=0; } if( m_impl->m_err_pipe!=0 ) { if( !CloseHandle( m_impl->m_err_pipe ) ) { throw os_error_t( "exec_stream_t::close: unable to close err_pipe handle" ); } m_impl->m_err_pipe=0; } if( m_impl->m_child_process!=0 ) { wait_result_t wait_result=wait( m_impl->m_child_process, m_impl->m_child_timeout ); if( !wait_result.ok() & !wait_result.timed_out() ) { throw os_error_t( std::string( "exec_stream_t::close: wait for child process failed. " )+wait_result.error_message() ); } if( wait_result.ok() ) { DWORD exit_code; if( !GetExitCodeProcess( m_impl->m_child_process, &exit_code ) ) { throw os_error_t( "exec_stream_t::close: unable to get process exit code" ); } m_impl->m_exit_code=exit_code; if( !CloseHandle( m_impl->m_child_process ) ) { throw os_error_t( "exec_stream_t::close: unable to close child process handle" ); } m_impl->m_child_process=0; } } return m_impl->m_child_process==0; }
void exec_stream_t::kill() { if( m_impl->m_child_process!=0 ) { if( !TerminateProcess( m_impl->m_child_process, 0 ) ) { throw os_error_t( "exec_stream_t::kill: unable to terminate child process" ); } m_impl->m_exit_code=0; if( !CloseHandle( m_impl->m_child_process ) ) { throw os_error_t( "exec_stream_t::close: unable to close child process handle" ); } m_impl->m_child_process=0; } }
bool thread_buffer_t::abort_thread() { if( m_thread_started ) { if( int code=pthread_cancel( m_thread ) ) { throw os_error_t( "thread_buffer_t::abort_thread: pthread_cancel failed", code ); } void * thread_result; if( int code=pthread_join( m_thread, &thread_result ) ) { throw os_error_t( "thread_buffer_t::stop_thread: pthread_join failed", code ); } m_thread_started=false; } return true; }
void thread_buffer_t::put( char * const src, std::size_t & size, bool & no_more ) { if( m_direction!=dir_write ) { throw exec_stream_t::error_t( "thread_buffer_t::put: thread not started or started for reading" ); } // check thread status DWORD thread_exit_code; if( !GetExitCodeThread( m_thread, &thread_exit_code ) ) { throw os_error_t( "thread_buffer_t::get: GetExitCodeThread failed" ); } if( thread_exit_code!=STILL_ACTIVE ) { // thread terminated - check for errors check_error( m_message_prefix, m_error_code, m_error_message ); // if terminated without error - signal eof, since no one will ever write our data size=0; no_more=true; }else { // wait for both m_want_data and m_mutex wait_result_t wait_result=wait( m_want_data, m_wait_timeout ); if( !wait_result.ok() ) { check_error( "thread_buffer_t::put: wait for want_data failed", wait_result.error_code(), wait_result.error_message() ); } grab_mutex_t grab_mutex( m_mutex, m_wait_timeout ); if( !grab_mutex.ok() ) { check_error( "thread_buffer_t::put: wait for mutex failed", grab_mutex.error_code(), grab_mutex.error_message() ); } // got them - put data no_more=false; if( m_translate_crlf ) { m_buffer_list.put_translate_crlf( src, size ); }else { m_buffer_list.put( src, size ); } // if the buffer is too long - make the next put() wait until it shrinks if( m_buffer_list.full( m_buffer_limit ) ) { if( !m_want_data.reset() ) { throw os_error_t( "thread_buffer_t::put: unable to reset m_want_data event" ); } } // tell the thread we got data if( !m_buffer_list.empty() ) { if( !m_got_data.set() ) { throw os_error_t( "thread_buffer_t::put: unable to set m_got_data event" ); } } } }
// mutex_t mutex_t::mutex_t() { m_handle=CreateMutex( 0, FALSE, 0 ); if( m_handle==0 ) { throw os_error_t( "mutex_t::mutex_t: CreateMutex failed" ); } }
// event_t event_t::event_t() { m_handle=CreateEvent( 0, TRUE, FALSE, 0 ); if( m_handle==0 ) { throw os_error_t( "event_t::event_t: create event failed" ); } }
// event_t event_t::event_t() { if( int code=pthread_cond_init( &m_cond, 0 ) ) { throw os_error_t( "event_t::event_t: pthread_cond_init failed", code ); } m_state=0; }
bool exec_stream_t::close() { close_in(); if( !m_impl->m_thread.stop_thread() ) { m_impl->m_thread.abort_thread(); } m_impl->m_in_pipe.close(); m_impl->m_out_pipe.close(); m_impl->m_err_pipe.close(); if( m_impl->m_child_pid!=-1 ) { pid_t code=waitpid( m_impl->m_child_pid, &m_impl->m_exit_code, WNOHANG ); if( code==-1 ) { throw os_error_t( "exec_stream_t::close: first waitpid failed" ); }else if( code==0 ) { struct timeval select_timeout; select_timeout.tv_sec=m_impl->m_child_timeout/1000; select_timeout.tv_usec=(m_impl->m_child_timeout%1000)*1000; if( (code=select( 0, 0, 0, 0, &select_timeout ))==-1 ) { // // This code is using select as a semi-portable // msec-res sleep. For some reason it seems to // fail semi-randomly on linux. Since we're really // just sleeping here, there's no reason to throw // if it "fails". -- Alan // //throw os_error_t( "exec_stream_t::close: select failed" ); } code=waitpid( m_impl->m_child_pid, &m_impl->m_exit_code, WNOHANG ); if( code==-1 ) { throw os_error_t( "exec_stream_t::close: second waitpid failed" ); }else if( code==0 ) { return false; }else { m_impl->m_child_pid=-1; return true; } }else { m_impl->m_child_pid=-1; return true; } } return true; }
void thread_buffer_t::check_error( std::string const & message_prefix, DWORD error_code, std::string const & error_message ) { if( !error_message.empty() ) { throw exec_stream_t::error_t( message_prefix+"\n"+error_message, error_code ); }else if( error_code!=ERROR_SUCCESS ) { throw os_error_t( message_prefix, error_code ); } }
void pipe_t::open() { close(); if( pipe( m_fds )==-1 ) { throw os_error_t( "pipe_t::open(): pipe() failed" ); } m_direction=both; }
void pipe_t::close_r() { if( m_direction==both || m_direction==read ) { if( ::close( m_fds[0] )==-1 ) { throw os_error_t( "pipe_t::close_r: close failed" ); } m_direction= m_direction==both ? write : closed; } }
void pipe_t::close_r() { if( m_direction==both || m_direction==read ) { if( !CloseHandle( m_r ) ) { throw os_error_t( "pipe_t::close_r: CloseHandle failed" ); } m_direction= m_direction==both ? write : closed; } }
void pipe_t::close_w() { if( m_direction==both || m_direction==write ) { if( !CloseHandle( m_w ) ) { throw os_error_t( "pipe_t::close_w: CloseHandle failed" ); } m_direction= m_direction==both ? read : closed; } }
void pipe_t::close_w() { if( m_direction==both || m_direction==write ) { if( ::close( m_fds[1] )==-1 ) { throw os_error_t( "pipe_t::close_w: close failed" ); } m_direction= m_direction==both ? read : closed; } }
void exec_stream_t::kill() { if( m_impl->m_child_pid!=-1 ) { if( ::kill( m_impl->m_child_pid, SIGKILL )==-1 ) { throw os_error_t( "exec_stream_t::kill: kill failed" ); } m_impl->m_child_pid=-1; m_impl->m_exit_code=0; } }
bool thread_buffer_t::abort_thread() { if( m_direction!=dir_none ) { if( !TerminateThread( m_thread, 0 ) ) { throw os_error_t( "exec_steam_t::abort_thread: TerminateThread failed" ); } return check_thread_stopped(); } return true; }
bool exec_stream_t::close() { close_in(); if( !m_impl->m_thread.stop_thread() ) { m_impl->m_thread.abort_thread(); } m_impl->m_in_pipe.close(); m_impl->m_out_pipe.close(); m_impl->m_err_pipe.close(); if( m_impl->m_child_pid!=-1 ) { pid_t code=waitpid( m_impl->m_child_pid, &m_impl->m_exit_code, WNOHANG ); if( code==-1 ) { throw os_error_t( "exec_stream_t::close: first waitpid failed" ); }else if( code==0 ) { struct timeval select_timeout; select_timeout.tv_sec=m_impl->m_child_timeout/1000; select_timeout.tv_usec=(m_impl->m_child_timeout%1000)*1000; //if( (code=select( 0, 0, 0, 0, &select_timeout ))==-1 ) { while( (code=select( 0, 0, 0, 0, &select_timeout ))==-1 ) { if (errno == EINTR) continue; throw os_error_t( "exec_stream_t::close: select failed" ); } code=waitpid( m_impl->m_child_pid, &m_impl->m_exit_code, WNOHANG ); if( code==-1 ) { throw os_error_t( "exec_stream_t::close: second waitpid failed" ); }else if( code==0 ) { return false; }else { m_impl->m_child_pid=-1; return true; } }else { m_impl->m_child_pid=-1; return true; } } return true; }
void pipe_t::open() { close(); SECURITY_ATTRIBUTES sa; sa.nLength=sizeof( sa ); sa.bInheritHandle=true; sa.lpSecurityDescriptor=0; if( !CreatePipe( &m_r, &m_w, &sa, 0 ) ) throw os_error_t( "pipe_t::pipe_t: CreatePipe failed" ); m_direction=both; }
void thread_buffer_t::close_in() { if( !m_in_bad ) { m_in.flush(); } if( m_thread_started ) { if( int code=m_thread_control.set( s_in_eof, 0 ) ) { throw os_error_t( "thread_buffer_t::close_in: unable to set in_got_data event", code ); } m_in_closed=true; } }
void thread_buffer_t::start_thread( HANDLE pipe, direction_t direction ) { if( m_direction!=dir_none ) { throw exec_stream_t::error_t( "thread_buffer_t::start_thread: thread already started" ); } m_buffer_list.clear(); m_pipe=pipe; if( !m_stop_thread.reset() ) { throw os_error_t( "thread_buffer_t::start_thread: unable to initialize m_stop_thread event" ); } if( !m_got_data.reset() ) { throw os_error_t( "thread_buffer_t::start_thread: unable to initialize m_got_data event" ); } if( !m_want_data.set() ) { throw os_error_t( "thread_buffer_t::start_thread: unable to initialize m_want_data event" ); } DWORD thread_id; m_thread=CreateThread( 0, 0, direction==dir_read ? reader_thread : writer_thread, this, 0, &thread_id ); if( m_thread==0 ) { throw os_error_t( "thread_buffer_t::start_thread: unable to start thread" ); } m_direction= direction==dir_read ? dir_read : dir_write; }
bool thread_buffer_t::stop_thread() { if( m_direction!=dir_none ) { if( !m_stop_thread.set() ) { throw os_error_t( "thread_buffer_t::stop_thread: m_stop_thread.set() failed" ); } bool res=check_thread_stopped(); if( res ) { check_error( m_message_prefix, m_error_code, m_error_message ); } return res; } return true; }
void exec_stream_t::start( std::string const & program, std::string const & arguments ) { if( !close() ) { throw exec_stream_t::error_t( "exec_stream_t::start: previous child process has not yet terminated" ); } pipe_t in; pipe_t out; pipe_t err; set_stdhandle_t set_in( STD_INPUT_HANDLE, in.r() ); set_stdhandle_t set_out( STD_OUTPUT_HANDLE, out.w() ); set_stdhandle_t set_err( STD_ERROR_HANDLE, err.w() ); HANDLE cp=GetCurrentProcess(); if( !DuplicateHandle( cp, in.w(), cp, &m_impl->m_in_pipe, 0, FALSE, DUPLICATE_SAME_ACCESS ) ) { throw os_error_t( "exec_stream_t::start: unable to duplicate in handle" ); } in.close_w(); if( !DuplicateHandle( cp, out.r(), cp, &m_impl->m_out_pipe, 0, FALSE, DUPLICATE_SAME_ACCESS ) ) { throw os_error_t( "exec_stream_t::start: unable to duplicate out handle" ); } out.close_r(); if( !DuplicateHandle( cp, err.r(), cp, &m_impl->m_err_pipe, 0, FALSE, DUPLICATE_SAME_ACCESS ) ) { throw os_error_t( "exec_stream_t::start: unable to duplicate err handle" ); } err.close_r(); std::string command; command.reserve( program.size()+arguments.size()+3 ); if( program.find_first_of( " \t" )!=std::string::npos ) { command+='"'; command+=program; command+='"'; }else command=program; if( arguments.size()!=0 ) { command+=' '; command+=arguments; } STARTUPINFOA si; ZeroMemory( &si, sizeof( si ) ); si.cb=sizeof( si ); PROCESS_INFORMATION pi; ZeroMemory( &pi, sizeof( pi ) ); if( !CreateProcessA( 0, const_cast< char * >( command.c_str() ), 0, 0, TRUE, 0, 0, 0, &si, &pi ) ) { throw os_error_t( "exec_stream_t::start: CreateProcess failed.\n command line was: "+command ); } m_impl->m_child_process=pi.hProcess; m_impl->m_in_buffer.clear(); m_impl->m_out_buffer.clear(); m_impl->m_err_buffer.clear(); m_impl->m_in.clear(); m_impl->m_out.clear(); m_impl->m_err.clear(); m_impl->m_out_thread.set_read_buffer_size( STREAM_BUFFER_SIZE ); m_impl->m_out_thread.start_reader_thread( m_impl->m_out_pipe ); m_impl->m_err_thread.set_read_buffer_size( STREAM_BUFFER_SIZE ); m_impl->m_err_thread.start_reader_thread( m_impl->m_err_pipe ); m_impl->m_in_thread.start_writer_thread( m_impl->m_in_pipe ); }
// mutex_t mutex_t::mutex_t() { if( int code=pthread_mutex_init( &m_mutex, 0 ) ) { throw os_error_t( "mutex_t::mutex_t: pthread_mutex_init failed", code ); } }
void exec_stream_t::impl_t::start( std::string const & program ) { m_in_pipe.open(); m_out_pipe.open(); m_err_pipe.open(); pipe_t status_pipe; status_pipe.open(); pid_t pid=fork(); if( pid==-1 ) { throw os_error_t( "exec_stream_t::start: fork failed" ); }else if( pid==0 ) { try { status_pipe.close_r(); if( fcntl( status_pipe.w(), F_SETFD, FD_CLOEXEC )==-1 ) { throw os_error_t( "exec_stream_t::start: unable to fcnth( status_pipe, F_SETFD, FD_CLOEXEC ) in child process" ); } m_in_pipe.close_w(); m_out_pipe.close_r(); m_err_pipe.close_r(); if( ::close( 0 )==-1 ) { throw os_error_t( "exec_stream_t::start: unable to close( 0 ) in child process" ); } if( fcntl( m_in_pipe.r(), F_DUPFD, 0 )==-1 ) { throw os_error_t( "exec_stream_t::start: unable to fcntl( .., F_DUPFD, 0 ) in child process" ); } if( ::close( 1 )==-1 ) { throw os_error_t( "exec_stream_t::start: unable to close( 1 ) in child process" ); } if( fcntl( m_out_pipe.w(), F_DUPFD, 1 )==-1 ) { throw os_error_t( "exec_stream_t::start: unable to fcntl( .., F_DUPFD, 1 ) in child process" ); } if( ::close( 2 )==-1 ) { throw os_error_t( "exec_stream_t::start: unable to close( 2 ) in child process" ); } if( fcntl( m_err_pipe.w(), F_DUPFD, 2 )==-1 ) { throw os_error_t( "exec_stream_t::start: unable to fcntl( .., F_DUPFD, 2 ) in child process" ); } m_in_pipe.close_r(); m_out_pipe.close_w(); m_err_pipe.close_w(); if( execvp( m_child_args.data(), m_child_argp.data() )==-1 ) { throw os_error_t( "exec_stream_t::start: exec in child process failed. "+program ); } throw exec_stream_t::error_t( "exec_stream_t::start: exec in child process returned" ); }catch( std::exception const & e ) { const char * msg=e.what(); std::size_t len=strlen( msg ); write( status_pipe.w(), &len, sizeof( len ) ); write( status_pipe.w(), msg, len ); _exit( -1 ); }catch( ... ) { const char * msg="exec_stream_t::start: unknown exception in child process"; std::size_t len=strlen( msg ); write( status_pipe.w(), &len, sizeof( len ) ); write( status_pipe.w(), msg, len ); _exit( 1 ); } }else { m_child_pid=pid; status_pipe.close_w(); fd_set status_fds; FD_ZERO( &status_fds ); FD_SET( status_pipe.r(), &status_fds ); struct timeval timeout; timeout.tv_sec=3; timeout.tv_usec=0; if( select( status_pipe.r()+1, &status_fds, 0, 0, &timeout )==-1 ) { throw os_error_t( "exec_stream_t::start: select on status_pipe failed" ); } if( !FD_ISSET( status_pipe.r(), &status_fds ) ) { throw os_error_t( "exec_stream_t::start: timeout while waiting for child to report via status_pipe" ); } std::size_t status_len; int status_nread=read( status_pipe.r(), &status_len, sizeof( status_len ) ); // when all ok, status_pipe is closed on child's exec, and nothing is written to it if( status_nread!=0 ) { // otherwize, check what went wrong. if( status_nread==-1 ) { throw os_error_t( "exec_stream_t::start: read from status pipe failed" ); }else if( status_nread!=sizeof( status_len ) ) { throw os_error_t( "exec_stream_t::start: unable to read length of status message from status_pipe" ); } std::string status_msg; if( status_len!=0 ) { buf_t< char > status_buf; status_buf.new_data( status_len ); status_nread=read( status_pipe.r(), status_buf.data(), status_len ); if( status_nread==-1 ) { throw os_error_t( "exec_stream_t::start: readof status message from status pipe failed" ); } status_msg.assign( status_buf.data(), status_len ); } throw exec_stream_t::error_t( "exec_stream_t::start: error in child process."+status_msg ); } status_pipe.close_r(); m_in_pipe.close_r(); m_out_pipe.close_w(); m_err_pipe.close_w(); if( fcntl( m_in_pipe.w(), F_SETFL, O_NONBLOCK )==-1 ) { throw os_error_t( "exec_stream_t::start: fcntl( in_pipe, F_SETFL, O_NONBLOCK ) failed" ); } m_in_buffer.clear(); m_out_buffer.clear(); m_err_buffer.clear(); m_in.clear(); m_out.clear(); m_err.clear(); m_thread.set_read_buffer_size( exec_stream_t::s_out, STREAM_BUFFER_SIZE ); m_thread.set_read_buffer_size( exec_stream_t::s_err, STREAM_BUFFER_SIZE ); m_thread.start(); } }