From 3b6d1242e54674f5b3032cb1c77972fff68f5baf Mon Sep 17 00:00:00 2001 From: Przemyslaw Czerpak Date: Tue, 13 Jan 2009 13:04:01 +0000 Subject: [PATCH] 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. --- harbour/ChangeLog | 46 +++ harbour/common.mak | 4 +- harbour/include/hbapifs.h | 10 +- harbour/include/hbextern.ch | 3 + harbour/source/rtl/Makefile | 4 +- harbour/source/rtl/hbproces.c | 731 ++++++++++++++++++++++++++++++++++ harbour/source/rtl/hbprocfn.c | 119 ++++++ 7 files changed, 914 insertions(+), 3 deletions(-) create mode 100644 harbour/source/rtl/hbproces.c create mode 100644 harbour/source/rtl/hbprocfn.c 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 ); +}