diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 9915fe70cb..f0de4f410a 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -8,6 +8,52 @@ 2008-12-31 13:59 UTC+0100 Foo Bar (foo.bar foobar.org) */ +2009-01-13 14:07 UTC+0100 Przemyslaw Czerpak (druzus/at/priv.onet.pl) + * harbour/common.mak + * harbour/source/rtl/Makefile + * harbour/include/hbapifs.h + * harbour/include/hbextern.ch + + harbour/source/rtl/hbproces.c + + harbour/source/rtl/hbprocfn.c + + added C functions hb_fsOpenProcess(), hb_fsProcessValue(), + hb_fsCloseProcess() + + added .prg functions HB_OPENPROCESS(), HB_PROCESSVALUE(), + HB_CLOSEPROCESS() + Based on xHarbour code by Giancarlo Niccolai. + Warning: it's possible that they will be changed in the future. + Please test current implementation. Now few notes about it. + Each handle returned by HB_OPENPROCESS() have to be closed + by HB_PROCESSVALUE(). Even if process is killed by HB_CLOSEPROCESS() + its handle is still open and HB_PROCESSVALUE() has to be executed. + Type of handle depends on OS. In *nixes it's process PID. In MS-Windows + it's HANDLE. HB_PROCESSVALUE() attach process exit result in *nixes + cleaning zombie processes and in Windows closing the HANDLE. If you + do not call this function then you will have resource leak. + HB_CLOSEPROCESS() only sends quite request to the process but does + not execute any cleanup code. + All communication handles returned in parameters by reference by + HB_OPENPROCESS() function have to be closed using FCLOSE() function. + If hStdOut and hStdErr are references to the same variables then + executing process stdout and stderr is redirected to the only one + pipe. When handles for stdout, stderr and stdin are not given then + executed process inherits them from parent process unless + parameter is not given. In such case they are redirected to null + device (/dev/null or NUL). + The OS2 version is not tested. Please make tests. + There is no support for DOS which is single process OS. + In WinCE builds std{out,err,in} redirecting and process detaching does + not work too. + The parameters parsing should be updated. Now MS-Windows version uses + native OS command line parsing and quoting by "" can be used for + parameters with blank characters. I do not know any escape character + which can be used to pass (") as parameter to MS-Windows application. + In all other OS-es standard bourne shell rules are used. Parameters can + be quoted by "" or '' and escape character is \. Quoting by '' disables + special meaning of escape character. In OS2 where \ is path separator + escaping and '' quoting is disabled so it works like in MS-Windows + but I do not know if parameters divided by calling process are fully + respected by low level API calls in this system. + 2009-01-12 23:35 UTC+0100 Przemyslaw Czerpak (druzus/at/priv.onet.pl) * harbour/source/rtl/cdpapi.c * harbour/source/rtl/langapi.c diff --git a/harbour/common.mak b/harbour/common.mak index 90c415d5cb..0dcfcffaf0 100644 --- a/harbour/common.mak +++ b/harbour/common.mak @@ -542,11 +542,13 @@ RTL_LIB_OBJS = \ $(OBJ_DIR)\hbgtcore$(OBJEXT) \ $(OBJ_DIR)\hbi18n$(OBJEXT) \ $(OBJ_DIR)\hbinet$(OBJEXT) \ - $(OBJ_DIR)\hbstrsh$(OBJEXT) \ + $(OBJ_DIR)\hbproces$(OBJEXT) \ + $(OBJ_DIR)\hbprocfn$(OBJEXT) \ $(OBJ_DIR)\hbrandom$(OBJEXT) \ $(OBJ_DIR)\hbregex$(OBJEXT) \ $(OBJ_DIR)\hbregexc$(OBJEXT) \ $(OBJ_DIR)\hbrunfun$(OBJEXT) \ + $(OBJ_DIR)\hbstrsh$(OBJEXT) \ $(OBJ_DIR)\hbtoken$(OBJEXT) \ $(OBJ_DIR)\hbzlib$(OBJEXT) \ $(OBJ_DIR)\idle$(OBJEXT) \ diff --git a/harbour/include/hbapifs.h b/harbour/include/hbapifs.h index 5a83b12526..e54ad8165a 100644 --- a/harbour/include/hbapifs.h +++ b/harbour/include/hbapifs.h @@ -258,7 +258,7 @@ typedef struct _HB_PATHNAMES extern HB_EXPORT void hb_fsAddSearchPath( const char * szPath, HB_PATHNAMES ** pSearchList ); extern HB_EXPORT void hb_fsFreeSearchPath( HB_PATHNAMES * pSearchList ); - + extern HB_EXPORT BOOL hb_spFile( BYTE * pFilename, BYTE * pRetPath ); extern HB_EXPORT HB_FHANDLE hb_spOpen( BYTE * pFilename, USHORT uiFlags ); extern HB_EXPORT HB_FHANDLE hb_spCreate( BYTE * pFilename, ULONG ulAttr ); @@ -289,6 +289,14 @@ extern HB_EXPORT PHB_FFIND hb_fsFindFirst( const char * pszFileName, ULONG ulAtt extern HB_EXPORT BOOL hb_fsFindNext( PHB_FFIND ffind ); extern HB_EXPORT void hb_fsFindClose( PHB_FFIND ffind ); +/* functions to create, wait and terminate processes */ +HB_FHANDLE hb_fsOpenProcess( const char *pszFilename, + HB_FHANDLE *phStdin, HB_FHANDLE *phStdout, + HB_FHANDLE *phStderr, + BOOL fDetach, ULONG *pulPID ); +int hb_fsProcessValue( HB_FHANDLE hProcess, BOOL fWait ); +BOOL hb_fsCloseProcess( HB_FHANDLE hProcess, BOOL fGentle ); + /* Misc helper functions */ extern ULONG hb_fsAttrFromRaw( ULONG raw_attr ); extern ULONG hb_fsAttrToRaw( ULONG ulAttr ); diff --git a/harbour/include/hbextern.ch b/harbour/include/hbextern.ch index 177236ebe9..3129cf1493 100644 --- a/harbour/include/hbextern.ch +++ b/harbour/include/hbextern.ch @@ -1144,6 +1144,9 @@ EXTERNAL HB_FNAMEEXISTS EXTERNAL HB_FNAMEMERGE EXTERNAL HB_FNAMESPLIT EXTERNAL HB_DIRSCAN +EXTERNAL HB_OPENPROCESS +EXTERNAL HB_PROCESSVALUE +EXTERNAL HB_CLOSEPROCESS EXTERNAL HB_GCALL EXTERNAL HB_KEYCLEAR EXTERNAL HB_KEYPUT diff --git a/harbour/source/rtl/Makefile b/harbour/source/rtl/Makefile index b4373326ac..fbb60d798c 100644 --- a/harbour/source/rtl/Makefile +++ b/harbour/source/rtl/Makefile @@ -67,11 +67,13 @@ C_SOURCES=\ hbgtcore.c \ hbi18n.c \ hbinet.c \ - hbstrsh.c \ + hbproces.c \ + hbprocfn.c \ hbrandom.c \ hbregex.c \ hbregexc.c \ hbrunfun.c \ + hbstrsh.c \ hbtoken.c \ hbzlib.c \ idle.c \ diff --git a/harbour/source/rtl/hbproces.c b/harbour/source/rtl/hbproces.c new file mode 100644 index 0000000000..5e5f1a7ed4 --- /dev/null +++ b/harbour/source/rtl/hbproces.c @@ -0,0 +1,731 @@ +/* + * $Id$ + */ + +/* + * Harbour Project source code: + * low level functions to create, wait and terminate processes + * + * Copyright 2009 Przemyslaw Czerpak + * www - http://www.harbour-project.org + * based on xHarbour code by + * Copyright 2003 Giancarlo Niccolai + * www - http://www.xharbour.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). + * + * As a special exception, the Harbour Project gives permission for + * additional uses of the text contained in its release of Harbour. + * + * The exception is that, if you link the Harbour libraries with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the Harbour library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by the Harbour + * Project under the name Harbour. If you copy code from other + * Harbour Project or Free Software Foundation releases into a copy of + * Harbour, as the General Public License permits, the exception does + * not apply to the code that you add in this way. To avoid misleading + * anyone as to the status of such modified files, you must delete + * this exception notice from them. + * + * If you write modifications of your own for Harbour, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + * + */ + +#define HB_OS_WIN_32_USED + +/* #define HB_IO_WIN_OFF */ + +#include "hbapi.h" +#include "hbapifs.h" +#include "hbvm.h" + +#if defined( HB_OS_UNIX ) +# include +# include +# include +# include +# include +#elif defined( HB_OS_OS2 ) || defined( HB_OS_UNIX ) || \ + ( defined( HB_OS_WIN_32 ) && !defined( HB_IO_WIN ) ) +# include +# include +# include +#endif + +#if defined( HB_OS_OS2 ) || defined( HB_OS_UNIX ) || \ + ( defined( HB_OS_WIN_32 ) && !defined( HB_IO_WIN ) ) + +/* convert command to argument list using standard bourne shell encoding: + * "" and '' can be used to group parameters with blank characters, + * the escape character is '\', quoting by '' disables escape character. + */ +static char ** hb_buildArgs( const char *pszFilename ) +{ + const char * src; + char ** argv, * dst, cQuote = 0; + int argc = 0; + + while( HB_ISSPACE( *pszFilename ) ) + ++pszFilename; + src = pszFilename; + while( *src ) + { +#if defined( HB_OS_UNIX ) + if( *src == '\\' && cQuote != '\'' ) + { + if( src[ 1 ] ) + ++src; + } + else +#endif + if( *src == cQuote ) + cQuote = 0; + else if( cQuote == 0 ) + { +#if defined( HB_OS_UNIX ) + if( *src == '"' || *src == '\'' ) +#else + if( *src == '"' ) +#endif + cQuote = *src; + else if( HB_ISSPACE( *src ) ) + { + while( HB_ISSPACE( src[ 1 ] ) ) + ++src; + if( src[ 1 ] ) + ++argc; + } + } + ++src; + } + dst = ( char * ) hb_xgrab( strlen( pszFilename ) + 1 ); + argv = ( char ** ) hb_xgrab( ( argc + 2 ) * sizeof( char * ) ); + argv[ 0 ] = dst; + argv[ argc + 1 ] = NULL; + argc = 0; + + cQuote = 0; + src = pszFilename; + while( *src ) + { +#if defined( HB_OS_UNIX ) + if( *src == '\\' && cQuote != '\'' ) + { + if( src[ 1 ] ) + { + *dst++ = src[ 1 ]; + ++src; + } + } + else +#endif + if( *src == cQuote ) + cQuote = 0; + else if( cQuote != 0 ) + *dst++ = *src; + else + { +#if defined( HB_OS_UNIX ) + if( *src == '"' || *src == '\'' ) +#else + if( *src == '"' ) +#endif + cQuote = *src; + else if( HB_ISSPACE( *src ) ) + { + *dst++ = '\0'; + while( HB_ISSPACE( src[ 1 ] ) ) + ++src; + if( src[ 1 ] ) + argv[ ++argc ] = dst; + } + else + *dst++ = *src; + } + ++src; + } + *dst = 0; + + return argv; +} + +static void hb_freeArgs( char ** argv ) +{ + hb_xfree( argv[ 0 ] ); + hb_xfree( argv ); +} + +#endif + +HB_FHANDLE hb_fsOpenProcess( const char *pszFilename, + HB_FHANDLE *phStdin, HB_FHANDLE *phStdout, + HB_FHANDLE *phStderr, + BOOL fDetach, ULONG *pulPID ) +{ + HB_FHANDLE hResult = FS_ERROR; + + HB_TRACE(HB_TR_DEBUG, ("hb_fsOpenProcess(%s, %p, %p, %p, %d, %p)", pszFilename, phStdin, phStdout, phStderr, fDetach, pulPID)); + +#if defined( HB_IO_WIN ) +{ + +#if defined( HB_WINCE ) +# define CreatePipe( hIn, hOut, sa, flags ) ( FALSE ) +#endif + + BOOL fError = FALSE; + HANDLE hPipes[ 6 ]; + SECURITY_ATTRIBUTES sa; + int i; + + for( i = 0; i < 6; ++i ) + hPipes[ i ] = INVALID_HANDLE_VALUE; + + memset( &sa, 0, sizeof( sa ) ); + sa.nLength = sizeof( sa ); + sa.bInheritHandle = TRUE; + + if( phStdin != NULL ) + fError = !CreatePipe( &hPipes[0], &hPipes[1], &sa, 0 ); + if( !fError && phStdout != NULL ) + fError = !CreatePipe( &hPipes[2], &hPipes[3], &sa, 0 ); + if( !fError && phStderr != NULL ) + { + if( phStdout == phStderr ) + { + hPipes[4] = hPipes[2]; + hPipes[5] = hPipes[3]; + } + else + fError = !CreatePipe( &hPipes[4], &hPipes[5], &sa, 0 ); + } + + if( fError ) + hb_fsSetIOError( FALSE, 0 ); + else + { + PROCESS_INFORMATION pi; + STARTUPINFO si; + DWORD dwFlags = 0; +#if defined(UNICODE) + LPWSTR lpCommand = hb_mbtowc( pszFilename ); +#else + char * lpCommand = hb_strdup( pszFilename ); +#endif + memset( &pi, 0, sizeof( pi ) ); + memset( &si, 0, sizeof( si ) ); + si.cb = sizeof( si ); + if( fDetach ) + { + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + si.hStdInput = hPipes[ 0 ]; + si.hStdOutput = hPipes[ 3 ]; + si.hStdError = hPipes[ 5 ]; +#ifdef DETACHED_PROCESS + dwFlags |= DETACHED_PROCESS; +#endif + } + else + { + si.dwFlags = STARTF_USESTDHANDLES; + + si.hStdInput = phStdin ? hPipes[ 0 ] : GetStdHandle( STD_INPUT_HANDLE ); + si.hStdOutput = phStdout ? hPipes[ 3 ] : GetStdHandle( STD_OUTPUT_HANDLE ); + si.hStdError = phStderr ? hPipes[ 5 ] : GetStdHandle( STD_ERROR_HANDLE ); + } + fError = ! CreateProcess( NULL, /* lpAppName */ + lpCommand, + NULL, /* lpProcessAttr */ + NULL, /* lpThreadAttr */ + TRUE, /* bInheritHandles */ + dwFlags, /* dwCreationFlags */ + NULL, /* lpEnvironment */ + NULL, /* lpCurrentDirectory */ + &si, + &pi ); + hb_fsSetIOError( !fError, 0 ); + hb_xfree( lpCommand ); + if( !fError ) + { + if( phStdin != NULL ) + { + *phStdin = ( HB_FHANDLE ) hPipes[ 1 ]; + hPipes[ 1 ] = INVALID_HANDLE_VALUE; + } + if( phStdout != NULL ) + { + *phStdout = ( HB_FHANDLE ) hPipes[ 2 ]; + hPipes[ 2 ] = INVALID_HANDLE_VALUE; + } + if( phStderr != NULL ) + { + *phStderr = ( HB_FHANDLE ) hPipes[ 4 ]; + hPipes[ 4 ] = INVALID_HANDLE_VALUE; + } + if( pulPID ) + *pulPID = pi.dwProcessId; + CloseHandle( pi.hThread ); + hResult = ( HB_FHANDLE ) pi.hProcess; + } + } + + for( i = phStdout == phStderr ? 3 : 5; i >= 0; --i ) + { + if( hPipes[ i ] != INVALID_HANDLE_VALUE ) + CloseHandle( hPipes[ i ] ); + } +} +#elif defined( HB_OS_UNIX ) +{ + BOOL fError = FALSE; + HB_FHANDLE hPipeIn [ 2 ] = { FS_ERROR, FS_ERROR }, + hPipeOut[ 2 ] = { FS_ERROR, FS_ERROR }, + hPipeErr[ 2 ] = { FS_ERROR, FS_ERROR }; + + if( phStdin != NULL ) + fError = pipe( hPipeIn ) != 0; + if( !fError && phStdout != NULL ) + fError = pipe( hPipeOut ) != 0; + if( !fError && phStderr != NULL ) + { + if( phStdout == phStderr ) + { + hPipeErr[ 0 ] = hPipeOut[ 0 ]; + hPipeErr[ 1 ] = hPipeOut[ 1 ]; + } + else + fError = pipe( hPipeErr ) != 0; + } + + if( !fError ) + { + pid_t pid = fork(); + + if( pid == -1 ) + fError = TRUE; + else if( pid != 0 ) /* parent process */ + { + if( phStdin != NULL ) + { + *phStdin = ( HB_FHANDLE ) hPipeIn[ 1 ]; + hPipeIn[ 1 ] = FS_ERROR; + } + if( phStdout != NULL ) + { + *phStdout = ( HB_FHANDLE ) hPipeOut[ 0 ]; + hPipeOut[ 0 ] = FS_ERROR; + } + if( phStderr != NULL ) + { + *phStderr = ( HB_FHANDLE ) hPipeErr[ 0 ]; + hPipeErr[ 0 ] = FS_ERROR; + } + if( pulPID ) + *pulPID = pid; + hResult = ( HB_FHANDLE ) pid; + } + else /* child process */ + { + if( fDetach && ( !phStdin || !phStdout || !phStderr ) ) + { + HB_FHANDLE hNull = open( "/dev/null", O_RDWR ); + + if( !phStdin ) + dup2( hNull, 0 ); + if( !phStdout ) + dup2( hNull, 1 ); + if( !phStderr ) + dup2( hNull, 2 ); + + if( hNull != FS_ERROR ) + close( hNull ); + } + + if( phStdin != NULL ) + dup2( hPipeIn[ 0 ], 0 ); + if( phStdout != NULL ) + dup2( hPipeOut[ 1 ], 1 ); + if( phStderr != NULL ) + dup2( hPipeErr[ 1 ], 2 ); + + /* close all non std* handles */ + { + int iMaxFD, i; + iMaxFD = sysconf( _SC_OPEN_MAX ); + if( iMaxFD < 3 ) + iMaxFD = 1024; + for( i = 3; i < iMaxFD; ++i ) + close( i ); + } + + /* reset extended process attributes */ + setuid( getuid() ); + setgid( getgid() ); + + /* execute command */ + { +#if 0 + char * argv[4]; + + argv[0] = ( char * ) "sh"; + argv[1] = ( char * ) "-c"; + argv[2] = ( char * ) pszFilename; + argv[3] = ( char * ) 0; + execv( "/bin/sh", argv ); +#else + char ** argv; + + argv = hb_buildArgs( pszFilename ); + execv( argv[ 0 ], argv ); + hb_freeArgs( argv ); +#endif + exit(1); + } + } + } + + hb_fsSetIOError( !fError, 0 ); + + if( hPipeIn[ 0 ] != FS_ERROR ) + close( hPipeIn[ 0 ] ); + if( hPipeIn[ 1 ] != FS_ERROR ) + close( hPipeIn[ 1 ] ); + if( hPipeOut[ 0 ] != FS_ERROR ) + close( hPipeOut[ 0 ] ); + if( hPipeOut[ 1 ] != FS_ERROR ) + close( hPipeOut[ 1 ] ); + if( phStdout != phStderr ) + { + if( hPipeErr[ 0 ] != FS_ERROR ) + close( hPipeErr[ 0 ] ); + if( hPipeErr[ 1 ] != FS_ERROR ) + close( hPipeErr[ 1 ] ); + } +} +#elif defined( HB_OS_OS2 ) || defined( HB_OS_WIN_32 ) +{ + +#if defined( HB_OS_WIN_32 ) + +# define pid_t int +# define _hb_pipe( e, p ) do { \ + (e) = _pipe( (p), 2048, _O_BINARY ) != 0; \ + } while( 0 ) + +#elif defined( HB_OS_OS2 ) + +# define _hb_pipe( e, p ) do { \ + (e) = pipe( (p) ) != 0; \ + if( !(e) ) \ + { \ + setmode( (p)[ 0 ], O_BINARY ); \ + setmode( (p)[ 1 ], O_BINARY ); \ + } \ + } while( 0 ) +#endif + + BOOL fError = FALSE; + HB_FHANDLE hPipeIn [ 2 ] = { FS_ERROR, FS_ERROR }, + hPipeOut[ 2 ] = { FS_ERROR, FS_ERROR }, + hPipeErr[ 2 ] = { FS_ERROR, FS_ERROR }; + + if( phStdin != NULL ) + { + _hb_pipe( fError, hPipeIn ); + } + if( !fError && phStdout != NULL ) + { + _hb_pipe( fError, hPipeOut ); + } + if( !fError && phStderr != NULL ) + { + if( phStdout == phStderr ) + { + hPipeErr[ 0 ] = hPipeOut[ 0 ]; + hPipeErr[ 1 ] = hPipeOut[ 1 ]; + } + else + { + _hb_pipe( fError, hPipeErr ); + } + } + + if( !fError ) + { + int hStdIn, hStdOut, hStdErr; + char ** argv; + pid_t pid; + + hStdIn = dup( 0 ); + hStdOut = dup( 1 ); + hStdErr = dup( 2 ); + + if( fDetach && ( !phStdin || !phStdout || !phStderr ) ) + { + HB_FHANDLE hNull = open( "NUL:", O_RDWR ); + + if( !phStdin ) + dup2( hNull, 0 ); + if( !phStdout ) + dup2( hNull, 1 ); + if( !phStderr ) + dup2( hNull, 2 ); + + if( hNull != FS_ERROR ) + close( hNull ); + } + + if( phStdin != NULL ) + dup2( hPipeIn[ 0 ], 0 ); + if( phStdout != NULL ) + dup2( hPipeOut[ 1 ], 1 ); + if( phStderr != NULL ) + dup2( hPipeErr[ 1 ], 2 ); + + argv = hb_buildArgs( pszFilename ); + +#if defined( _MSC_VER ) || defined( __LCC__ ) || \ + defined( __XCC__ ) || defined( __POCC__ ) + pid = _spawnvp( _P_NOWAIT, argv[ 0 ], argv ); +#elif defined( __MINGW32__ ) + pid = spawnvp( P_NOWAIT, argv[ 0 ], ( const char * const * ) argv ); +#else + pid = spawnvp( P_NOWAIT, argv[ 0 ], ( char * const * ) argv ); +#endif + hb_freeArgs( argv ); + + dup2( hStdIn, 0 ); + dup2( hStdOut, 1 ); + dup2( hStdErr, 2 ); + + if( pid < 0 ) + fError = TRUE; + else if( pid != 0 ) /* parent process */ + { + if( phStdin != NULL ) + { + *phStdin = ( HB_FHANDLE ) hPipeIn[ 1 ]; + hPipeIn[ 1 ] = FS_ERROR; + } + if( phStdout != NULL ) + { + *phStdout = ( HB_FHANDLE ) hPipeOut[ 0 ]; + hPipeOut[ 0 ] = FS_ERROR; + } + if( phStderr != NULL ) + { + *phStderr = ( HB_FHANDLE ) hPipeErr[ 0 ]; + hPipeErr[ 0 ] = FS_ERROR; + } + if( pulPID ) + *pulPID = pid; + hResult = ( HB_FHANDLE ) pid; + } + } + + hb_fsSetIOError( !fError, 0 ); + + if( hPipeIn[ 0 ] != FS_ERROR ) + close( hPipeIn[ 0 ] ); + if( hPipeIn[ 1 ] != FS_ERROR ) + close( hPipeIn[ 1 ] ); + if( hPipeOut[ 0 ] != FS_ERROR ) + close( hPipeOut[ 0 ] ); + if( hPipeOut[ 1 ] != FS_ERROR ) + close( hPipeOut[ 1 ] ); + if( phStdout != phStderr ) + { + if( hPipeErr[ 0 ] != FS_ERROR ) + close( hPipeErr[ 0 ] ); + if( hPipeErr[ 1 ] != FS_ERROR ) + close( hPipeErr[ 1 ] ); + } +} +#else +{ + int TODO; /* TODO: for given platform */ + + HB_SYMBOL_UNUSED( pszFileName ); + HB_SYMBOL_UNUSED( phStdin ); + HB_SYMBOL_UNUSED( phStdout ); + HB_SYMBOL_UNUSED( phStderr ); + HB_SYMBOL_UNUSED( fDetach ); + HB_SYMBOL_UNUSED( pulPID ); + + hb_fsSetError( ( USHORT ) FS_ERROR ); +} +#endif + + return hResult; +} + +int hb_fsProcessValue( HB_FHANDLE hProcess, BOOL fWait ) +{ + int iRetStatus = -1; + + HB_TRACE(HB_TR_DEBUG, ("hb_fsProcessValue(%p, %d)", ( void * ) ( HB_PTRDIFF ) hProcess, fWait)); + +#if defined( HB_IO_WIN ) +{ + BOOL fError = TRUE; + DWORD dwResult; + HANDLE hProc = ( HANDLE ) hb_fsGetOsHandle( hProcess ); + + if( hProc ) + { + hb_vmUnlock(); + dwResult = WaitForSingleObject( hProc, fWait ? INFINITE : 0 ); + if( dwResult == WAIT_OBJECT_0 ) + { + fError = !GetExitCodeProcess( hProc, &dwResult ); + iRetStatus = !fError ? ( int ) dwResult : -2; + } + hb_fsSetIOError( !fError, 0 ); + if( !fError ) + CloseHandle( hProc ); + hb_vmLock(); + } + else + hb_fsSetError( ( USHORT ) FS_ERROR ); +} +#elif defined( HB_OS_UNIX ) || ( defined( HB_OS_OS2 ) && defined( __GNUC__ ) ) +{ + int iStatus; + pid_t pid = ( pid_t ) hProcess; + + if( pid > 0 ) + { + hb_vmUnlock(); + iRetStatus = waitpid( pid, &iStatus, fWait ? 0 : WNOHANG ); + hb_fsSetIOError( iRetStatus >= 0, 0 ); +#ifdef ERESTARTSYS + if( iRetStatus < 0 && errno != ERESTARTSYS) +#else + if( iRetStatus < 0 ) +#endif + iRetStatus = -2; + else if( iRetStatus == 0 ) + iRetStatus = -1; + else + iRetStatus = WIFEXITED( iStatus ) ? WEXITSTATUS( iStatus ) : 0; + hb_vmLock(); + } + else + hb_fsSetError( ( USHORT ) FS_ERROR ); +} +#elif defined( HB_OS_OS2 ) || defined( HB_OS_WIN_32 ) +{ + int iPid = ( int ) hProcess; + + HB_SYMBOL_UNUSED( fWait ); + + if( iPid > 0 ) + { + hb_vmUnlock(); +#if defined( __BORLANDC__ ) + iPid = cwait( &iRetStatus, iPid, 0 ); +#else + iPid = _cwait( &iRetStatus, iPid, 0 ); +#endif + hb_fsSetIOError( iPid > 0, 0 ); + if( iPid != ( int ) hProcess ) + iRetStatus = -1; + hb_vmLock(); + } + else + hb_fsSetError( ( USHORT ) FS_ERROR ); +} +#else +{ + int TODO; /* TODO: for given platform */ + + HB_SYMBOL_UNUSED( hProcess ); + HB_SYMBOL_UNUSED( fWait ); + hb_fsSetError( ( USHORT ) FS_ERROR ); +} +#endif + return iRetStatus; +} + +/* Closes/kills process. The handle is still valid until you + * catch it with hb_fsProcessValue. + */ +BOOL hb_fsCloseProcess( HB_FHANDLE hProcess, BOOL fGentle ) +{ + BOOL fResult = FALSE; + + HB_TRACE(HB_TR_DEBUG, ("hb_fsCloseProcess(%p, %d)", ( void * ) ( HB_PTRDIFF ) hProcess, fGentle)); + +#if defined( HB_IO_WIN ) +{ + HANDLE hProc = ( HANDLE ) hb_fsGetOsHandle( hProcess ); + + if( hProc ) + { + if( TerminateProcess( hProc, fGentle ? 0 : 1 ) ) + fResult = TRUE; + hb_fsSetIOError( fResult, 0 ); + } + else + hb_fsSetError( ( USHORT ) FS_ERROR ); +} +#elif defined( HB_OS_UNIX ) || ( defined( HB_OS_OS2 ) && defined( __GNUC__ ) ) +{ + pid_t pid = ( pid_t ) hProcess; + if( pid > 0 ) + { + if( kill( pid, fGentle ? SIGTERM : SIGKILL ) == 0 ) + fResult = TRUE; + hb_fsSetIOError( fResult, 0 ); + } + else + hb_fsSetError( ( USHORT ) FS_ERROR ); +} +#elif defined( HB_OS_WIN_32 ) +{ + HANDLE hProc = OpenProcess( PROCESS_TERMINATE, FALSE, hProcess ); + + if( hProc ) + { + if( TerminateProcess( hProc, fGentle ? 0 : 1 ) ) + fResult = TRUE; + hb_fsSetIOError( fResult, 0 ); + CloseHandle( hProc ); + } + else + hb_fsSetError( ( USHORT ) FS_ERROR ); +} +#else +{ + int TODO; /* TODO: for given platform */ + + HB_SYMBOL_UNUSED( hProcess ); + HB_SYMBOL_UNUSED( fGentle ); + hb_fsSetError( ( USHORT ) FS_ERROR ); +} +#endif + return fResult; +} diff --git a/harbour/source/rtl/hbprocfn.c b/harbour/source/rtl/hbprocfn.c new file mode 100644 index 0000000000..db36d7eaf8 --- /dev/null +++ b/harbour/source/rtl/hbprocfn.c @@ -0,0 +1,119 @@ +/* + * $Id$ + */ + +/* + * Harbour Project source code: + * .prg level functions to create, wait and terminate processes + * + * Copyright 2009 Przemyslaw Czerpak + * www - http://www.harbour-project.org + * based on xHarbour code by + * Copyright 2003 Giancarlo Niccolai + * www - http://www.xharbour.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). + * + * As a special exception, the Harbour Project gives permission for + * additional uses of the text contained in its release of Harbour. + * + * The exception is that, if you link the Harbour libraries with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the Harbour library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by the Harbour + * Project under the name Harbour. If you copy code from other + * Harbour Project or Free Software Foundation releases into a copy of + * Harbour, as the General Public License permits, the exception does + * not apply to the code that you add in this way. To avoid misleading + * anyone as to the status of such modified files, you must delete + * this exception notice from them. + * + * If you write modifications of your own for Harbour, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + * + */ + +#include "hbapi.h" +#include "hbapifs.h" +#include "hbapierr.h" + +HB_FUNC( HB_OPENPROCESS ) +{ + const char *szName = hb_parc( 1 ); + PHB_ITEM pStdIn = hb_param( 2, HB_IT_BYREF ); + PHB_ITEM pStdOut = hb_param( 3, HB_IT_BYREF ); + PHB_ITEM pStdErr = hb_param( 4, HB_IT_BYREF ); + BOOL fDetach = hb_parl( 5 ); + HB_FHANDLE hStdIn, *phStdIn, hStdOut, *phStdOut, hStdErr, *phStdErr; + HB_FHANDLE hProcess; + ULONG ulPID; + + if( szName && + ( pStdIn || ISNIL( 2 ) ) && + ( pStdOut || ISNIL( 3 ) ) && + ( pStdErr || ISNIL( 4 ) ) && + ( ISLOG( 5 ) || ISNIL( 5 ) ) && + ( ISBYREF( 6 ) || ISNIL( 6 ) ) && + ( !pStdIn || ( pStdIn != pStdOut && pStdIn != pStdErr ) ) ) + { + phStdIn = pStdIn ? &hStdIn : NULL; + phStdOut = pStdOut ? &hStdOut : NULL; + phStdErr = pStdErr ? ( pStdOut == pStdErr ? phStdOut : &hStdErr ) : NULL; + + hProcess = hb_fsOpenProcess( szName, phStdIn, phStdOut, phStdErr, + fDetach, &ulPID ); + if( hProcess != FS_ERROR ) + { + if( phStdIn ) + hb_stornint( ( HB_NHANDLE ) *phStdIn, 2 ); + if( phStdOut ) + hb_stornint( ( HB_NHANDLE ) *phStdOut, 3 ); + if( phStdErr && phStdOut != phStdErr ) + hb_stornint( ( HB_NHANDLE ) *phStdErr, 4 ); + hb_stornint( ulPID, 6 ); + } + hb_retnint( ( HB_NHANDLE ) hProcess ); + } + else + hb_errRT_BASE_SubstR( EG_ARG, 4001, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( HB_PROCESSVALUE ) +{ + HB_FHANDLE hProcess = hb_numToHandle( hb_parnint( 1 ) ); + + if( hProcess != 0 && hProcess != FS_ERROR && ( hb_pcount() < 2 || ISLOG( 2 ) ) ) + hb_retni( hb_fsProcessValue( hProcess, hb_pcount() < 2 || hb_parl( 2 ) ) ); + else + hb_errRT_BASE_SubstR( EG_ARG, 4001, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( HB_CLOSEPROCESS ) +{ + HB_FHANDLE hProcess = hb_numToHandle( hb_parnint( 1 ) ); + + if( hProcess != 0 && hProcess != FS_ERROR && ( hb_pcount() < 2 || ISLOG( 2 ) ) ) + hb_retl( hb_fsCloseProcess( hProcess, hb_pcount() < 2 || hb_parl( 2 ) ) ); + else + hb_errRT_BASE_SubstR( EG_ARG, 4001, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +}