xref: /AOO41X/main/sal/systools/win32/kill/kill.cxx (revision 87d2adbc9cadf14644c3679b041b9226f7630199)
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 adress 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 ( 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 
ParseCommandArgs(LPDWORD lpProcesses,LPDWORD lpdwNumProcesses,int * pSig)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 
OutputSystemMessage(DWORD dwErrorCode)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 
_tmain()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