Mercurial > hg > xemacs-beta
comparison src/process-nt.c @ 274:ca9a9ec9c1c1 r21-0b35
Import from CVS: tag r21-0b35
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:29:42 +0200 |
parents | |
children | 6330739388db |
comparison
equal
deleted
inserted
replaced
273:411aac7253ef | 274:ca9a9ec9c1c1 |
---|---|
1 /* Asynchronous subprocess implemenation for Win32 | |
2 Copyright (C) 1985, 1986, 1987, 1988, 1992, 1993, 1994, 1995 | |
3 Free Software Foundation, Inc. | |
4 Copyright (C) 1995 Sun Microsystems, Inc. | |
5 Copyright (C) 1995, 1996 Ben Wing. | |
6 | |
7 This file is part of XEmacs. | |
8 | |
9 XEmacs is free software; you can redistribute it and/or modify it | |
10 under the terms of the GNU General Public License as published by the | |
11 Free Software Foundation; either version 2, or (at your option) any | |
12 later version. | |
13 | |
14 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 for more details. | |
18 | |
19 You should have received a copy of the GNU General Public License | |
20 along with XEmacs; see the file COPYING. If not, write to | |
21 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
22 Boston, MA 02111-1307, USA. */ | |
23 | |
24 /* Written by Kirill M. Katsnelson <kkm@kis.ru>, April 1998 */ | |
25 | |
26 #include <config.h> | |
27 #include "lisp.h" | |
28 | |
29 #include "hash.h" | |
30 #include "lstream.h" | |
31 #include "process.h" | |
32 #include "procimpl.h" | |
33 | |
34 #include <windows.h> | |
35 | |
36 /* Implemenation-specific data. Pointed to by Lisp_Process->process_data */ | |
37 struct nt_process_data | |
38 { | |
39 HANDLE h_process; | |
40 }; | |
41 | |
42 #define NT_DATA(p) ((struct nt_process_data*)((p)->process_data)) | |
43 | |
44 /*-----------------------------------------------------------------------*/ | |
45 /* Process helpers */ | |
46 /*-----------------------------------------------------------------------*/ | |
47 | |
48 /* #### Ok, a pretty frame is here, and a nifty text is in. Now, | |
49 any function around here to be put here? Yahoooo.... */ | |
50 | |
51 /*-----------------------------------------------------------------------*/ | |
52 /* Process methods */ | |
53 /*-----------------------------------------------------------------------*/ | |
54 | |
55 /* | |
56 * Allocate and initialize Lisp_Process->process_data | |
57 */ | |
58 | |
59 static void | |
60 nt_alloc_process_data (struct Lisp_Process *p) | |
61 { | |
62 p->process_data = xnew (struct nt_process_data); | |
63 } | |
64 | |
65 #if 0 /* #### Need this method? */ | |
66 /* | |
67 * Mark any Lisp objects in Lisp_Process->process_data | |
68 */ | |
69 | |
70 static void | |
71 nt_mark_process_data (struct Lisp_Process *proc, | |
72 void (*markobj) (Lisp_Object)) | |
73 { | |
74 } | |
75 #endif | |
76 | |
77 static void | |
78 nt_finalize_process_data (struct Lisp_Process *p, int for_disksave) | |
79 { | |
80 assert (!for_disksave); | |
81 if (NT_DATA(p)->h_process) | |
82 CloseHandle (NT_DATA(p)->h_process); | |
83 } | |
84 | |
85 #if 0 /* #### Need this method? */ | |
86 /* | |
87 * Initialize XEmacs process implemenation once | |
88 */ | |
89 | |
90 static void | |
91 nt_init_process (void) | |
92 { | |
93 } | |
94 #endif | |
95 | |
96 #if 0 /* #### Need this method? */ | |
97 /* | |
98 * Initialize any process local data. This is called when newly | |
99 * created process is connected to real OS file handles. The | |
100 * handles are generally represented by void* type, but are | |
101 * of type HANDLE for Win32 | |
102 */ | |
103 | |
104 static void | |
105 nt_init_process_io_handles (struct Lisp_Process *p, void* in, void* out, int flags) | |
106 { | |
107 } | |
108 #endif | |
109 | |
110 /* | |
111 * Fork off a subprocess. P is a pointer to newly created subprocess | |
112 * object. If this function signals, the caller is responsible for | |
113 * deleting (and finalizing) the process object. | |
114 * | |
115 * The method must return PID of the new proces, a (positive??? ####) number | |
116 * which fits into Lisp_Int. No return value indicates an error, the method | |
117 * must signal an error instead. | |
118 */ | |
119 | |
120 /* #### This function completely ignores Vprocess_environment */ | |
121 | |
122 static int | |
123 nt_create_process (struct Lisp_Process *p, | |
124 char **argv, CONST char *current_dir) | |
125 { | |
126 HANDLE hmyshove, hmyslurp, hprocin, hprocout; | |
127 LPTSTR command_line; | |
128 | |
129 /* Create two unidirectional named pipes */ | |
130 { | |
131 HANDLE htmp; | |
132 SECURITY_ATTRIBUTES sa; | |
133 | |
134 sa.nLength = sizeof(sa); | |
135 sa.bInheritHandle = TRUE; | |
136 sa.lpSecurityDescriptor = NULL; | |
137 | |
138 CreatePipe (&hprocin, &hmyshove, &sa, 0); | |
139 CreatePipe (&hmyslurp, &hprocout, &sa, 0); | |
140 | |
141 /* Stupid Win32 allows to create a pipe with *both* ends either | |
142 inheritable or not. We need process ends inheritable, and local | |
143 ends not inheritable. */ | |
144 /* #### Perhaps even stupider me does not know how to do this better */ | |
145 DuplicateHandle (GetCurrentProcess(), hmyshove, GetCurrentProcess(), &htmp, | |
146 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); | |
147 hmyshove = htmp; | |
148 DuplicateHandle (GetCurrentProcess(), hmyslurp, GetCurrentProcess(), &htmp, | |
149 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); | |
150 hmyslurp = htmp; | |
151 } | |
152 | |
153 /* Convert an argv vector into Win32 style command line. | |
154 | |
155 #### This works only for cmd, and not for cygwin bash. Perhaps, | |
156 instead of ad-hoc fiddling with different methods for quoting | |
157 process arguments in ntproc.c (disgust shudder), this must call a | |
158 smart lisp routine. The code here will be a fallback, if the | |
159 lisp function is not specified. | |
160 */ | |
161 { | |
162 char** thisarg; | |
163 size_t size = 1; | |
164 | |
165 for (thisarg = argv; *thisarg; ++thisarg) | |
166 size += strlen (*thisarg) + 1; | |
167 | |
168 command_line = alloca_array (char, size); | |
169 *command_line = 0; | |
170 | |
171 for (thisarg = argv; *thisarg; ++thisarg) | |
172 { | |
173 if (thisarg != argv) | |
174 strcat (command_line, " "); | |
175 strcat (command_line, *thisarg); | |
176 } | |
177 } | |
178 | |
179 /* Create process */ | |
180 { | |
181 STARTUPINFO si; | |
182 PROCESS_INFORMATION pi; | |
183 DWORD err; | |
184 BOOL windowed; | |
185 | |
186 xzero (si); | |
187 si.hStdInput = hprocin; | |
188 si.hStdOutput = hprocout; | |
189 si.hStdError = hprocout; | |
190 si.wShowWindow = SW_HIDE; | |
191 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; | |
192 | |
193 err = (CreateProcess (NULL, command_line, NULL, NULL, TRUE, | |
194 CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP | CREATE_SUSPENDED, | |
195 NULL, current_dir, &si, &pi) | |
196 ? 0 : GetLastError ()); | |
197 | |
198 CloseHandle (hprocin); | |
199 CloseHandle (hprocout); | |
200 | |
201 /* See if we succeeded with process creation */ | |
202 if (err) | |
203 { | |
204 process_error__One_of_those_nasty_uses_for_goto_statement: | |
205 CloseHandle (hmyshove); | |
206 CloseHandle (hmyslurp); | |
207 error ("Cannot start \"%s\": error code was %lu", argv[0], err); | |
208 } | |
209 | |
210 /* Determine if the new process is a windowed one */ | |
211 windowed = WaitForInputIdle (pi.hProcess, 100) == WAIT_TIMEOUT; | |
212 if (windowed) | |
213 { | |
214 /* We restart windowed process fire-and forget style, and | |
215 indicate successful process creation, just as if the | |
216 process ended instantly upon launching */ | |
217 CloseHandle (hmyshove); | |
218 CloseHandle (hmyslurp); | |
219 /* TerminateProcess is safe becuase the process is not yet | |
220 running */ | |
221 TerminateProcess (pi.hProcess, 0); | |
222 si.dwFlags = STARTF_USESHOWWINDOW; | |
223 si.wShowWindow = SW_SHOWNORMAL; | |
224 if (!CreateProcess (NULL, command_line, NULL, NULL, FALSE, | |
225 DETACHED_PROCESS , NULL, current_dir, &si, &pi)) | |
226 { | |
227 err = GetLastError (); | |
228 goto process_error__One_of_those_nasty_uses_for_goto_statement; | |
229 } | |
230 | |
231 /* We just launched a windowed process. Fake it as if a | |
232 process launched has already ended */ | |
233 p->status_symbol = Qexit; | |
234 | |
235 /* Get rid of process and thread handles */ | |
236 CloseHandle (pi.hThread); | |
237 CloseHandle (pi.hProcess); | |
238 } | |
239 else | |
240 { | |
241 /* Just started a console subprocess */ | |
242 | |
243 NT_DATA(p)->h_process = pi.hProcess; | |
244 | |
245 init_process_io_handles (p, (void*)hmyslurp, (void*)hmyshove, 0); | |
246 | |
247 /* We created it suspended. Resume the only thread */ | |
248 ResumeThread (pi.hThread); | |
249 CloseHandle (pi.hThread); | |
250 } | |
251 | |
252 return ((int)pi.dwProcessId < 0 | |
253 ? -(int)pi.dwProcessId : (int)pi.dwProcessId); | |
254 } | |
255 } | |
256 | |
257 /* | |
258 * This method is called to update status fields of the process | |
259 * structure. If the process has not existed, this method is expected | |
260 * to do nothing. | |
261 * | |
262 * The method is called only for real child processes. | |
263 */ | |
264 | |
265 static void | |
266 nt_update_status_if_terminated (struct Lisp_Process* p) | |
267 { | |
268 DWORD exit_code; | |
269 if (GetExitCodeProcess (NT_DATA(p)->h_process, &exit_code) | |
270 && exit_code != STILL_ACTIVE) | |
271 { | |
272 p->tick++; | |
273 p->core_dumped = 0; | |
274 /* The exit code can be a code returned by process, or an | |
275 NTSTATUS value. We cannot accurately handle the latter since | |
276 it is a full 32 bit integer */ | |
277 if (exit_code & 0xC0000000) | |
278 { | |
279 p->status_symbol = Qsignal; | |
280 p->exit_code = exit_code & 0x1FFFFFFF; | |
281 } | |
282 else | |
283 { | |
284 p->status_symbol = Qexit; | |
285 p->exit_code = exit_code; | |
286 } | |
287 } | |
288 } | |
289 | |
290 /* | |
291 * Stuff the entire contents of LSTREAM to the process ouptut pipe | |
292 */ | |
293 | |
294 /* #### If only this function could be somehow merged with | |
295 unix_send_process... */ | |
296 | |
297 static void | |
298 nt_send_process (Lisp_Object proc, struct lstream* lstream) | |
299 { | |
300 struct Lisp_Process *p = XPROCESS (proc); | |
301 | |
302 /* use a reasonable-sized buffer (somewhere around the size of the | |
303 stream buffer) so as to avoid inundating the stream with blocked | |
304 data. */ | |
305 Bufbyte chunkbuf[512]; | |
306 Bytecount chunklen; | |
307 | |
308 while (1) | |
309 { | |
310 int writeret; | |
311 | |
312 chunklen = Lstream_read (lstream, chunkbuf, 512); | |
313 if (chunklen <= 0) | |
314 break; /* perhaps should abort() if < 0? | |
315 This should never happen. */ | |
316 | |
317 /* Lstream_write() will never successfully write less than the | |
318 amount sent in. In the worst case, it just buffers the | |
319 unwritten data. */ | |
320 writeret = Lstream_write (XLSTREAM (DATA_OUTSTREAM(p)), chunkbuf, | |
321 chunklen); | |
322 if (writeret < 0) | |
323 { | |
324 p->status_symbol = Qexit; | |
325 p->exit_code = ERROR_BROKEN_PIPE; | |
326 p->core_dumped = 0; | |
327 p->tick++; | |
328 process_tick++; | |
329 deactivate_process (proc); | |
330 error ("Broken pipe error sending to process %s; closed it", | |
331 XSTRING_DATA (p->name)); | |
332 } | |
333 | |
334 while (Lstream_was_blocked_p (XLSTREAM (p->pipe_outstream))) | |
335 { | |
336 /* Buffer is full. Wait, accepting input; that may allow | |
337 the program to finish doing output and read more. */ | |
338 Faccept_process_output (Qnil, make_int (1), Qnil); | |
339 Lstream_flush (XLSTREAM (p->pipe_outstream)); | |
340 } | |
341 } | |
342 Lstream_flush (XLSTREAM (DATA_OUTSTREAM(p))); | |
343 } | |
344 | |
345 /*-----------------------------------------------------------------------*/ | |
346 /* Initialization */ | |
347 /*-----------------------------------------------------------------------*/ | |
348 | |
349 void | |
350 process_type_create_nt (void) | |
351 { | |
352 PROCESS_HAS_METHOD (nt, alloc_process_data); | |
353 PROCESS_HAS_METHOD (nt, finalize_process_data); | |
354 /* PROCESS_HAS_METHOD (nt, mark_process_data); */ | |
355 /* PROCESS_HAS_METHOD (nt, init_process); */ | |
356 /* PROCESS_HAS_METHOD (nt, init_process_io_handles); */ | |
357 PROCESS_HAS_METHOD (nt, create_process); | |
358 PROCESS_HAS_METHOD (nt, update_status_if_terminated); | |
359 PROCESS_HAS_METHOD (nt, send_process); | |
360 /* PROCESS_HAS_METHOD (nt, kill_child_process); */ | |
361 /* PROCESS_HAS_METHOD (nt, kill_process_by_pid); */ | |
362 #if 0 /* Yet todo */ | |
363 #ifdef HAVE_SOCKETS | |
364 PROCESS_HAS_METHOD (nt, canonicalize_host_name); | |
365 PROCESS_HAS_METHOD (nt, open_network_stream); | |
366 #ifdef HAVE_MULTICAST | |
367 PROCESS_HAS_METHOD (nt, open_multicast_group); | |
368 #endif | |
369 #endif | |
370 #endif | |
371 } | |
372 | |
373 void | |
374 vars_of_process_nt (void) | |
375 { | |
376 } | |
377 |