xref: /trunk/main/sal/systools/win32/kill/kill.cxx (revision 87d2adbc)
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 
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 adress in another process
71 /////////////////////////////////////////////////////////////////////////////
72 
73 #if 1
74 #define GetProcAddressEx( hProcess, hModule, lpProcName ) GetProcAddress( hModule, lpProcName )
75 #else
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 
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 
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 ( sig )
194 				{
195 					DWORD	dwStackBuffer[] =
196 					{
197 						aContext.Eip,
198 						SignalToExceptionCode( sig ),
199 						EXCEPTION_NONCONTINUABLE,
200 						0,
201 						0
202 					};
203 
204 					aContext.Esp -= sizeof(dwStackBuffer);
205 					WriteProcessMemory( hProcess, (LPVOID)aContext.Esp, dwStackBuffer, sizeof(dwStackBuffer), NULL );
206 					aContext.Eip = (DWORD)GetProcAddressEx( hProcess, GetModuleHandleA("KERNEL32"), "RaiseException" );
207 				}
208 				else
209 				{
210 					aContext.Ecx = aContext.Eax = aContext.Ebx = aContext.Edx = aContext.Esi = aContext.Edi = 0;
211 				}
212 
213 				fSuccess = SetThreadContext( hThread, &aContext );
214 			}
215 
216 			fSuccess = ResumeThread( hThread ) && fSuccess;
217 
218 			DWORD	dwLastError = GetLastError();
219 			CloseHandle( hThread );
220 			SetLastError( dwLastError );
221 
222 			return fSuccess;
223 		}
224 	}
225 
226 	return FALSE;
227 }
228 /////////////////////////////////////////////////////////////////////////////
229 // Command line parameter parsing
230 /////////////////////////////////////////////////////////////////////////////
231 
232 static void ParseCommandArgs( LPDWORD lpProcesses, LPDWORD lpdwNumProcesses, int *pSig )
233 {
234 	typedef struct _SignalEntry
235 	{
236 		LPCTSTR lpSignalName;
237 		int iSignalValue;
238 	} SignalEntry;
239 
240 	#define SIG_ENTRY( signal ) { TEXT(#signal), SIG##signal }
241 
242 	static SignalEntry SupportedSignals[] =
243 	{
244 		SIG_ENTRY( NULL ),
245 		SIG_ENTRY( SEGV ),
246 		SIG_ENTRY( ILL ),
247 		SIG_ENTRY( FPE ),
248 		SIG_ENTRY( INT ),
249 		SIG_ENTRY( BREAK ),
250 		SIG_ENTRY( TERM ),
251 		SIG_ENTRY( ABRT ),
252 		SIG_ENTRY( KILL )
253 	};
254 
255 	const int NumSupportedSignals = elementsof(SupportedSignals);
256 
257 	DWORD	dwMaxProcesses = *lpdwNumProcesses;
258 	int		argc = __argc;
259 	TCHAR	**argv = __targv;
260 
261 	*lpdwNumProcesses = 0;
262 
263 	for ( int argn = 1; argn < argc; argn++ )
264 	{
265 		if ( 0 == lstrcmpi( argv[argn], TEXT("-l") ) ||
266 			 0 == lstrcmpi( argv[argn], TEXT("/l") ) )
267 
268 		{
269 			for ( int n = 0; n < NumSupportedSignals; n++ )
270 			{
271 				_tprintf( _T("%s "), SupportedSignals[n].lpSignalName );
272 			}
273 			_tprintf( _T("\n") );
274 			ExitProcess( 0 );
275 		}
276 		else if ( 0 == lstrcmpi( argv[argn], TEXT("-?") ) ||
277 			      0 == lstrcmpi( argv[argn], TEXT("/?") ) ||
278 			      0 == lstrcmpi( argv[argn], TEXT("-h") ) ||
279 			      0 == lstrcmpi( argv[argn], TEXT("/h") ) ||
280 			      0 == lstrcmpi( argv[argn], TEXT("--help") ) )
281 		{
282 			_tprintf(
283 				_T("Terminates a process by sending a signal.\n\n")
284 				_T("Usage: kill [ -l ] [ -signal ] pid ...\n\n")
285 				_T("-l        Lists supported signals\n")
286 				_T("-signal   Sends the specified signal to the given processes.\n")
287 				_T("          signal can be a numeric value specifying the signal number\n")
288 				_T("          or a string listed by the -l parameter. If no signal is\n")
289 				_T("          given SIGTERM (-TERM) is used.\n")
290 				_T("pid       Process id(s) or executables names(s) of processes to \n")
291 				_T("          signal or terminate.\n\n")
292 				);
293 			ExitProcess( 0 );
294 		}
295 		else if ( argv[argn] && ( *argv[argn] == '-' || *argv[argn] == '/' ) )
296 		{
297 			LPCTSTR	argsig = CharNext( argv[argn] );
298 
299 			int	n;
300 			for ( n = 0; n < NumSupportedSignals; n++ )
301 			{
302 				_TCHAR *endptr = NULL;
303 
304 				if ( 0 == lstrcmpi( SupportedSignals[n].lpSignalName, argsig ) ||
305 					 _tcstoul( argsig, &endptr, 0 ) == static_cast< unsigned >(SupportedSignals[n].iSignalValue) && (!endptr || !*endptr) )
306 				{
307 					*pSig = SupportedSignals[n].iSignalValue;
308 					break;
309 				}
310 			}
311 
312 			if ( n >= NumSupportedSignals )
313 			{
314 				_ftprintf( stderr,
315 					_T("kill: Illegal argument %s\n")
316 					_T("Type 'kill --help' to show allowed syntax.\n")
317 					_T("Type 'kill -l' to show supported signals.\n"),
318 					argv[argn] );
319 				ExitProcess( 0 );
320 			}
321 		}
322 		else
323 		{
324 			unsigned long value = 0;
325 			_TCHAR	*endptr = NULL;
326 
327 			value = _tcstoul( argv[argn], &endptr, 0 );
328 
329 			if ( !endptr || !*endptr )
330 			{
331 				if ( *lpdwNumProcesses < dwMaxProcesses )
332 				{
333 					*(lpProcesses++) = value;
334 					(*lpdwNumProcesses)++;
335 				}
336 			}
337 			else
338 			{
339 				HANDLE	hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
340 
341 				if ( IsValidHandle( hSnapshot ) )
342 				{
343 					PROCESSENTRY32	pe;
344 
345 					pe.dwSize = sizeof(pe);
346 					BOOL fSuccess = Process32First( hSnapshot, &pe );
347 
348 					while ( fSuccess )
349 					{
350 						if ( 0 == lstrcmpi( argv[argn], pe.szExeFile ) )
351 						{
352 							if ( *lpdwNumProcesses < dwMaxProcesses )
353 							{
354 								*(lpProcesses++) = pe.th32ProcessID;
355 								(*lpdwNumProcesses)++;
356 							}
357 						}
358 						fSuccess = Process32Next( hSnapshot, &pe );
359 					}
360 
361 					CloseHandle( hSnapshot );
362 				}
363 			}
364 		}
365 	}
366 
367 	if ( !*lpdwNumProcesses )
368 	{
369 		_ftprintf( stderr,
370 			_T("kill: No process specified.\n")
371 			_T("Use kill --help to show allowed syntax.\n")
372 			);
373 		ExitProcess( 0 );
374 	}
375 
376 }
377 
378 void OutputSystemMessage( DWORD dwErrorCode )
379 {
380 	LPVOID lpMsgBuf;
381 	FormatMessageA(
382 					FORMAT_MESSAGE_ALLOCATE_BUFFER |
383 					FORMAT_MESSAGE_FROM_SYSTEM |
384 					FORMAT_MESSAGE_IGNORE_INSERTS,
385 					NULL,
386 					dwErrorCode,
387 					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
388 					(LPSTR)&lpMsgBuf,
389 					0,
390 					NULL
391 				);
392 
393 	printf( (LPSTR)lpMsgBuf );
394 	LocalFree( lpMsgBuf );
395 }
396 
397 int _tmain()
398 {
399 	DWORD	dwProcessIds[1024];
400 	DWORD	nProcesses = elementsof(dwProcessIds);
401 	int		sig = SIGTERM;
402 
403 
404 	ParseCommandArgs( dwProcessIds, &nProcesses, &sig );
405 
406 	for ( ULONG n = 0; n < nProcesses; n++ )
407 	{
408 		HANDLE	hProcess;
409 
410 		_tprintf( _T("Sending signal to process id %d..."), dwProcessIds[n] );
411 		hProcess = OpenProcess( PROCESS_TERMINATE | PROCESS_CREATE_THREAD | SYNCHRONIZE |
412 			PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
413 			FALSE, dwProcessIds[n] );
414 
415 		if ( IsValidHandle( hProcess ) )
416 		{
417 			if ( SIGKILL == sig )
418 				TerminateProcess( hProcess, 255 );
419 			else
420 			{
421 				if ( RaiseSignalEx( hProcess, sig ) )
422 					_tprintf( _T("OK\n") );
423 				else
424 				{
425 					OutputSystemMessage( GetLastError() );
426 				}
427 			}
428 
429 			CloseHandle( hProcess );
430 		}
431 		else
432 		{
433 			OutputSystemMessage( GetLastError() );
434 		}
435 	}
436 
437 	return 0;
438 }
439 
440