From 618ffae80369dbf00d505020234d3fe0c4966e85 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Tue, 3 Apr 2012 15:55:17 +0000 Subject: Process works...sorta...in windows. --- src/stable/process.cpp | 347 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 282 insertions(+), 65 deletions(-) (limited to 'src/stable/process.cpp') diff --git a/src/stable/process.cpp b/src/stable/process.cpp index a98936e..92e7086 100644 --- a/src/stable/process.cpp +++ b/src/stable/process.cpp @@ -6,23 +6,40 @@ */ #include "bu/process.h" -#include -#include #include #include -#include + +#ifndef WIN32 +#include +#include #include +#include +#include #include +#else +#include +#include +#include +#endif -#include #include "bu/config.h" -Bu::Process::Process( Flags eFlags, const char *sName, char *const argv[] ) : +Bu::Process::ProcData::ProcData() : +#ifdef WIN32 + hStdIn( NULL ), + hStdOut( NULL ), + hStdErr( NULL ) +#else iStdIn( -1 ), iStdOut( -1 ), - iStdErr( -1 ), - iPid( 0 ), + iStdErr( -1 ) +#endif +{ +} + + +Bu::Process::Process( Flags eFlags, const char *sName, char *const argv[] ) : iProcStatus( 0 ), bBlocking( true ), bStdOutEos( true ), @@ -32,10 +49,6 @@ Bu::Process::Process( Flags eFlags, const char *sName, char *const argv[] ) : } Bu::Process::Process( Flags eFlags, const char *sName, const char *argv, ...) : - iStdIn( -1 ), - iStdOut( -1 ), - iStdErr( -1 ), - iPid( 0 ), iProcStatus( 0 ), bBlocking( true ), bStdOutEos( true ), @@ -62,10 +75,6 @@ Bu::Process::Process( Flags eFlags, const char *sName, const char *argv, ...) : } Bu::Process::Process( Flags eFlags, const Bu::Process::Options &opt, const char *sName, char *const argv[] ) : - iStdIn( -1 ), - iStdOut( -1 ), - iStdErr( -1 ), - iPid( 0 ), iProcStatus( 0 ), bBlocking( true ), bStdOutEos( true ), @@ -76,10 +85,6 @@ Bu::Process::Process( Flags eFlags, const Bu::Process::Options &opt, const char } Bu::Process::Process( Flags eFlags, const Bu::Process::Options &opt, const char *sName, const char *argv, ...) : - iStdIn( -1 ), - iStdOut( -1 ), - iStdErr( -1 ), - iPid( 0 ), iProcStatus( 0 ), bBlocking( true ), bStdOutEos( true ), @@ -118,6 +123,102 @@ void Bu::Process::wait() void Bu::Process::gexec( Flags eFlags, const char *sName, char *const argv[] ) { +#ifdef WIN32 + bStdErrEos = true; + SECURITY_ATTRIBUTES saAttr; + HANDLE hChildStd_IN_Rd; + HANDLE hChildStd_IN_Wr; + HANDLE hChildStd_OUT_Rd; + HANDLE hChildStd_OUT_Wr; + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + // Create a pipe for the child process's STDOUT. + if ( ! CreatePipe( + &hChildStd_OUT_Rd, + &hChildStd_OUT_Wr, + &saAttr, 0) ) + throw "StdoutRd CreatePipe"; + + // Ensure the read handle to the pipe for STDOUT is not inherited. + if ( ! SetHandleInformation( + hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ) + throw "Stdout SetHandleInformation"; + + // Create a pipe for the child process's STDIN. + if (! CreatePipe( + &hChildStd_IN_Rd, + &hChildStd_IN_Wr, + &saAttr, 0)) + throw "Stdin CreatePipe"; + + // Ensure the write handle to the pipe for STDIN is not inherited. + if ( ! SetHandleInformation( + hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) ) + throw "Stdin SetHandleInformation"; + + //TCHAR szCmdline[] = TEXT( sName ); + //PROCESS_INFORMATION pd.piProcInfo; + STARTUPINFO siStartInfo; + BOOL bSuccess = FALSE; + + // Set up members of the PROCESS_INFORMATION structure. + ZeroMemory( &pd.piProcInfo, sizeof(PROCESS_INFORMATION) ); + + // Set up members of the STARTUPINFO structure. + // This structure specifies the STDIN and STDOUT handles for redirection. + ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdError = hChildStd_OUT_Wr; + siStartInfo.hStdOutput = hChildStd_OUT_Wr; + siStartInfo.hStdInput = hChildStd_IN_Rd; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + Bu::String sCmd; + for( char *const *arg = argv; *arg; arg++ ) + { + if( arg != argv ) + sCmd += " "; + sCmd += *arg; + } + + // Create the child process. + bSuccess = CreateProcessA( + sName, //sName, //module name + sCmd.getStr(), //(TCHAR *) sName, //szCmdline, // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + CREATE_NO_WINDOW, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &pd.piProcInfo ); // receives PROCESS_INFORMATION + + // If an error occurs, exit the application. + if ( ! bSuccess ) + { + throw Bu::ExceptionBase("Error spawning child process."); + return; + } + else + { + // Close handles to the child process and its primary thread. + // Some applications might keep these handles to monitor the status + // of the child process, for example. + + //CloseHandle(pData->pd.piProcInfo.hProcess); + CloseHandle(pd.piProcInfo.hThread); + + // Close the ends we can't use + CloseHandle( hChildStd_OUT_Wr ); + CloseHandle( hChildStd_IN_Rd ); + pd.hStdIn = hChildStd_IN_Wr; + pd.hStdOut = hChildStd_OUT_Rd; + } +#else int iaStdIn[2]; int iaStdOut[2]; int iaStdErr[2]; @@ -125,22 +226,22 @@ void Bu::Process::gexec( Flags eFlags, const char *sName, char *const argv[] ) if( eFlags & StdOut ) { pipe( iaStdOut ); - iStdOut = iaStdOut[0]; + pd.iStdOut = iaStdOut[0]; bStdOutEos = false; } if( eFlags & StdErr ) { pipe( iaStdErr ); - iStdErr = iaStdErr[0]; + pd.iStdErr = iaStdErr[0]; bStdErrEos = false; } - iStdIn = iaStdIn[1]; + pd.iStdIn = iaStdIn[1]; -// fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); +// fcntl( pd.iStdOut, F_SETFL, fcntl( pd.iStdOut, F_GETFL, 0 )|O_NONBLOCK ); - iPid = fork(); - if( iPid == 0 ) + pd.iPid = fork(); + if( pd.iPid == 0 ) { ::close( iaStdIn[1] ); dup2( iaStdIn[0], 0 ); @@ -170,48 +271,97 @@ void Bu::Process::gexec( Flags eFlags, const char *sName, char *const argv[] ) ::close( iaStdOut[1] ); if( eFlags & StdErr ) ::close( iaStdErr[1] ); +#endif } void Bu::Process::close() { - if( iPid ) +#ifdef WIN32 + if( pd.hStdIn ) + { + CloseHandle( pd.hStdIn ); + CloseHandle( pd.hStdOut ); + + pd.hStdIn = pd.hStdOut = pd.hStdErr = NULL; + + if( !TerminateProcess(pd.piProcInfo.hProcess, 1) ) + { + throw Bu::ExceptionBase("Error closing process."); + } + + GetExitCodeProcess( pd.piProcInfo.hProcess, (PDWORD)&iProcStatus ); + + CloseHandle( pd.piProcInfo.hProcess ); + pd.piProcInfo.hProcess = NULL; + } +#else + if( pd.iPid ) { - if( iStdIn > -1 ) - ::close( iStdIn ); - if( iStdOut > -1 ) - ::close( iStdOut ); - if( iStdErr > -1 ) - ::close( iStdErr ); - waitpid( iPid, &iProcStatus, 0 ); - iPid = 0; + if( pd.iStdIn > -1 ) + ::close( pd.iStdIn ); + if( pd.iStdOut > -1 ) + ::close( pd.iStdOut ); + if( pd.iStdErr > -1 ) + ::close( pd.iStdErr ); + waitpid( pd.iPid, &iProcStatus, 0 ); + pd.iPid = 0; } +#endif } void Bu::Process::closeStdIn() { - ::close( iStdIn ); - iStdIn = -1; +#ifdef WIN32 + CloseHandle( pd.hStdIn ); + pd.hStdIn = NULL; +#else + ::close( pd.iStdIn ); + pd.iStdIn = -1; +#endif } void Bu::Process::closeStdOut() { - ::close( iStdOut ); - iStdOut = -1; +#ifdef WIN32 + CloseHandle( pd.hStdOut ); + pd.hStdOut = NULL; +#else + ::close( pd.iStdOut ); + pd.iStdOut = -1; +#endif } Bu::size Bu::Process::read( void *pBuf, Bu::size nBytes ) { +#ifdef WIN32 + DWORD dwRead; + DWORD lExitCode; + BOOL bSuccess = FALSE; + DWORD dwLen = (DWORD) nBytes; + bSuccess = ReadFile( + pd.hStdOut, (char *) pBuf, + dwLen, &dwRead, NULL); +// if( dwRead < dwLen ) + { + bSuccess = GetExitCodeProcess( pd.piProcInfo.hProcess, &lExitCode ); + if( lExitCode != STILL_ACTIVE ) + { + bStdOutEos = true; + } + } + return (int32_t) dwRead; +#else if( bStdOutEos ) return 0; fd_set rfds; FD_ZERO( &rfds ); - FD_SET( iStdOut, &rfds ); + FD_SET( pd.iStdOut, &rfds ); struct timeval tv = {0, 0}; - if( ::bu_select( iStdOut+1, &rfds, NULL, NULL, &tv ) < 0 ) + if( ::bu_select( pd.iStdOut+1, &rfds, NULL, NULL, &tv ) < 0 ) throw Bu::ExceptionBase( strerror( errno ) ); - if( FD_ISSET( iStdOut, &rfds ) || bBlocking ) + if( FD_ISSET( pd.iStdOut, &rfds ) || bBlocking ) { - Bu::size nRead = TEMP_FAILURE_RETRY( ::read( iStdOut, pBuf, nBytes ) ); + Bu::size nRead = TEMP_FAILURE_RETRY( ::read( pd.iStdOut, pBuf, nBytes ) ); if( nRead == 0 ) { bStdOutEos = true; @@ -227,21 +377,33 @@ Bu::size Bu::Process::read( void *pBuf, Bu::size nBytes ) return nRead; } return 0; +#endif } Bu::size Bu::Process::readErr( void *pBuf, Bu::size nBytes ) { +#ifdef WIN32 + if( !pd.hStdErr ) + return 0; + DWORD dwRead; + BOOL bSuccess = FALSE; + DWORD dwLen = (DWORD) nBytes; + bSuccess = ReadFile( + pd.hStdErr, (char *) pBuf, + dwLen, &dwRead, NULL); + return (int32_t) dwRead; +#else if( bStdErrEos ) return 0; fd_set rfds; FD_ZERO( &rfds ); - FD_SET( iStdErr, &rfds ); + FD_SET( pd.iStdErr, &rfds ); struct timeval tv = {0, 0}; - if( ::bu_select( iStdErr+1, &rfds, NULL, NULL, &tv ) < 0 ) + if( ::bu_select( pd.iStdErr+1, &rfds, NULL, NULL, &tv ) < 0 ) throw Bu::ExceptionBase( strerror( errno ) ); - if( FD_ISSET( iStdErr, &rfds ) || bBlocking ) + if( FD_ISSET( pd.iStdErr, &rfds ) || bBlocking ) { - Bu::size nRead = TEMP_FAILURE_RETRY( ::read( iStdErr, pBuf, nBytes ) ); + Bu::size nRead = TEMP_FAILURE_RETRY( ::read( pd.iStdErr, pBuf, nBytes ) ); if( nRead == 0 ) { bStdErrEos = true; @@ -257,11 +419,23 @@ Bu::size Bu::Process::readErr( void *pBuf, Bu::size nBytes ) return nRead; } return 0; +#endif } Bu::size Bu::Process::write( const void *pBuf, Bu::size nBytes ) { - return TEMP_FAILURE_RETRY( ::write( iStdIn, pBuf, nBytes ) ); +#ifdef WIN32 + DWORD dwWritten; + BOOL bSuccess = FALSE; + DWORD dwLen = (DWORD) nBytes; + bSuccess = WriteFile( + pd.hStdIn, (const char *) pBuf, + dwLen, &dwWritten, NULL ); + FlushFileBuffers( pd.hStdIn ); + return (int32_t) dwWritten; +#else + return TEMP_FAILURE_RETRY( ::write( pd.iStdIn, pBuf, nBytes ) ); +#endif } Bu::size Bu::Process::tell() @@ -283,12 +457,16 @@ void Bu::Process::setPosEnd( Bu::size ) bool Bu::Process::isEos() { - return (iPid == 0); +#ifdef WIN32 + return (pd.piProcInfo.hProcess == NULL); +#else + return (pd.iPid == 0); +#endif } bool Bu::Process::isOpen() { - return (iPid != 0); + return !isEos(); } void Bu::Process::flush() @@ -327,21 +505,24 @@ bool Bu::Process::isBlocking() void Bu::Process::setBlocking( bool bBlocking ) { +#ifdef WIN32 +#else if( bBlocking ) { if( !bStdOutEos ) - fcntl( iStdOut, F_SETFL, fcntl(iStdOut,F_GETFL,0 )&(~O_NONBLOCK) ); + fcntl( pd.iStdOut, F_SETFL, fcntl(pd.iStdOut,F_GETFL,0 )&(~O_NONBLOCK) ); if( !bStdErrEos ) - fcntl( iStdErr, F_SETFL, fcntl(iStdErr,F_GETFL,0 )&(~O_NONBLOCK) ); + fcntl( pd.iStdErr, F_SETFL, fcntl(pd.iStdErr,F_GETFL,0 )&(~O_NONBLOCK) ); } else { if( !bStdOutEos ) - fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); + fcntl( pd.iStdOut, F_SETFL, fcntl( pd.iStdOut, F_GETFL, 0 )|O_NONBLOCK ); if( !bStdErrEos ) - fcntl( iStdErr, F_SETFL, fcntl( iStdErr, F_GETFL, 0 )|O_NONBLOCK ); + fcntl( pd.iStdErr, F_SETFL, fcntl( pd.iStdErr, F_GETFL, 0 )|O_NONBLOCK ); } this->bBlocking = bBlocking; +#endif } void Bu::Process::setSize( Bu::size ) @@ -365,70 +546,107 @@ Bu::String Bu::Process::getLocation() const void Bu::Process::select( bool &bStdOut, bool &bStdErr ) { +#ifdef WIN32 +#else fd_set rfds; FD_ZERO( &rfds ); if( !bStdOutEos ) - FD_SET( iStdOut, &rfds ); + FD_SET( pd.iStdOut, &rfds ); if( !bStdErrEos ) - FD_SET( iStdErr, &rfds ); - if( ::bu_select( iStdErr+1, &rfds, NULL, NULL, NULL ) < 0 ) + FD_SET( pd.iStdErr, &rfds ); + if( ::bu_select( pd.iStdErr+1, &rfds, NULL, NULL, NULL ) < 0 ) throw Bu::ExceptionBase( strerror( errno ) ); - if( FD_ISSET( iStdOut, &rfds ) ) + if( FD_ISSET( pd.iStdOut, &rfds ) ) bStdOut = true; else bStdOut = false; - if( FD_ISSET( iStdErr, &rfds ) ) + if( FD_ISSET( pd.iStdErr, &rfds ) ) bStdErr = true; else bStdErr = false; +#endif } bool Bu::Process::isRunning() { - if( waitpid( iPid, NULL, WNOHANG ) == iPid ) +#ifdef WIN32 + DWORD lExitCode; + GetExitCodeProcess( pd.piProcInfo.hProcess, &lExitCode ); + if( lExitCode != STILL_ACTIVE ) checkClose(); - return iPid != 0; +#else + if( waitpid( pd.iPid, NULL, WNOHANG ) == pd.iPid ) + checkClose(); +#endif + return isOpen(); } void Bu::Process::ignoreStdErr() { - if( iStdErr == -1 ) +#ifdef WIN32 +#else + if( pd.iStdErr == -1 ) return; - ::close( iStdErr ); - iStdErr = -1; + ::close( pd.iStdErr ); + pd.iStdErr = -1; bStdErrEos = true; +#endif } pid_t Bu::Process::getPid() { - return iPid; +#ifdef WIN32 + return 0; +#else + return pd.iPid; +#endif } bool Bu::Process::childExited() { +#ifdef WIN32 + return pd.piProcInfo.hProcess != NULL; +#else return WIFEXITED( iProcStatus ); +#endif } int Bu::Process::childExitStatus() { +#ifdef WIN32 + return iProcStatus; +#else return WEXITSTATUS( iProcStatus ); +#endif } bool Bu::Process::childSignaled() { +#ifdef WIN32 + return false; +#else return WIFSIGNALED( iProcStatus ); +#endif } int Bu::Process::childSignal() { +#ifdef WIN32 + return 0; +#else return WTERMSIG( iProcStatus ); +#endif } bool Bu::Process::childCoreDumped() { +#ifdef WIN32 + return false; +#else return WCOREDUMP( iProcStatus ); +#endif } void Bu::Process::checkClose() @@ -438,4 +656,3 @@ void Bu::Process::checkClose() close(); } } - -- cgit v1.2.3