xref: /trunk/main/sal/systools/win32/kill/kill.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sal.hxx"
30 
31 #include <tchar.h>
32 
33 #ifdef _MSC_VER
34 #pragma warning(push,1) // disable warnings within system headers
35 #endif
36 #define WIN32_LEAN_AND_MEAN
37 #include <windows.h>
38 #include <tlhelp32.h>
39 #include <psapi.h>
40 #ifdef _MSC_VER
41 #pragma warning(pop)
42 #endif
43 
44 #include <signal.h>
45 #include <stdarg.h>
46 #include <stdlib.h>
47 #include <stdio.h>
48 
49 #ifndef SIGNULL
50 #define SIGNULL	0
51 #endif
52 
53 #ifndef SIGKILL
54 #define SIGKILL	9
55 #endif
56 
57 #include <signal.h>
58 
59 #define MAX_MODULES	1024
60 
61 /////////////////////////////////////////////////////////////////////////////
62 // Determines if a returned handle value is valid
63 /////////////////////////////////////////////////////////////////////////////
64 
65 static inline bool IsValidHandle( HANDLE handle )
66 {
67 	return INVALID_HANDLE_VALUE != handle && NULL != handle;
68 }
69 
70 
71 #define elementsof( a ) (sizeof(a) / sizeof( (a)[0] ))
72 
73 /////////////////////////////////////////////////////////////////////////////
74 // Retrieves function adress in another process
75 /////////////////////////////////////////////////////////////////////////////
76 
77 #if 1
78 #define GetProcAddressEx( hProcess, hModule, lpProcName ) GetProcAddress( hModule, lpProcName )
79 #else
80 FARPROC WINAPI GetProcAddressEx( HANDLE hProcess, HMODULE hModule, LPCSTR lpProcName )
81 {
82 	FARPROC	lpfnProcAddress = GetProcAddress( hModule, lpProcName );
83 
84 	if ( lpfnProcAddress )
85 	{
86 		DWORD	dwProcessId = GetProcessId( hProcess );
87 
88 		if ( GetCurrentProcessId() != dwProcessId )
89 		{
90 			FARPROC	lpfnRemoteProcAddress = NULL;
91 			TCHAR	szBaseName[MAX_PATH];
92 
93 			if ( GetModuleBaseName( GetCurrentProcess(), hModule, szBaseName, elementsof(szBaseName) ) )
94 			{
95 				HMODULE	ahModules[MAX_MODULES];
96 				DWORD	cbNeeded = 0;
97 
98 				if ( EnumProcessModules( hProcess, ahModules, sizeof(ahModules), &cbNeeded ) )
99 				{
100 					ULONG	nModules = cbNeeded / sizeof(ahModules[0]);
101 
102 					for ( ULONG n = 0; n < nModules; n++ )
103 					{
104 						TCHAR	szRemoteBaseName[MAX_PATH];
105 
106 						if ( GetModuleBaseName(
107                             hProcess, ahModules[n], szRemoteBaseName, elementsof(szRemoteBaseName) ) &&
108 							0 == lstrcmpi( szRemoteBaseName, szBaseName )
109 							)
110 						{
111 							lpfnRemoteProcAddress = lpfnProcAddress;
112 
113 							if ( ahModules[n] != hModule )
114 								*(LPBYTE*)&lpfnRemoteProcAddress += (LPBYTE)ahModules[n] - (LPBYTE)hModule;
115 							break;
116 						}
117 					}
118 				}
119 			}
120 
121 			lpfnProcAddress = lpfnRemoteProcAddress;
122 		}
123 	}
124 
125 	return lpfnProcAddress;
126 }
127 #endif
128 
129 /////////////////////////////////////////////////////////////////////////////
130 // Raises a signal in an other process
131 /////////////////////////////////////////////////////////////////////////////
132 
133 static DWORD SignalToExceptionCode( int signal )
134 {
135 	switch ( signal )
136 	{
137 	case SIGSEGV:
138 		return EXCEPTION_ACCESS_VIOLATION;
139 	case SIGFPE:
140 		return EXCEPTION_FLT_INVALID_OPERATION;
141 	case SIGILL:
142 		return EXCEPTION_ILLEGAL_INSTRUCTION;
143 	case SIGINT:
144 		return CONTROL_C_EXIT;
145 	case SIGBREAK:
146 		return CONTROL_C_EXIT;
147 	default:
148 		return 0;
149 	}
150 }
151 
152 static BOOL RaiseSignalEx( HANDLE hProcess, int sig )
153 {
154 	DWORD	dwProcessId = GetProcessId( hProcess );
155 
156 	HANDLE	hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
157 	HANDLE	hThread = 0;
158 	BOOL fSuccess = FALSE;
159 
160 	if ( IsValidHandle(hSnapshot) )
161 	{
162 		THREADENTRY32	te;
163 
164 		te.dwSize = sizeof(te);
165 		fSuccess = Thread32First( hSnapshot, &te );
166 		while ( fSuccess )
167 		{
168 			if ( te.th32OwnerProcessID == dwProcessId )
169 			{
170 				hThread = OpenThread(
171 					THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION |
172 					THREAD_GET_CONTEXT | THREAD_SET_CONTEXT,
173 					FALSE, te.th32ThreadID );
174 				if ( IsValidHandle(hThread) )
175 					break;
176 			}
177 
178 			fSuccess = Thread32Next( hSnapshot, &te );
179 		}
180 
181 		CloseHandle( hSnapshot );
182 	}
183 
184 	if ( fSuccess )
185 	{
186 		CONTEXT	aContext;
187 
188 		if ( SuspendThread( hThread ) != (DWORD)-1 )
189 		{
190 			ZeroMemory( &aContext, sizeof(aContext) );
191 			aContext.ContextFlags = CONTEXT_FULL;
192 
193 			fSuccess = GetThreadContext( hThread, &aContext );
194 
195 			if ( fSuccess )
196 			{
197 				if ( sig )
198 				{
199 					DWORD	dwStackBuffer[] =
200 					{
201 						aContext.Eip,
202 						SignalToExceptionCode( sig ),
203 						EXCEPTION_NONCONTINUABLE,
204 						0,
205 						0
206 					};
207 
208 					aContext.Esp -= sizeof(dwStackBuffer);
209 					WriteProcessMemory( hProcess, (LPVOID)aContext.Esp, dwStackBuffer, sizeof(dwStackBuffer), NULL );
210 					aContext.Eip = (DWORD)GetProcAddressEx( hProcess, GetModuleHandleA("KERNEL32"), "RaiseException" );
211 				}
212 				else
213 				{
214 					aContext.Ecx = aContext.Eax = aContext.Ebx = aContext.Edx = aContext.Esi = aContext.Edi = 0;
215 				}
216 
217 				fSuccess = SetThreadContext( hThread, &aContext );
218 			}
219 
220 			fSuccess = ResumeThread( hThread ) && fSuccess;
221 
222 			DWORD	dwLastError = GetLastError();
223 			CloseHandle( hThread );
224 			SetLastError( dwLastError );
225 
226 			return fSuccess;
227 		}
228 	}
229 
230 	return FALSE;
231 }
232 /////////////////////////////////////////////////////////////////////////////
233 // Command line parameter parsing
234 /////////////////////////////////////////////////////////////////////////////
235 
236 static void ParseCommandArgs( LPDWORD lpProcesses, LPDWORD lpdwNumProcesses, int *pSig )
237 {
238 	typedef struct _SignalEntry
239 	{
240 		LPCTSTR lpSignalName;
241 		int iSignalValue;
242 	} SignalEntry;
243 
244 	#define SIG_ENTRY( signal ) { TEXT(#signal), SIG##signal }
245 
246 	static SignalEntry SupportedSignals[] =
247 	{
248 		SIG_ENTRY( NULL ),
249 		SIG_ENTRY( SEGV ),
250 		SIG_ENTRY( ILL ),
251 		SIG_ENTRY( FPE ),
252 		SIG_ENTRY( INT ),
253 		SIG_ENTRY( BREAK ),
254 		SIG_ENTRY( TERM ),
255 		SIG_ENTRY( ABRT ),
256 		SIG_ENTRY( KILL )
257 	};
258 
259 	const int NumSupportedSignals = elementsof(SupportedSignals);
260 
261 	DWORD	dwMaxProcesses = *lpdwNumProcesses;
262 	int		argc = __argc;
263 	TCHAR	**argv = __targv;
264 
265 	*lpdwNumProcesses = 0;
266 
267 	for ( int argn = 1; argn < argc; argn++ )
268 	{
269 		if ( 0 == lstrcmpi( argv[argn], TEXT("-l") ) ||
270 			 0 == lstrcmpi( argv[argn], TEXT("/l") ) )
271 
272 		{
273 			for ( int n = 0; n < NumSupportedSignals; n++ )
274 			{
275 				_tprintf( _T("%s "), SupportedSignals[n].lpSignalName );
276 			}
277 			_tprintf( _T("\n") );
278 			ExitProcess( 0 );
279 		}
280 		else if ( 0 == lstrcmpi( argv[argn], TEXT("-?") ) ||
281 			      0 == lstrcmpi( argv[argn], TEXT("/?") ) ||
282 			      0 == lstrcmpi( argv[argn], TEXT("-h") ) ||
283 			      0 == lstrcmpi( argv[argn], TEXT("/h") ) ||
284 			      0 == lstrcmpi( argv[argn], TEXT("--help") ) )
285 		{
286 			_tprintf(
287 				_T("Terminates a process by sending a signal.\n\n")
288 				_T("Usage: kill [ -l ] [ -signal ] pid ...\n\n")
289 				_T("-l        Lists supported signals\n")
290 				_T("-signal   Sends the specified signal to the given processes.\n")
291 				_T("          signal can be a numeric value specifying the signal number\n")
292 				_T("          or a string listed by the -l parameter. If no signal is\n")
293 				_T("          given SIGTERM (-TERM) is used.\n")
294 				_T("pid       Process id(s) or executables names(s) of processes to \n")
295 				_T("          signal or terminate.\n\n")
296 				);
297 			ExitProcess( 0 );
298 		}
299 		else if ( argv[argn] && ( *argv[argn] == '-' || *argv[argn] == '/' ) )
300 		{
301 			LPCTSTR	argsig = CharNext( argv[argn] );
302 
303 			int	n;
304 			for ( n = 0; n < NumSupportedSignals; n++ )
305 			{
306 				_TCHAR *endptr = NULL;
307 
308 				if ( 0 == lstrcmpi( SupportedSignals[n].lpSignalName, argsig ) ||
309 					 _tcstoul( argsig, &endptr, 0 ) == static_cast< unsigned >(SupportedSignals[n].iSignalValue) && (!endptr || !*endptr) )
310 				{
311 					*pSig = SupportedSignals[n].iSignalValue;
312 					break;
313 				}
314 			}
315 
316 			if ( n >= NumSupportedSignals )
317 			{
318 				_ftprintf( stderr,
319 					_T("kill: Illegal argument %s\n")
320 					_T("Type 'kill --help' to show allowed syntax.\n")
321 					_T("Type 'kill -l' to show supported signals.\n"),
322 					argv[argn] );
323 				ExitProcess( 0 );
324 			}
325 		}
326 		else
327 		{
328 			unsigned long value = 0;
329 			_TCHAR	*endptr = NULL;
330 
331 			value = _tcstoul( argv[argn], &endptr, 0 );
332 
333 			if ( !endptr || !*endptr )
334 			{
335 				if ( *lpdwNumProcesses < dwMaxProcesses )
336 				{
337 					*(lpProcesses++) = value;
338 					(*lpdwNumProcesses)++;
339 				}
340 			}
341 			else
342 			{
343 				HANDLE	hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
344 
345 				if ( IsValidHandle( hSnapshot ) )
346 				{
347 					PROCESSENTRY32	pe;
348 
349 					pe.dwSize = sizeof(pe);
350 					BOOL fSuccess = Process32First( hSnapshot, &pe );
351 
352 					while ( fSuccess )
353 					{
354 						if ( 0 == lstrcmpi( argv[argn], pe.szExeFile ) )
355 						{
356 							if ( *lpdwNumProcesses < dwMaxProcesses )
357 							{
358 								*(lpProcesses++) = pe.th32ProcessID;
359 								(*lpdwNumProcesses)++;
360 							}
361 						}
362 						fSuccess = Process32Next( hSnapshot, &pe );
363 					}
364 
365 					CloseHandle( hSnapshot );
366 				}
367 			}
368 		}
369 	}
370 
371 	if ( !*lpdwNumProcesses )
372 	{
373 		_ftprintf( stderr,
374 			_T("kill: No process specified.\n")
375 			_T("Use kill --help to show allowed syntax.\n")
376 			);
377 		ExitProcess( 0 );
378 	}
379 
380 }
381 
382 void OutputSystemMessage( DWORD dwErrorCode )
383 {
384 	LPVOID lpMsgBuf;
385 	FormatMessageA(
386 					FORMAT_MESSAGE_ALLOCATE_BUFFER |
387 					FORMAT_MESSAGE_FROM_SYSTEM |
388 					FORMAT_MESSAGE_IGNORE_INSERTS,
389 					NULL,
390 					dwErrorCode,
391 					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
392 					(LPSTR)&lpMsgBuf,
393 					0,
394 					NULL
395 				);
396 
397 	printf( (LPSTR)lpMsgBuf );
398 	LocalFree( lpMsgBuf );
399 }
400 
401 int _tmain()
402 {
403 	DWORD	dwProcessIds[1024];
404 	DWORD	nProcesses = elementsof(dwProcessIds);
405 	int		sig = SIGTERM;
406 
407 
408 	ParseCommandArgs( dwProcessIds, &nProcesses, &sig );
409 
410 	for ( ULONG n = 0; n < nProcesses; n++ )
411 	{
412 		HANDLE	hProcess;
413 
414 		_tprintf( _T("Sending signal to process id %d..."), dwProcessIds[n] );
415 		hProcess = OpenProcess( PROCESS_TERMINATE | PROCESS_CREATE_THREAD | SYNCHRONIZE |
416 			PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
417 			FALSE, dwProcessIds[n] );
418 
419 		if ( IsValidHandle( hProcess ) )
420 		{
421 			if ( SIGKILL == sig )
422 				TerminateProcess( hProcess, 255 );
423 			else
424 			{
425 				if ( RaiseSignalEx( hProcess, sig ) )
426 					_tprintf( _T("OK\n") );
427 				else
428 				{
429 					OutputSystemMessage( GetLastError() );
430 				}
431 			}
432 
433 			CloseHandle( hProcess );
434 		}
435 		else
436 		{
437 			OutputSystemMessage( GetLastError() );
438 		}
439 	}
440 
441 	return 0;
442 }
443 
444