Mercurial > hg > xemacs-beta
comparison src/process-nt.c @ 280:7df0dd720c89 r21-0b38
Import from CVS: tag r21-0b38
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:32:22 +0200 |
parents | 90d73dddcdc4 |
children | c42ec1d1cded |
comparison
equal
deleted
inserted
replaced
279:c20b2fb5bb0a | 280:7df0dd720c89 |
---|---|
32 #include "procimpl.h" | 32 #include "procimpl.h" |
33 #include "sysdep.h" | 33 #include "sysdep.h" |
34 | 34 |
35 #include <windows.h> | 35 #include <windows.h> |
36 #include <shellapi.h> | 36 #include <shellapi.h> |
37 #include <signal.h> | |
37 | 38 |
38 /* Implemenation-specific data. Pointed to by Lisp_Process->process_data */ | 39 /* Implemenation-specific data. Pointed to by Lisp_Process->process_data */ |
39 struct nt_process_data | 40 struct nt_process_data |
40 { | 41 { |
41 HANDLE h_process; | 42 HANDLE h_process; |
52 HANDLE | 53 HANDLE |
53 get_nt_process_handle (struct Lisp_Process *p) | 54 get_nt_process_handle (struct Lisp_Process *p) |
54 { | 55 { |
55 return (NT_DATA (p)->h_process); | 56 return (NT_DATA (p)->h_process); |
56 } | 57 } |
57 | 58 |
59 /*-----------------------------------------------------------------------*/ | |
60 /* Running remote threads. See Microsoft Systems Journal 1994 Number 5 */ | |
61 /* Jeffrey Richter, Load Your 32-bit DLL into Another Process's Address..*/ | |
62 /*-----------------------------------------------------------------------*/ | |
63 | |
64 typedef struct | |
65 { | |
66 HANDLE h_process; | |
67 HANDLE h_thread; | |
68 LPVOID address; | |
69 } process_memory; | |
70 | |
71 /* | |
72 * Allocate SIZE bytes in H_PROCESS address space. Fill in PMC used | |
73 * further by other routines. Return nonzero if successful. | |
74 * | |
75 * The memory in other process is allocated by creating a suspended | |
76 * thread. Initial stack of that thread is used as the memory | |
77 * block. The thread entry point is the routine ExitThread in | |
78 * kernel32.dll, so the allocated memory is freed just by resuming the | |
79 * thread, which immediately terminates after that. | |
80 */ | |
81 | |
82 static int | |
83 alloc_process_memory (HANDLE h_process, size_t size, | |
84 process_memory* pmc) | |
85 { | |
86 LPTHREAD_START_ROUTINE adr_ExitThread = | |
87 (LPTHREAD_START_ROUTINE) | |
88 GetProcAddress (GetModuleHandle ("kernel32"), "ExitThread"); | |
89 DWORD dw_unused; | |
90 CONTEXT context; | |
91 MEMORY_BASIC_INFORMATION mbi; | |
92 | |
93 pmc->h_process = h_process; | |
94 pmc->h_thread = CreateRemoteThread (h_process, NULL, size, | |
95 adr_ExitThread, NULL, | |
96 CREATE_SUSPENDED, &dw_unused); | |
97 if (pmc->h_thread == NULL) | |
98 return 0; | |
99 | |
100 /* Get context, for thread's stack pointer */ | |
101 context.ContextFlags = CONTEXT_CONTROL; | |
102 if (!GetThreadContext (pmc->h_thread, &context)) | |
103 goto failure; | |
104 | |
105 /* Determine base address of the committed range */ | |
106 if (sizeof(mbi) != VirtualQueryEx (h_process, | |
107 #if defined (_X86_) | |
108 (LPDWORD)context.Esp - 1, | |
109 #elif defined (_ALPHA_) | |
110 (LPDWORD)context.IntSp - 1, | |
111 #else | |
112 #error Unknown processor architecture | |
113 #endif | |
114 &mbi, sizeof(mbi))) | |
115 goto failure; | |
116 | |
117 /* Change the page protection of the allocated memory to executable, | |
118 read, and write. */ | |
119 if (!VirtualProtectEx (h_process, mbi.BaseAddress, size, | |
120 PAGE_EXECUTE_READWRITE, &dw_unused)) | |
121 goto failure; | |
122 | |
123 pmc->address = mbi.BaseAddress; | |
124 return 1; | |
125 | |
126 failure: | |
127 ResumeThread (pmc->h_thread); | |
128 pmc->address = 0; | |
129 return 0; | |
130 } | |
131 | |
132 static void | |
133 free_process_memory (process_memory* pmc) | |
134 { | |
135 ResumeThread (pmc->h_thread); | |
136 } | |
137 | |
138 /* | |
139 * Run ROUTINE in the context of process determined by H_PROCESS. The | |
140 * routine is passed the address of DATA as parameter. CODE_END is the | |
141 * address immediately after ROUTINE's code. DATA_SIZE is the size of | |
142 * DATA structure. | |
143 * | |
144 * Note that the code must be positionally independent, and compiled | |
145 * without stack checks (they cause implicit calls into CRT so will | |
146 * fail). DATA should not refer any data in calling process, as both | |
147 * routine and its data are copied into remote process. Size of data | |
148 * and code together should not exceed one page (4K on x86 systems). | |
149 * | |
150 * Return the value returned by ROUTINE, or (DWORD)-1 if call failed. | |
151 */ | |
152 static DWORD | |
153 run_in_other_process (HANDLE h_process, | |
154 LPTHREAD_START_ROUTINE routine, LPVOID code_end, | |
155 LPVOID data, size_t data_size) | |
156 { | |
157 process_memory pm; | |
158 size_t code_size = (LPBYTE)code_end - (LPBYTE)routine; | |
159 /* Need at most 3 extra bytes of memory, for data alignment */ | |
160 size_t total_size = code_size + data_size + 3; | |
161 LPVOID remote_data; | |
162 HANDLE h_thread; | |
163 DWORD dw_unused; | |
164 | |
165 /* Allocate memory */ | |
166 if (!alloc_process_memory (h_process, total_size, &pm)) | |
167 return (DWORD)-1; | |
168 | |
169 /* Copy code */ | |
170 if (!WriteProcessMemory (h_process, pm.address, (LPVOID)routine, | |
171 code_size, NULL)) | |
172 goto failure; | |
173 | |
174 /* Copy data */ | |
175 if (data_size) | |
176 { | |
177 remote_data = (LPBYTE)pm.address + ((code_size + 4) & ~3); | |
178 if (!WriteProcessMemory (h_process, remote_data, data, data_size, NULL)) | |
179 goto failure; | |
180 } | |
181 else | |
182 remote_data = NULL; | |
183 | |
184 /* Execute the remote copy of code, passing it remote data */ | |
185 h_thread = CreateRemoteThread (h_process, NULL, 0, | |
186 (LPTHREAD_START_ROUTINE) pm.address, | |
187 remote_data, 0, &dw_unused); | |
188 if (h_thread == NULL) | |
189 goto failure; | |
190 | |
191 /* Wait till thread finishes */ | |
192 WaitForSingleObject (h_thread, INFINITE); | |
193 | |
194 /* Free remote memory */ | |
195 free_process_memory (&pm); | |
196 | |
197 /* Return thread's exit code */ | |
198 { | |
199 DWORD exit_code; | |
200 GetExitCodeThread (h_thread, &exit_code); | |
201 CloseHandle (h_thread); | |
202 return exit_code; | |
203 } | |
204 | |
205 failure: | |
206 free_process_memory (&pm); | |
207 return (DWORD)-1; | |
208 } | |
209 | |
210 /*-----------------------------------------------------------------------*/ | |
211 /* Sending signals */ | |
212 /*-----------------------------------------------------------------------*/ | |
213 | |
214 /* | |
215 * We handle the following signals: | |
216 * | |
217 * SIGKILL, SIGTERM, SIGQUIT - These three translate to ExitProcess | |
218 * executed by the remote process | |
219 * SIGINT - The remote process is sent CTRL_BREAK_EVENT | |
220 */ | |
221 | |
222 /* | |
223 * Sending SIGKILL | |
224 */ | |
225 typedef struct | |
226 { | |
227 void (WINAPI *adr_ExitProcess) (UINT); | |
228 } sigkill_data; | |
229 | |
230 static DWORD WINAPI | |
231 sigkill_proc (sigkill_data* data) | |
232 { | |
233 (*data->adr_ExitProcess)(255); | |
234 return 1; | |
235 } | |
236 | |
237 /* Watermark in code space */ | |
238 static void | |
239 sigkill_code_end (void) | |
240 { | |
241 } | |
242 | |
243 /* | |
244 * Sending break or control c | |
245 */ | |
246 typedef struct | |
247 { | |
248 BOOL (WINAPI *adr_GenerateConsoleCtrlEvent) (DWORD, DWORD); | |
249 DWORD event; | |
250 } sigint_data; | |
251 | |
252 static DWORD WINAPI | |
253 sigint_proc (sigint_data* data) | |
254 { | |
255 return (*data->adr_GenerateConsoleCtrlEvent) (data->event, 0); | |
256 } | |
257 | |
258 /* Watermark in code space */ | |
259 static void | |
260 sigint_code_end (void) | |
261 { | |
262 } | |
263 | |
264 /* | |
265 * Enabling signals | |
266 */ | |
267 typedef struct | |
268 { | |
269 BOOL (WINAPI *adr_SetConsoleCtrlHandler) (LPVOID, BOOL); | |
270 } sig_enable_data; | |
271 | |
272 static DWORD WINAPI | |
273 sig_enable_proc (sig_enable_data* data) | |
274 { | |
275 (*data->adr_SetConsoleCtrlHandler) (NULL, FALSE); | |
276 return 1; | |
277 } | |
278 | |
279 /* Watermark in code space */ | |
280 static void | |
281 sig_enable_code_end (void) | |
282 { | |
283 } | |
284 | |
285 /* | |
286 * Send signal SIGNO to process H_PROCESS. | |
287 * Return nonzero if successful. | |
288 */ | |
289 | |
290 /* This code assigns a return value of GetProcAddress to function pointers | |
291 of many different types. Instead of heavy obscure casts, we just disable | |
292 warnings about assignments to different function pointer types. */ | |
293 #pragma warning (disable : 4113) | |
294 | |
295 static int | |
296 send_signal (HANDLE h_process, int signo) | |
297 { | |
298 HMODULE h_kernel = GetModuleHandle ("kernel32"); | |
299 DWORD retval; | |
300 | |
301 assert (h_kernel != NULL); | |
302 | |
303 switch (signo) | |
304 { | |
305 case SIGKILL: | |
306 case SIGTERM: | |
307 case SIGQUIT: | |
308 { | |
309 sigkill_data d; | |
310 d.adr_ExitProcess = GetProcAddress (h_kernel, "ExitProcess"); | |
311 assert (d.adr_ExitProcess); | |
312 retval = run_in_other_process (h_process, | |
313 sigkill_proc, sigkill_code_end, | |
314 &d, sizeof (d)); | |
315 break; | |
316 } | |
317 case SIGINT: | |
318 { | |
319 sigint_data d; | |
320 d.adr_GenerateConsoleCtrlEvent = | |
321 GetProcAddress (h_kernel, "GenerateConsoleCtrlEvent"); | |
322 assert (d.adr_GenerateConsoleCtrlEvent); | |
323 d.event = CTRL_C_EVENT; | |
324 retval = run_in_other_process (h_process, | |
325 sigint_proc, sigint_code_end, | |
326 &d, sizeof (d)); | |
327 break; | |
328 } | |
329 default: | |
330 assert (0); | |
331 } | |
332 | |
333 return (int)retval > 0 ? 1 : 0; | |
334 } | |
335 | |
336 /* | |
337 * Enable CTRL_C_EVENT handling in a new child process | |
338 */ | |
339 static void | |
340 enable_child_signals (HANDLE h_process) | |
341 { | |
342 HMODULE h_kernel = GetModuleHandle ("kernel32"); | |
343 sig_enable_data d; | |
344 | |
345 assert (h_kernel != NULL); | |
346 d.adr_SetConsoleCtrlHandler = | |
347 GetProcAddress (h_kernel, "SetConsoleCtrlHandler"); | |
348 assert (d.adr_SetConsoleCtrlHandler); | |
349 run_in_other_process (h_process, | |
350 sig_enable_proc, sig_enable_code_end, | |
351 &d, sizeof (d)); | |
352 } | |
353 | |
354 #pragma warning (default : 4113) | |
355 | |
356 /* | |
357 * Signal error if SIGNO is not supported | |
358 */ | |
359 static void | |
360 validate_signal_number (int signo) | |
361 { | |
362 if (signo != SIGKILL && signo != SIGTERM | |
363 && signo != SIGQUIT && signo != SIGINT) | |
364 error ("Signal number %d not supported", signo); | |
365 } | |
366 | |
58 /*-----------------------------------------------------------------------*/ | 367 /*-----------------------------------------------------------------------*/ |
59 /* Process methods */ | 368 /* Process methods */ |
60 /*-----------------------------------------------------------------------*/ | 369 /*-----------------------------------------------------------------------*/ |
61 | 370 |
62 /* | 371 /* |
224 si.hStdError = hprocout; | 533 si.hStdError = hprocout; |
225 si.dwFlags |= STARTF_USESTDHANDLES; | 534 si.dwFlags |= STARTF_USESTDHANDLES; |
226 } | 535 } |
227 | 536 |
228 err = (CreateProcess (NULL, command_line, NULL, NULL, TRUE, | 537 err = (CreateProcess (NULL, command_line, NULL, NULL, TRUE, |
229 CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, | 538 CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP |
539 | CREATE_SUSPENDED, | |
230 NULL, current_dir, &si, &pi) | 540 NULL, current_dir, &si, &pi) |
231 ? 0 : GetLastError ()); | 541 ? 0 : GetLastError ()); |
232 | 542 |
233 if (do_io) | 543 if (do_io) |
234 { | 544 { |
259 /* Indicate as if the process has exited immediately. */ | 569 /* Indicate as if the process has exited immediately. */ |
260 p->status_symbol = Qexit; | 570 p->status_symbol = Qexit; |
261 CloseHandle (pi.hProcess); | 571 CloseHandle (pi.hProcess); |
262 } | 572 } |
263 | 573 |
574 if (!windowed) | |
575 enable_child_signals (pi.hProcess); | |
576 | |
577 ResumeThread (pi.hThread); | |
264 CloseHandle (pi.hThread); | 578 CloseHandle (pi.hThread); |
265 | 579 |
266 /* Hack to support Windows 95 negative pids */ | 580 /* Hack to support Windows 95 negative pids */ |
267 return ((int)pi.dwProcessId < 0 | 581 return ((int)pi.dwProcessId < 0 |
268 ? -(int)pi.dwProcessId : (int)pi.dwProcessId); | 582 ? -(int)pi.dwProcessId : (int)pi.dwProcessId); |
355 } | 669 } |
356 } | 670 } |
357 Lstream_flush (XLSTREAM (DATA_OUTSTREAM(p))); | 671 Lstream_flush (XLSTREAM (DATA_OUTSTREAM(p))); |
358 } | 672 } |
359 | 673 |
674 /* | |
675 * Send a signal number SIGNO to PROCESS. | |
676 * CURRENT_GROUP means send to the process group that currently owns | |
677 * the terminal being used to communicate with PROCESS. | |
678 * This is used for various commands in shell mode. | |
679 * If NOMSG is zero, insert signal-announcements into process's buffers | |
680 * right away. | |
681 * | |
682 * If we can, we try to signal PROCESS by sending control characters | |
683 * down the pty. This allows us to signal inferiors who have changed | |
684 * their uid, for which killpg would return an EPERM error. | |
685 * | |
686 * The method signals an error if the given SIGNO is not valid | |
687 */ | |
688 | |
689 static void | |
690 nt_kill_child_process (Lisp_Object proc, int signo, | |
691 int current_group, int nomsg) | |
692 { | |
693 struct Lisp_Process *p = XPROCESS (proc); | |
694 | |
695 /* Signal error if SIGNO cannot be sent */ | |
696 validate_signal_number (signo); | |
697 | |
698 /* Send signal */ | |
699 if (!send_signal (NT_DATA(p)->h_process, signo)) | |
700 error ("Cannot send signal to process"); | |
701 } | |
702 | |
703 /* | |
704 * Kill any process in the system given its PID. | |
705 * | |
706 * Returns zero if a signal successfully sent, or | |
707 * negative number upon failure | |
708 */ | |
709 static int | |
710 nt_kill_process_by_pid (int pid, int signo) | |
711 { | |
712 HANDLE h_process; | |
713 int send_result; | |
714 | |
715 /* Signal error if SIGNO cannot be sent */ | |
716 validate_signal_number (signo); | |
717 | |
718 /* Try to open the process with required privileges */ | |
719 h_process = OpenProcess (PROCESS_CREATE_THREAD | |
720 | PROCESS_QUERY_INFORMATION | |
721 | PROCESS_VM_OPERATION | |
722 | PROCESS_VM_WRITE, | |
723 FALSE, pid); | |
724 if (h_process == NULL) | |
725 return -1; | |
726 | |
727 send_result = send_signal (h_process, signo); | |
728 | |
729 CloseHandle (h_process); | |
730 | |
731 return send_result ? 0 : -1; | |
732 } | |
733 | |
734 | |
360 /*-----------------------------------------------------------------------*/ | 735 /*-----------------------------------------------------------------------*/ |
361 /* Initialization */ | 736 /* Initialization */ |
362 /*-----------------------------------------------------------------------*/ | 737 /*-----------------------------------------------------------------------*/ |
363 | 738 |
364 void | 739 void |
370 /* PROCESS_HAS_METHOD (nt, init_process); */ | 745 /* PROCESS_HAS_METHOD (nt, init_process); */ |
371 /* PROCESS_HAS_METHOD (nt, init_process_io_handles); */ | 746 /* PROCESS_HAS_METHOD (nt, init_process_io_handles); */ |
372 PROCESS_HAS_METHOD (nt, create_process); | 747 PROCESS_HAS_METHOD (nt, create_process); |
373 PROCESS_HAS_METHOD (nt, update_status_if_terminated); | 748 PROCESS_HAS_METHOD (nt, update_status_if_terminated); |
374 PROCESS_HAS_METHOD (nt, send_process); | 749 PROCESS_HAS_METHOD (nt, send_process); |
375 /* PROCESS_HAS_METHOD (nt, kill_child_process); */ | 750 PROCESS_HAS_METHOD (nt, kill_child_process); |
376 /* PROCESS_HAS_METHOD (nt, kill_process_by_pid); */ | 751 PROCESS_HAS_METHOD (nt, kill_process_by_pid); |
377 #if 0 /* Yet todo */ | 752 #if 0 /* Yet todo */ |
378 #ifdef HAVE_SOCKETS | 753 #ifdef HAVE_SOCKETS |
379 PROCESS_HAS_METHOD (nt, canonicalize_host_name); | 754 PROCESS_HAS_METHOD (nt, canonicalize_host_name); |
380 PROCESS_HAS_METHOD (nt, open_network_stream); | 755 PROCESS_HAS_METHOD (nt, open_network_stream); |
381 #ifdef HAVE_MULTICAST | 756 #ifdef HAVE_MULTICAST |