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