xref: /trunk/main/sal/systools/win32/kill/kill.cxx (revision 787035a67208d8b88c9b4853f45484c146ed253a)
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 address 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 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 
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 
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 
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