Explorar o código

Merge pull request #23 from GWRon/feat_freeprocesskill

[FreeProcess] Add TProcess.Kill() to allow forceful process termination
Brucey %!s(int64=6) %!d(string=hai) anos
pai
achega
219e60035a
Modificáronse 2 ficheiros con 117 adicións e 51 borrados
  1. 49 20
      freeprocess.mod/freeprocess.bmx
  2. 68 31
      freeprocess.mod/freeprocess.c

+ 49 - 20
freeprocess.mod/freeprocess.bmx

@@ -31,7 +31,7 @@ Import brl.filesystem
 
 Import "freeprocess.c"
 
-'note: Once fdProcessStatus() returns 0 OR fdTerminateProcess() is called,
+'note: Once fdProcessStatus() returns 0 OR fdTerminateProcess()/fdKillProcess is called,
 'processhandle should be assumed to be invalid, and neither function should be called
 'again.
 Extern
@@ -43,6 +43,7 @@ Function fdAvail:Int(fd:Int)
 Function fdProcess:Int(exe$,in_fd:Int Ptr,out_fd:Int Ptr,err_fd:Int Ptr,flags:Int)="fdProcess"
 Function fdProcessStatus:Int(processhandle:Int)
 Function fdTerminateProcess:Int(processhandle:Int)
+Function fdKillProcess:Int(processhandle:Int)
 End Extern
 
 Const HIDECONSOLE:Int=1
@@ -54,11 +55,11 @@ Type TPipeStream Extends TStream
 	Field	readhandle:Int,writehandle:Int
 
 	Method Close()
-		If readhandle 
+		If readhandle
 			fdClose(readhandle)
 			readhandle=0
 		EndIf
-		If writehandle 
+		If writehandle
 			fdClose(writehandle)
 			writehandle=0
 		EndIf
@@ -71,15 +72,15 @@ Type TPipeStream Extends TStream
 	Method Write:Long( buf:Byte Ptr,count:Long )
 		Return fdWrite(writehandle,buf,count)
 	End Method
-	
+
 	Method Flush()
 		fdFlush(writehandle)
 	End Method
-		
+
 	Method ReadAvail:Int()
 		Return fdAvail(readhandle)
 	End Method
-	
+
 	Method ReadPipe:Byte[]()
 		Local	bytes:Byte[],n:Int
 		n=ReadAvail()
@@ -87,9 +88,9 @@ Type TPipeStream Extends TStream
 			bytes=New Byte[n]
 			Read(bytes,n)
 			Return bytes
-		EndIf	
+		EndIf
 	End Method
-	
+
 	Method ReadLine$()	'nonblocking - returns empty string if no data available
 		Local	n:Long,r:Long,p0:Int,p1:Int,line$
 		n=ReadAvail()
@@ -113,7 +114,7 @@ Type TPipeStream Extends TStream
 				If bufferpos MemMove(readbuffer,Varptr readbuffer[n],Size_T(bufferpos))
 				Return line$
 			EndIf
-		Next			
+		Next
 	End Method
 
 	Function Create:TPipeStream( in:Int,out:Int )
@@ -126,7 +127,7 @@ Type TPipeStream Extends TStream
 End Type
 
 Type TProcess
-	Global ProcessList:TList 
+	Global ProcessList:TList
 	Field	name$
 	Field	handle:Int
 	Field	pipe:TPipeStream
@@ -137,24 +138,24 @@ Type TProcess
 		detached = True
 		Return 1
 	End Method
-	
+
 	Method Attach:Int()
 		detached = False
 		Return 1
 	End Method
 	Method Status:Int()
-		If handle 
+		If handle
 			If fdProcessStatus(handle) Return 1
 			handle=0
 		EndIf
 		Return 0
 	End Method
-	
+
 	Method Close()
 		If pipe pipe.Close;pipe=Null
 		If err err.Close;err=Null
 	End Method
-	
+
 	Method Terminate:Int()
 		Local res:Int
 		If handle
@@ -164,9 +165,20 @@ Type TProcess
 		Return res
 	End Method
 
+	'the less nicer version of "terminate()" as it does not allow
+	'the process to finish its stuff
+	Method Kill:Int()
+		Local res:Int
+		If handle
+			res=fdKillProcess( handle )
+			handle=0
+		EndIf
+		Return res
+	End Method
+
 	Function Create:TProcess(name$,flags:Int)
 		Local	p:TProcess
