Commit 6c4ab7ec authored by Brad King's avatar Brad King
Browse files

ENH: Adding native windows process tree kill to ProcessWin32.c. This replaces...

ENH: Adding native windows process tree kill to ProcessWin32.c.  This replaces the ProcessWin32Kill.c implementation.
parent dd70c8f8
......@@ -274,8 +274,6 @@ IF(KWSYS_USE_Process)
SET_SOURCE_FILES_PROPERTIES(
${PROJECT_BINARY_DIR}/${KWSYS_NAMESPACE}ProcessFwd9xEnc.c
PROPERTIES GENERATED 1)
SET(KWSYS_H_FILES ${KWSYS_H_FILES} ProcessWin32Kill)
SET(KWSYS_SRCS ${KWSYS_SRCS} ProcessWin32Kill.c)
ELSE(NOT UNIX)
# Use the UNIX implementation.
SET(KWSYS_SRCS ${KWSYS_SRCS} ProcessUNIX.c)
......
......@@ -14,7 +14,6 @@
#define KWSYS_IN_PROCESS_C
#include "kwsysPrivate.h"
#include KWSYS_HEADER(Process.h)
#include KWSYS_HEADER(ProcessWin32Kill.h)
/*
......@@ -102,6 +101,7 @@ static int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2);
static kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2);
static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2);
static void kwsysProcessSetExitException(kwsysProcess* cp, int code);
static void kwsysProcessKillTree(int pid);
extern kwsysEXPORT int kwsysEncodedWriteArrayProcessFwd9x(const char* fname);
/*--------------------------------------------------------------------------*/
......@@ -1405,10 +1405,7 @@ void kwsysProcess_Kill(kwsysProcess* cp)
/* Not Windows 9x. Just terminate the children. */
for(i=0; i < cp->NumberOfCommands; ++i)
{
if(!kwsysProcessWin32Kill(cp->ProcessInformation[i].dwProcessId))
{
TerminateProcess(cp->ProcessInformation[i].hProcess, 255);
}
kwsysProcessKillTree(cp->ProcessInformation[i].dwProcessId);
}
}
......@@ -2141,3 +2138,404 @@ static void kwsysProcessSetExitException(kwsysProcess* cp, int code)
}
#undef KWSYSPE_CASE
typedef struct kwsysProcess_List_s kwsysProcess_List;
static kwsysProcess_List* kwsysProcess_List_New();
static void kwsysProcess_List_Delete(kwsysProcess_List* self);
static int kwsysProcess_List_Update(kwsysProcess_List* self);
static int kwsysProcess_List_NextProcess(kwsysProcess_List* self);
static int kwsysProcess_List_GetCurrentProcessId(kwsysProcess_List* self);
static int kwsysProcess_List_GetCurrentParentId(kwsysProcess_List* self);
/*--------------------------------------------------------------------------*/
/* Windows NT 4 API definitions. */
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
typedef LONG NTSTATUS;
typedef LONG KPRIORITY;
typedef struct _UNICODE_STRING UNICODE_STRING;
struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
};
/* The process information structure. Declare only enough to get
process identifiers. The rest may be ignored because we use the
NextEntryDelta to move through an array of instances. */
typedef struct _SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION;
typedef SYSTEM_PROCESS_INFORMATION* PSYSTEM_PROCESS_INFORMATION;
struct _SYSTEM_PROCESS_INFORMATION
{
ULONG NextEntryDelta;
ULONG ThreadCount;
ULONG Reserved1[6];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ProcessName;
KPRIORITY BasePriority;
ULONG ProcessId;
ULONG InheritedFromProcessId;
};
/*--------------------------------------------------------------------------*/
/* Toolhelp32 API definitions. */
#define TH32CS_SNAPPROCESS 0x00000002
typedef struct tagPROCESSENTRY32 PROCESSENTRY32;
typedef PROCESSENTRY32* LPPROCESSENTRY32;
struct tagPROCESSENTRY32
{
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
DWORD th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
char szExeFile[MAX_PATH];
};
/*--------------------------------------------------------------------------*/
/* Windows API function types. */
typedef HANDLE (WINAPI* CreateToolhelp32SnapshotType)(DWORD, DWORD);
typedef BOOL (WINAPI* Process32FirstType)(HANDLE, LPPROCESSENTRY32);
typedef BOOL (WINAPI* Process32NextType)(HANDLE, LPPROCESSENTRY32);
typedef NTSTATUS (WINAPI* ZwQuerySystemInformationType)(ULONG, PVOID,
ULONG, PULONG);
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List__New_NT4(kwsysProcess_List* self);
static int kwsysProcess_List__New_Snapshot(kwsysProcess_List* self);
static void kwsysProcess_List__Delete_NT4(kwsysProcess_List* self);
static void kwsysProcess_List__Delete_Snapshot(kwsysProcess_List* self);
static int kwsysProcess_List__Update_NT4(kwsysProcess_List* self);
static int kwsysProcess_List__Update_Snapshot(kwsysProcess_List* self);
static int kwsysProcess_List__Next_NT4(kwsysProcess_List* self);
static int kwsysProcess_List__Next_Snapshot(kwsysProcess_List* self);
static int kwsysProcess_List__GetProcessId_NT4(kwsysProcess_List* self);
static int kwsysProcess_List__GetProcessId_Snapshot(kwsysProcess_List* self);
static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self);
static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self);
struct kwsysProcess_List_s
{
/* Implementation switches at runtime based on version of Windows. */
int NT4;
/* Implementation functions and data for NT 4. */
ZwQuerySystemInformationType P_ZwQuerySystemInformation;
char* Buffer;
int BufferSize;
PSYSTEM_PROCESS_INFORMATION CurrentInfo;
/* Implementation functions and data for other Windows versions. */
CreateToolhelp32SnapshotType P_CreateToolhelp32Snapshot;
Process32FirstType P_Process32First;
Process32NextType P_Process32Next;
HANDLE Snapshot;
PROCESSENTRY32 CurrentEntry;
};
/*--------------------------------------------------------------------------*/
static kwsysProcess_List* kwsysProcess_List_New()
{
OSVERSIONINFO osv;
kwsysProcess_List* self;
/* Allocate and initialize the list object. */
if(!(self = (kwsysProcess_List*)malloc(sizeof(kwsysProcess_List))))
{
return 0;
}
memset(self, 0, sizeof(*self));
/* Select an implementation. */
ZeroMemory(&osv, sizeof(osv));
osv.dwOSVersionInfoSize = sizeof(osv);
GetVersionEx(&osv);
self->NT4 = (osv.dwPlatformId == VER_PLATFORM_WIN32_NT &&
osv.dwMajorVersion < 5)? 1:0;
/* Initialize the selected implementation. */
if(!(self->NT4?
kwsysProcess_List__New_NT4(self) :
kwsysProcess_List__New_Snapshot(self)))
{
kwsysProcess_List_Delete(self);
return 0;
}
/* Update to the current set of processes. */
if(!kwsysProcess_List_Update(self))
{
kwsysProcess_List_Delete(self);
return 0;
}
return self;
}
/*--------------------------------------------------------------------------*/
static void kwsysProcess_List_Delete(kwsysProcess_List* self)
{
if(self)
{
if(self->NT4)
{
kwsysProcess_List__Delete_NT4(self);
}
else
{
kwsysProcess_List__Delete_Snapshot(self);
}
free(self);
}
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List_Update(kwsysProcess_List* self)
{
return self? (self->NT4?
kwsysProcess_List__Update_NT4(self) :
kwsysProcess_List__Update_Snapshot(self)) : 0;
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List_GetCurrentProcessId(kwsysProcess_List* self)
{
return self? (self->NT4?
kwsysProcess_List__GetProcessId_NT4(self) :
kwsysProcess_List__GetProcessId_Snapshot(self)) : -1;
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List_GetCurrentParentId(kwsysProcess_List* self)
{
return self? (self->NT4?
kwsysProcess_List__GetParentId_NT4(self) :
kwsysProcess_List__GetParentId_Snapshot(self)) : -1;
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List_NextProcess(kwsysProcess_List* self)
{
return (self? (self->NT4?
kwsysProcess_List__Next_NT4(self) :
kwsysProcess_List__Next_Snapshot(self)) : 0);
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List__New_NT4(kwsysProcess_List* self)
{
HANDLE hNT = GetModuleHandle("ntdll.dll");
if(hNT)
{
/* Get pointers to the needed API functions. */
self->P_ZwQuerySystemInformation =
((ZwQuerySystemInformationType)
GetProcAddress(hNT, "ZwQuerySystemInformation"));
CloseHandle(hNT);
}
if(!self->P_ZwQuerySystemInformation)
{
return 0;
}
/* Allocate an initial process information buffer. */
self->BufferSize = 32768;
self->Buffer = (char*)malloc(self->BufferSize);
return self->Buffer? 1:0;
}
/*--------------------------------------------------------------------------*/
static void kwsysProcess_List__Delete_NT4(kwsysProcess_List* self)
{
/* Free the process information buffer. */
if(self->Buffer)
{
free(self->Buffer);
}
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List__Update_NT4(kwsysProcess_List* self)
{
self->CurrentInfo = 0;
while(1)
{
/* Query number 5 is for system process list. */
NTSTATUS status =
self->P_ZwQuerySystemInformation(5, self->Buffer, self->BufferSize, 0);
if(status == STATUS_INFO_LENGTH_MISMATCH)
{
/* The query requires a bigger buffer. */
int newBufferSize = self->BufferSize * 2;
char* newBuffer = (char*)malloc(newBufferSize);
if(newBuffer)
{
free(self->Buffer);
self->Buffer = newBuffer;
self->BufferSize = newBufferSize;
}
else
{
return 0;
}
}
else if(status >= 0)
{
/* The query succeeded. Initialize traversal of the process list. */
self->CurrentInfo = (PSYSTEM_PROCESS_INFORMATION)self->Buffer;
return 1;
}
else
{
/* The query failed. */
return 0;
}
}
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List__Next_NT4(kwsysProcess_List* self)
{
if(self->CurrentInfo)
{
if(self->CurrentInfo->NextEntryDelta > 0)
{
self->CurrentInfo = ((PSYSTEM_PROCESS_INFORMATION)
((char*)self->CurrentInfo +
self->CurrentInfo->NextEntryDelta));
return 1;
}
self->CurrentInfo = 0;
}
return 0;
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List__GetProcessId_NT4(kwsysProcess_List* self)
{
return self->CurrentInfo? self->CurrentInfo->ProcessId : -1;
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self)
{
return self->CurrentInfo? self->CurrentInfo->InheritedFromProcessId : -1;
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List__New_Snapshot(kwsysProcess_List* self)
{
HANDLE hKernel = GetModuleHandle("kernel32.dll");
if(hKernel)
{
self->P_CreateToolhelp32Snapshot =
((CreateToolhelp32SnapshotType)
GetProcAddress(hKernel, "CreateToolhelp32Snapshot"));
self->P_Process32First =
((Process32FirstType)
GetProcAddress(hKernel, "Process32First"));
self->P_Process32Next =
((Process32NextType)
GetProcAddress(hKernel, "Process32Next"));
CloseHandle(hKernel);
}
return (self->P_CreateToolhelp32Snapshot &&
self->P_Process32First &&
self->P_Process32Next)? 1:0;
}
/*--------------------------------------------------------------------------*/
static void kwsysProcess_List__Delete_Snapshot(kwsysProcess_List* self)
{
if(self->Snapshot)
{
CloseHandle(self->Snapshot);
}
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List__Update_Snapshot(kwsysProcess_List* self)
{
if(self->Snapshot)
{
CloseHandle(self->Snapshot);
}
if(!(self->Snapshot =
self->P_CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)))
{
return 0;
}
ZeroMemory(&self->CurrentEntry, sizeof(self->CurrentEntry));
self->CurrentEntry.dwSize = sizeof(self->CurrentEntry);
if(!self->P_Process32First(self->Snapshot, &self->CurrentEntry))
{
CloseHandle(self->Snapshot);
self->Snapshot = 0;
return 0;
}
return 1;
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List__Next_Snapshot(kwsysProcess_List* self)
{
if(self->Snapshot)
{
if(self->P_Process32Next(self->Snapshot, &self->CurrentEntry))
{
return 1;
}
CloseHandle(self->Snapshot);
self->Snapshot = 0;
}
return 0;
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List__GetProcessId_Snapshot(kwsysProcess_List* self)
{
return self->Snapshot? self->CurrentEntry.th32ProcessID : -1;
}
/*--------------------------------------------------------------------------*/
static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self)
{
return self->Snapshot? self->CurrentEntry.th32ParentProcessID : -1;
}
/*--------------------------------------------------------------------------*/
static void kwsysProcessKill(DWORD pid)
{
HANDLE h = OpenProcess(PROCESS_TERMINATE, 0, pid);
if(h)
{
TerminateProcess(h, 255);
WaitForSingleObject(h, INFINITE);
}
}
/*--------------------------------------------------------------------------*/
static void kwsysProcessKillTree(int pid)
{
kwsysProcess_List* plist = kwsysProcess_List_New();
kwsysProcessKill(pid);
if(plist)
{
do
{
if(kwsysProcess_List_GetCurrentParentId(plist) == pid)
{
int ppid = kwsysProcess_List_GetCurrentProcessId(plist);
kwsysProcessKillTree(ppid);
}
} while(kwsysProcess_List_NextProcess(plist));
kwsysProcess_List_Delete(plist);
}
}
/*=========================================================================
Program: KWSys - Kitware System Library
Module: $RCSfile$
Copyright (c) Kitware, Inc., Insight Consortium. All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#define KWSYS_IN_PROCESS_C
#include "kwsysPrivate.h"
#include KWSYS_HEADER(ProcessWin32Kill.h)
/* The following process tree kill implementation is taken from
http://www.alexfedotov.com/articles/killproc.asp
It will work only on some versions of windows. Hopefully
I will eventually get some time to do a real implementation of this
for all windows versions. */
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdarg.h>
#include <tlhelp32.h>
//---------------------------------------------------------------------------
// KillProcess
//
// Terminates the specified process.
//
// Parameters:
// dwProcessId - identifier of the process to terminate
//
// Returns:
// TRUE, if successful, FALSE - otherwise.
//
static BOOL
WINAPI
KillProcess(
IN DWORD dwProcessId
)
{
HANDLE hProcess;
DWORD dwError;
// first try to obtain handle to the process without the use of any
// additional privileges
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
if (hProcess == NULL)
{
OSVERSIONINFO osvi;
TOKEN_PRIVILEGES Priv, PrivOld;
DWORD cbPriv;
HANDLE hToken;
if (GetLastError() != ERROR_ACCESS_DENIED)
return FALSE;
// determine operating system version
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
// we cannot do anything else if this is not Windows NT
if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT)
return SetLastError(ERROR_ACCESS_DENIED), FALSE;
// enable SE_DEBUG_NAME privilege and try again
cbPriv = sizeof(PrivOld);
// obtain the token of the current thread
if (!OpenThreadToken(GetCurrentThread(),
TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES,
FALSE, &hToken))
{
if (GetLastError() != ERROR_NO_TOKEN)
return FALSE;
// revert to the process token
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES,
&hToken))
return FALSE;
}
if(!(ANYSIZE_ARRAY > 0))
{
return 0;
}
Priv.PrivilegeCount = 1;
Priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Priv.Privileges[0].Luid);
// try to enable the privilege
if (!AdjustTokenPrivileges(hToken, FALSE, &Priv, sizeof(Priv),
&PrivOld, &cbPriv))
{
dwError = GetLastError();
CloseHandle(hToken);
return SetLastError(dwError), FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
// the SE_DEBUG_NAME privilege is not present in the caller's
// token
CloseHandle(hToken);
return SetLastError(ERROR_ACCESS_DENIED), FALSE;
}
// try to open process handle again
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
dwError = GetLastError();
// restore the original state of the privilege
AdjustTokenPrivileges(hToken, FALSE, &PrivOld, sizeof(PrivOld),
NULL, NULL);
CloseHandle(hToken);
if (hProcess == NULL)
{
return SetLastError(FALSE), 0;
}
}
// terminate the process
if (!TerminateProcess(hProcess, (UINT)-1))
{
dwError = GetLastError();
CloseHandle(hProcess);
return SetLastError(dwError), FALSE;
}
CloseHandle(hProcess);
// completed successfully
return TRUE;
}
typedef LONG NTSTATUS;
typedef LONG KPRIORITY;
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#define SystemProcessesAndThreadsInformation 5
typedef struct _CLIENT_ID {
DWORD UniqueProcess;
DWORD UniqueThread;
} CLIENT_ID;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef struct _VM_COUNTERS {
SIZE_T PeakVirtualSize;
SIZE_T VirtualSize;
ULONG PageFaultCount;
SIZE_T PeakWorkingSetSize;
SIZE_T WorkingSetSize;
SIZE_T QuotaPeakPagedPoolUsage;
SIZE_T QuotaPagedPoolUsage;
SIZE_T QuotaPeakNonPagedPoolUsage;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;