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