-		Local	infd:Int,outfd:Int,errfd:Int	
+		Local	infd:Int,outfd:Int,errfd:Int
 ?MacOS
 		If FileType(name)=2
 			Local a$=StripExt(StripDir(name))
@@ -180,12 +192,12 @@ Type TProcess
 		If Not p.handle Return Null
 		p.pipe=TPipeStream.Create(infd,outfd)
 		p.err=TPipeStream.Create(errfd,0)
-		p.detached = False 
+		p.detached = False
 		If Not ProcessList ProcessList=New TList
 		ProcessList.AddLast p
 		Return p
 	End Function
-	
+
 	Function FlushZombies()
 		If Not ProcessList Return
 		Local live:TList=New TList
@@ -194,17 +206,26 @@ Type TProcess
 		Next
 		ProcessList=live
 	End Function
-	
+
 	Function TerminateAll() NoDebug
 		If Not ProcessList Return
 		For Local p:TProcess=EachIn ProcessList
-			If p.detached = False Then 
+			If p.detached = False Then
 				p.Terminate
 			EndIf
 		Next
 		ProcessList=Null
 	End Function
-	
+
+	Function KillAll() NoDebug
+		If Not ProcessList Return
+		For Local p:TProcess=EachIn ProcessList
+			If p.detached = False Then
+				p.Kill
+			EndIf
+		Next
+		ProcessList=Null
+	End Function
 End Type
 
 Rem
@@ -245,4 +266,12 @@ Function TerminateProcess:Int(process:TProcess)
 	Return process.Terminate()
 End Function
 
+Rem
+bbdoc: Forcefully End Process
+returns: 1 if forceful termination of program was successful and 0 otherwise.
+End Rem
+Function KillProcess:Int(process:TProcess)
+	Return process.Kill()
+End Function
+
 OnEnd TProcess.TerminateAll

+ 68 - 31
freeprocess.mod/freeprocess.c

@@ -39,16 +39,30 @@ int fdTerminateProcess(int pid){
 	return -1;
 }
 
+
+//returns 0 for success, -1 for error
+//
+int fdKillProcess(int pid){
+
+	if( !killpg( pid,SIGKILL ) ){
+		int status=0;
+		waitpid( pid,&status,0 );
+		return 0;
+	}
+	return -1;
+}
+
+
 static char **makeargv( const char *cmd ){
 	int n,c;
 	char *p;
 	static char *args,**argv;
-	
+
 	if( args ) free( args );
 	if( argv ) free( argv );
 	args=(char*)malloc( strlen(cmd)+1 );
 	strcpy( args,cmd );
-	
+
 	n=0;
 	p=args;
 	while( c=*p++ ){
@@ -91,17 +105,17 @@ int fdProcess( BBString *bbcmd,int *procin,int *procout,int *procerr,int flags)
 {
 	char 	*const*argv;
 	int   	procid;
-	
+
 	const char *cmd=bbTmpUTF8String(bbcmd);
-	
+
 	//Set-up interprocess communication
 	if (pipe(in)) return 0;
   	if (pipe(out)) return 0;
   	if (pipe(errfd)) return 0;
-	
+
 	//Fork process (returned value used to distinguish between child and parent process)
 	procid=vfork();	//vfork() avoids memory overhead of fork()
-	
+
 	//Child process
 	if (procid==0)
 	{
@@ -110,37 +124,37 @@ int fdProcess( BBString *bbcmd,int *procin,int *procout,int *procerr,int flags)
 		#else
 			setpgid(0,0);	//but OS X doesn't like it, therefore resort to using setpgid().
 		#endif
-		
+
 		dup2(out[PIPEREAD],STDIN_FILENO);
 		close(out[PIPEWRITE]);
-		
+
 		dup2(in[PIPEWRITE],STDOUT_FILENO);
 		close(in[PIPEREAD]);
-		
-		dup2(errfd[PIPEWRITE],STDERR_FILENO);		
+
+		dup2(errfd[PIPEWRITE],STDERR_FILENO);
 		close(errfd[PIPEREAD]);
-		
+
 		argv=makeargv(cmd);
 		execvp(argv[0],argv);
-		
+
 		_exit( -1 );
-		
+
 		return 0;	//Supposedly, we need this for some compilers.
-		
+
 	}
-	
+
 	//Parent process
-	
+
 	if(procid==-1) return 0;	//Return if child process couldn't be started.
-	
+
 	close(out[PIPEREAD]);		//Close the end of the pipes in that the child
 	close(in[PIPEWRITE]);		//process is using.
 	close(errfd[PIPEWRITE]);
-	
+
 	*procin=in[PIPEREAD];		//And return the end of the pipes that we should
 	*procout=out[PIPEWRITE];	//be using.
 	*procerr=errfd[PIPEREAD];
-	
+
 	return procid;
 }
 
@@ -155,6 +169,17 @@ extern int _bbusew;
 #include <tlhelp32.h>
 
 int TerminateProcessGroup(HANDLE prochandle,int procid)
+{
+	//for now we just do a kill instead of politely asking all
+	//sub-windows (gui only) to gracefully end.
+	return KillProcessGroup(prochandle, procid);
+}
+
+
+// In the Windows world "TerminateProcess" kills a process without
+// gracefully asking to stop I/O and other operations first.
+// So there is a name clash between Linux' SIGKILL and Windows' TERMINATE
+int KillProcessGroup(HANDLE prochandle,int procid)
 {
 	HANDLE snapshot,child;
 	PROCESSENTRY32 procinfo;
@@ -192,7 +217,7 @@ int fdClose(int fd)
 
 BBLONG fdRead(int fd,char *buffer,BBLONG bytes)
 {
-	int		res; 
+	int		res;
 	long	count;
 	res=ReadFile((HANDLE)fd,buffer,bytes,&count,0);
 	if (res) return count;
@@ -215,7 +240,7 @@ int fdFlush(int fd)
 	return res;
 }
 
-int fdAvail(int fd) 
+int fdAvail(int fd)
 {
 	int		res;
 	long	avail;
@@ -228,13 +253,13 @@ int fdAvail(int fd)
 int fdProcessStatus( int pid ){
 
 	PROCESS_INFORMATION *pi=(PROCESS_INFORMATION *)pid;
-	
+
 	long exitcode;
-	
+
 	if( GetExitCodeProcess( pi->hProcess,&exitcode ) ){
 
 		if( exitcode==STILL_ACTIVE ) return 1;
-		
+
 		CloseHandle( pi->hProcess );
 		free( pi );
 	}
@@ -245,9 +270,21 @@ int fdProcessStatus( int pid ){
 int fdTerminateProcess( int pid ){
 
 	PROCESS_INFORMATION *pi=(PROCESS_INFORMATION *)pid;
-	
+
 	int res=TerminateProcessGroup( pi->hProcess,pi->dwProcessId );
-	
+
+	CloseHandle( pi->hProcess );
+	free( pi );
+
+	return res;
+}
+
+//returns 0 for success
+int fdKillProcess( int pid ){
+	PROCESS_INFORMATION *pi=(PROCESS_INFORMATION *)pid;
+
+	int res=KillProcessGroup( pi->hProcess,pi->dwProcessId );
+
 	CloseHandle( pi->hProcess );
 	free( pi );
 
@@ -286,7 +323,7 @@ int fdProcess( BBString *cmd,int *procin,int *procout,int *procerr,int flags)
 	}
 
 	pi=(PROCESS_INFORMATION*)calloc(1,sizeof(PROCESS_INFORMATION));
-	
+
 	if( _bbusew ){
 		STARTUPINFOW si={sizeof(si)};
 
@@ -320,7 +357,7 @@ int fdProcess( BBString *cmd,int *procin,int *procout,int *procerr,int flags)
 		}
 		res=CreateProcess( 0,bbTmpCString(cmd),0,0,-1,pflags,0,0,&si,pi );
 	}
-	
+
 	if( !res ){
 		CloseHandle( istr );
 		CloseHandle( ostr );
@@ -330,9 +367,9 @@ int fdProcess( BBString *cmd,int *procin,int *procout,int *procerr,int flags)
 		CloseHandle( p_estr );
 		return 0;
 	}
-	
+
 	CloseHandle( pi->hThread );
-	
+
 	*procin=(int)istr;
 	*procout=(int)ostr;
 	*procerr=(int)estr;
@@ -340,7 +377,7 @@ int fdProcess( BBString *cmd,int *procin,int *procout,int *procerr,int flags)
 	CloseHandle( p_istr );
 	CloseHandle( p_ostr );
 	CloseHandle( p_estr );
-	
+
 	return (int)pi;
 }