1 /*
2 * support routines for subprocess module
3 *
4 * Currently, this extension module is only required when using the
5 * subprocess module on Windows, but in the future, stubs for other
6 * platforms might be added here as well.
7 *
8 * Copyright (c) 2004 by Fredrik Lundh <fredrik@pythonware.com>
9 * Copyright (c) 2004 by Secret Labs AB, http://www.pythonware.com
10 * Copyright (c) 2004 by Peter Astrand <astrand@lysator.liu.se>
11 *
12 * By obtaining, using, and/or copying this software and/or its
13 * associated documentation, you agree that you have read, understood,
14 * and will comply with the following terms and conditions:
15 *
16 * Permission to use, copy, modify, and distribute this software and
17 * its associated documentation for any purpose and without fee is
18 * hereby granted, provided that the above copyright notice appears in
19 * all copies, and that both that copyright notice and this permission
20 * notice appear in supporting documentation, and that the name of the
21 * authors not be used in advertising or publicity pertaining to
22 * distribution of the software without specific, written prior
23 * permission.
24 *
25 * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
26 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
27 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
28 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
29 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
30 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
31 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 *
33 */
34
35 /* Licensed to PSF under a Contributor Agreement. */
36 /* See http://www.python.org/2.4/license for licensing details. */
37
38 /* TODO: handle unicode command lines? */
39 /* TODO: handle unicode environment? */
40
41 #include "Python.h"
42
43 #define WINDOWS_LEAN_AND_MEAN
44 #include "windows.h"
45
46 /* -------------------------------------------------------------------- */
47 /* handle wrapper. note that this library uses integers when passing
48 handles to a function, and handle wrappers when returning handles.
49 the wrapper is used to provide Detach and Close methods */
50
51 typedef struct {
52 PyObject_HEAD
53 HANDLE handle;
54 } sp_handle_object;
55
56 staticforward PyTypeObject sp_handle_type;
57
58 static PyObject*
sp_handle_new(HANDLE handle)59 sp_handle_new(HANDLE handle)
60 {
61 sp_handle_object* self;
62
63 self = PyObject_NEW(sp_handle_object, &sp_handle_type);
64 if (self == NULL)
65 return NULL;
66
67 self->handle = handle;
68
69 return (PyObject*) self;
70 }
71
72 #if defined(MS_WIN32) && !defined(MS_WIN64)
73 #define HANDLE_TO_PYNUM(handle) PyInt_FromLong((long) handle)
74 #define PY_HANDLE_PARAM "l"
75 #else
76 #define HANDLE_TO_PYNUM(handle) PyLong_FromLongLong((long long) handle)
77 #define PY_HANDLE_PARAM "L"
78 #endif
79
80 static PyObject*
sp_handle_detach(sp_handle_object * self,PyObject * args)81 sp_handle_detach(sp_handle_object* self, PyObject* args)
82 {
83 HANDLE handle;
84
85 if (! PyArg_ParseTuple(args, ":Detach"))
86 return NULL;
87
88 handle = self->handle;
89
90 self->handle = INVALID_HANDLE_VALUE;
91
92 /* note: return the current handle, as an integer */
93 return HANDLE_TO_PYNUM(handle);
94 }
95
96 static PyObject*
sp_handle_close(sp_handle_object * self,PyObject * args)97 sp_handle_close(sp_handle_object* self, PyObject* args)
98 {
99 if (! PyArg_ParseTuple(args, ":Close"))
100 return NULL;
101
102 if (self->handle != INVALID_HANDLE_VALUE) {
103 CloseHandle(self->handle);
104 self->handle = INVALID_HANDLE_VALUE;
105 }
106 Py_INCREF(Py_None);
107 return Py_None;
108 }
109
110 static void
sp_handle_dealloc(sp_handle_object * self)111 sp_handle_dealloc(sp_handle_object* self)
112 {
113 if (self->handle != INVALID_HANDLE_VALUE)
114 CloseHandle(self->handle);
115 PyObject_FREE(self);
116 }
117
118 static PyMethodDef sp_handle_methods[] = {
119 {"Detach", (PyCFunction) sp_handle_detach, METH_VARARGS},
120 {"Close", (PyCFunction) sp_handle_close, METH_VARARGS},
121 {NULL, NULL}
122 };
123
124 static PyObject*
sp_handle_getattr(sp_handle_object * self,char * name)125 sp_handle_getattr(sp_handle_object* self, char* name)
126 {
127 return Py_FindMethod(sp_handle_methods, (PyObject*) self, name);
128 }
129
130 static PyObject*
sp_handle_as_int(sp_handle_object * self)131 sp_handle_as_int(sp_handle_object* self)
132 {
133 return HANDLE_TO_PYNUM(self->handle);
134 }
135
136 static PyNumberMethods sp_handle_as_number;
137
138 statichere PyTypeObject sp_handle_type = {
139 PyObject_HEAD_INIT(NULL)
140 0, /*ob_size*/
141 "_subprocess_handle", sizeof(sp_handle_object), 0,
142 (destructor) sp_handle_dealloc, /*tp_dealloc*/
143 0, /*tp_print*/
144 (getattrfunc) sp_handle_getattr,/*tp_getattr*/
145 0, /*tp_setattr*/
146 0, /*tp_compare*/
147 0, /*tp_repr*/
148 &sp_handle_as_number, /*tp_as_number */
149 0, /*tp_as_sequence */
150 0, /*tp_as_mapping */
151 0 /*tp_hash*/
152 };
153
154 /* -------------------------------------------------------------------- */
155 /* windows API functions */
156
157 PyDoc_STRVAR(GetStdHandle_doc,
158 "GetStdHandle(handle) -> integer\n\
159 \n\
160 Return a handle to the specified standard device\n\
161 (STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE).\n\
162 The integer associated with the handle object is returned.");
163
164 static PyObject *
sp_GetStdHandle(PyObject * self,PyObject * args)165 sp_GetStdHandle(PyObject* self, PyObject* args)
166 {
167 HANDLE handle;
168 int std_handle;
169
170 if (! PyArg_ParseTuple(args, "i:GetStdHandle", &std_handle))
171 return NULL;
172
173 Py_BEGIN_ALLOW_THREADS
174 handle = GetStdHandle((DWORD) std_handle);
175 Py_END_ALLOW_THREADS
176
177 if (handle == INVALID_HANDLE_VALUE)
178 return PyErr_SetFromWindowsErr(GetLastError());
179
180 if (! handle) {
181 Py_INCREF(Py_None);
182 return Py_None;
183 }
184
185 /* note: returns integer, not handle object */
186 return HANDLE_TO_PYNUM(handle);
187 }
188
189 PyDoc_STRVAR(GetCurrentProcess_doc,
190 "GetCurrentProcess() -> handle\n\
191 \n\
192 Return a handle object for the current process.");
193
194 static PyObject *
sp_GetCurrentProcess(PyObject * self,PyObject * args)195 sp_GetCurrentProcess(PyObject* self, PyObject* args)
196 {
197 if (! PyArg_ParseTuple(args, ":GetCurrentProcess"))
198 return NULL;
199
200 return sp_handle_new(GetCurrentProcess());
201 }
202
203 PyDoc_STRVAR(DuplicateHandle_doc,
204 "DuplicateHandle(source_proc_handle, source_handle,\n\
205 target_proc_handle, target_handle, access,\n\
206 inherit[, options]) -> handle\n\
207 \n\
208 Return a duplicate handle object.\n\
209 \n\
210 The duplicate handle refers to the same object as the original\n\
211 handle. Therefore, any changes to the object are reflected\n\
212 through both handles.");
213
214 static PyObject *
sp_DuplicateHandle(PyObject * self,PyObject * args)215 sp_DuplicateHandle(PyObject* self, PyObject* args)
216 {
217 HANDLE target_handle;
218 BOOL result;
219
220 HANDLE source_process_handle;
221 HANDLE source_handle;
222 HANDLE target_process_handle;
223 int desired_access;
224 int inherit_handle;
225 int options = 0;
226
227 if (! PyArg_ParseTuple(args,
228 PY_HANDLE_PARAM PY_HANDLE_PARAM PY_HANDLE_PARAM
229 "ii|i:DuplicateHandle",
230 &source_process_handle,
231 &source_handle,
232 &target_process_handle,
233 &desired_access,
234 &inherit_handle,
235 &options))
236 return NULL;
237
238 Py_BEGIN_ALLOW_THREADS
239 result = DuplicateHandle(
240 source_process_handle,
241 source_handle,
242 target_process_handle,
243 &target_handle,
244 desired_access,
245 inherit_handle,
246 options
247 );
248 Py_END_ALLOW_THREADS
249
250 if (! result)
251 return PyErr_SetFromWindowsErr(GetLastError());
252
253 return sp_handle_new(target_handle);
254 }
255
256 PyDoc_STRVAR(CreatePipe_doc,
257 "CreatePipe(pipe_attrs, size) -> (read_handle, write_handle)\n\
258 \n\
259 Create an anonymous pipe, and return handles to the read and\n\
260 write ends of the pipe.\n\
261 \n\
262 pipe_attrs is ignored internally and can be None.");
263
264 static PyObject *
sp_CreatePipe(PyObject * self,PyObject * args)265 sp_CreatePipe(PyObject* self, PyObject* args)
266 {
267 HANDLE read_pipe;
268 HANDLE write_pipe;
269 BOOL result;
270
271 PyObject* pipe_attributes; /* ignored */
272 int size;
273
274 if (! PyArg_ParseTuple(args, "Oi:CreatePipe", &pipe_attributes, &size))
275 return NULL;
276
277 Py_BEGIN_ALLOW_THREADS
278 result = CreatePipe(&read_pipe, &write_pipe, NULL, size);
279 Py_END_ALLOW_THREADS
280
281 if (! result)
282 return PyErr_SetFromWindowsErr(GetLastError());
283
284 return Py_BuildValue(
285 "NN", sp_handle_new(read_pipe), sp_handle_new(write_pipe));
286 }
287
288 /* helpers for createprocess */
289
290 static int
getint(PyObject * obj,char * name)291 getint(PyObject* obj, char* name)
292 {
293 PyObject* value;
294 int ret;
295
296 value = PyObject_GetAttrString(obj, name);
297 if (! value) {
298 PyErr_Clear(); /* FIXME: propagate error? */
299 return 0;
300 }
301 ret = (int) PyInt_AsLong(value);
302 Py_DECREF(value);
303 return ret;
304 }
305
306 static HANDLE
gethandle(PyObject * obj,char * name)307 gethandle(PyObject* obj, char* name)
308 {
309 sp_handle_object* value;
310 HANDLE ret;
311
312 value = (sp_handle_object*) PyObject_GetAttrString(obj, name);
313 if (! value) {
314 PyErr_Clear(); /* FIXME: propagate error? */
315 return NULL;
316 }
317 if (value->ob_type != &sp_handle_type)
318 ret = NULL;
319 else
320 ret = value->handle;
321 Py_DECREF(value);
322 return ret;
323 }
324
325 static PyObject*
getenvironment(PyObject * environment)326 getenvironment(PyObject* environment)
327 {
328 int i, envsize;
329 PyObject* out = NULL;
330 PyObject* keys;
331 PyObject* values;
332 char* p;
333
334 /* convert environment dictionary to windows environment string */
335 if (! PyMapping_Check(environment)) {
336 PyErr_SetString(
337 PyExc_TypeError, "environment must be dictionary or None");
338 return NULL;
339 }
340
341 envsize = PyMapping_Length(environment);
342
343 keys = PyMapping_Keys(environment);
344 if (!keys) {
345 return NULL;
346 }
347 values = PyMapping_Values(environment);
348 if (!values) {
349 goto error;
350 }
351
352 out = PyString_FromStringAndSize(NULL, 2048);
353 if (! out)
354 goto error;
355
356 p = PyString_AS_STRING(out);
357
358 for (i = 0; i < envsize; i++) {
359 size_t ksize, vsize, totalsize;
360 PyObject* key = PyList_GET_ITEM(keys, i);
361 PyObject* value = PyList_GET_ITEM(values, i);
362
363 if (! PyString_Check(key) || ! PyString_Check(value)) {
364 PyErr_SetString(PyExc_TypeError,
365 "environment can only contain strings");
366 goto error;
367 }
368 ksize = PyString_GET_SIZE(key);
369 vsize = PyString_GET_SIZE(value);
370 if (strlen(PyString_AS_STRING(key)) != ksize ||
371 strlen(PyString_AS_STRING(value)) != vsize)
372 {
373 PyErr_SetString(PyExc_TypeError, "embedded null character");
374 goto error;
375 }
376 /* Search from index 1 because on Windows starting '=' is allowed for
377 defining hidden environment variables. */
378 if (ksize == 0 || strchr(PyString_AS_STRING(key) + 1, '=') != NULL) {
379 PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
380 goto error;
381 }
382 totalsize = (p - PyString_AS_STRING(out)) + ksize + 1 +
383 vsize + 1 + 1;
384 if (totalsize > (size_t)PyString_GET_SIZE(out)) {
385 size_t offset = p - PyString_AS_STRING(out);
386 if (_PyString_Resize(&out, totalsize + 1024))
387 goto exit;
388 p = PyString_AS_STRING(out) + offset;
389 }
390 memcpy(p, PyString_AS_STRING(key), ksize);
391 p += ksize;
392 *p++ = '=';
393 memcpy(p, PyString_AS_STRING(value), vsize);
394 p += vsize;
395 *p++ = '\0';
396 }
397
398 /* add trailing null byte */
399 *p++ = '\0';
400 _PyString_Resize(&out, p - PyString_AS_STRING(out));
401
402 /* PyObject_Print(out, stdout, 0); */
403 exit:
404 Py_XDECREF(keys);
405 Py_XDECREF(values);
406
407 return out;
408
409 error:
410 Py_XDECREF(out);
411 Py_XDECREF(keys);
412 Py_XDECREF(values);
413 return NULL;
414 }
415
416 PyDoc_STRVAR(CreateProcess_doc,
417 "CreateProcess(app_name, cmd_line, proc_attrs, thread_attrs,\n\
418 inherit, flags, env_mapping, curdir,\n\
419 startup_info) -> (proc_handle, thread_handle,\n\
420 pid, tid)\n\
421 \n\
422 Create a new process and its primary thread. The return\n\
423 value is a tuple of the process handle, thread handle,\n\
424 process ID, and thread ID.\n\
425 \n\
426 proc_attrs and thread_attrs are ignored internally and can be None.");
427
428 static PyObject *
sp_CreateProcess(PyObject * self,PyObject * args)429 sp_CreateProcess(PyObject* self, PyObject* args)
430 {
431 BOOL result;
432 PROCESS_INFORMATION pi;
433 STARTUPINFO si;
434 PyObject* environment;
435
436 char* application_name;
437 char* command_line;
438 PyObject* process_attributes; /* ignored */
439 PyObject* thread_attributes; /* ignored */
440 int inherit_handles;
441 int creation_flags;
442 PyObject* env_mapping;
443 char* current_directory;
444 PyObject* startup_info;
445
446 if (! PyArg_ParseTuple(args, "zzOOiiOzO:CreateProcess",
447 &application_name,
448 &command_line,
449 &process_attributes,
450 &thread_attributes,
451 &inherit_handles,
452 &creation_flags,
453 &env_mapping,
454 ¤t_directory,
455 &startup_info))
456 return NULL;
457
458 ZeroMemory(&si, sizeof(si));
459 si.cb = sizeof(si);
460
461 /* note: we only support a small subset of all SI attributes */
462 si.dwFlags = getint(startup_info, "dwFlags");
463 si.wShowWindow = getint(startup_info, "wShowWindow");
464 si.hStdInput = gethandle(startup_info, "hStdInput");
465 si.hStdOutput = gethandle(startup_info, "hStdOutput");
466 si.hStdError = gethandle(startup_info, "hStdError");
467
468 if (PyErr_Occurred())
469 return NULL;
470
471 if (env_mapping == Py_None)
472 environment = NULL;
473 else {
474 environment = getenvironment(env_mapping);
475 if (! environment)
476 return NULL;
477 }
478
479 Py_BEGIN_ALLOW_THREADS
480 result = CreateProcess(application_name,
481 command_line,
482 NULL,
483 NULL,
484 inherit_handles,
485 creation_flags,
486 environment ? PyString_AS_STRING(environment) : NULL,
487 current_directory,
488 &si,
489 &pi);
490 Py_END_ALLOW_THREADS
491
492 Py_XDECREF(environment);
493
494 if (! result)
495 return PyErr_SetFromWindowsErr(GetLastError());
496
497 return Py_BuildValue("NNii",
498 sp_handle_new(pi.hProcess),
499 sp_handle_new(pi.hThread),
500 pi.dwProcessId,
501 pi.dwThreadId);
502 }
503
504 PyDoc_STRVAR(TerminateProcess_doc,
505 "TerminateProcess(handle, exit_code) -> None\n\
506 \n\
507 Terminate the specified process and all of its threads.");
508
509 static PyObject *
sp_TerminateProcess(PyObject * self,PyObject * args)510 sp_TerminateProcess(PyObject* self, PyObject* args)
511 {
512 BOOL result;
513
514 HANDLE process;
515 int exit_code;
516 if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM "i:TerminateProcess",
517 &process, &exit_code))
518 return NULL;
519
520 result = TerminateProcess(process, exit_code);
521
522 if (! result)
523 return PyErr_SetFromWindowsErr(GetLastError());
524
525 Py_INCREF(Py_None);
526 return Py_None;
527 }
528
529 PyDoc_STRVAR(GetExitCodeProcess_doc,
530 "GetExitCodeProcess(handle) -> Exit code\n\
531 \n\
532 Return the termination status of the specified process.");
533
534 static PyObject *
sp_GetExitCodeProcess(PyObject * self,PyObject * args)535 sp_GetExitCodeProcess(PyObject* self, PyObject* args)
536 {
537 DWORD exit_code;
538 BOOL result;
539
540 HANDLE process;
541 if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM ":GetExitCodeProcess", &process))
542 return NULL;
543
544 result = GetExitCodeProcess(process, &exit_code);
545
546 if (! result)
547 return PyErr_SetFromWindowsErr(GetLastError());
548
549 return PyInt_FromLong(exit_code);
550 }
551
552 PyDoc_STRVAR(WaitForSingleObject_doc,
553 "WaitForSingleObject(handle, timeout) -> result\n\
554 \n\
555 Wait until the specified object is in the signaled state or\n\
556 the time-out interval elapses. The timeout value is specified\n\
557 in milliseconds.");
558
559 static PyObject *
sp_WaitForSingleObject(PyObject * self,PyObject * args)560 sp_WaitForSingleObject(PyObject* self, PyObject* args)
561 {
562 DWORD result;
563
564 HANDLE handle;
565 int milliseconds;
566 if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM "i:WaitForSingleObject",
567 &handle,
568 &milliseconds))
569 return NULL;
570
571 Py_BEGIN_ALLOW_THREADS
572 result = WaitForSingleObject(handle, (DWORD) milliseconds);
573 Py_END_ALLOW_THREADS
574
575 if (result == WAIT_FAILED)
576 return PyErr_SetFromWindowsErr(GetLastError());
577
578 return PyInt_FromLong((int) result);
579 }
580
581 PyDoc_STRVAR(GetVersion_doc,
582 "GetVersion() -> version\n\
583 \n\
584 Return the version number of the current operating system.");
585
586 static PyObject *
sp_GetVersion(PyObject * self,PyObject * args)587 sp_GetVersion(PyObject* self, PyObject* args)
588 {
589 if (! PyArg_ParseTuple(args, ":GetVersion"))
590 return NULL;
591
592 return PyInt_FromLong((int) GetVersion());
593 }
594
595 PyDoc_STRVAR(GetModuleFileName_doc,
596 "GetModuleFileName(module) -> path\n\
597 \n\
598 Return the fully-qualified path for the file that contains\n\
599 the specified module. The module must have been loaded by the\n\
600 current process.\n\
601 \n\
602 The module parameter should be a handle to the loaded module\n\
603 whose path is being requested. If this parameter is 0, \n\
604 GetModuleFileName retrieves the path of the executable file\n\
605 of the current process.");
606
607 static PyObject *
sp_GetModuleFileName(PyObject * self,PyObject * args)608 sp_GetModuleFileName(PyObject* self, PyObject* args)
609 {
610 BOOL result;
611 HMODULE module;
612 TCHAR filename[MAX_PATH];
613
614 if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM ":GetModuleFileName",
615 &module))
616 return NULL;
617
618 result = GetModuleFileName(module, filename, MAX_PATH);
619 filename[MAX_PATH-1] = '\0';
620
621 if (! result)
622 return PyErr_SetFromWindowsErr(GetLastError());
623
624 return PyString_FromString(filename);
625 }
626
627 static PyMethodDef sp_functions[] = {
628 {"GetStdHandle", sp_GetStdHandle, METH_VARARGS, GetStdHandle_doc},
629 {"GetCurrentProcess", sp_GetCurrentProcess, METH_VARARGS,
630 GetCurrentProcess_doc},
631 {"DuplicateHandle", sp_DuplicateHandle, METH_VARARGS,
632 DuplicateHandle_doc},
633 {"CreatePipe", sp_CreatePipe, METH_VARARGS, CreatePipe_doc},
634 {"CreateProcess", sp_CreateProcess, METH_VARARGS, CreateProcess_doc},
635 {"TerminateProcess", sp_TerminateProcess, METH_VARARGS,
636 TerminateProcess_doc},
637 {"GetExitCodeProcess", sp_GetExitCodeProcess, METH_VARARGS,
638 GetExitCodeProcess_doc},
639 {"WaitForSingleObject", sp_WaitForSingleObject, METH_VARARGS,
640 WaitForSingleObject_doc},
641 {"GetVersion", sp_GetVersion, METH_VARARGS, GetVersion_doc},
642 {"GetModuleFileName", sp_GetModuleFileName, METH_VARARGS,
643 GetModuleFileName_doc},
644 {NULL, NULL}
645 };
646
647 /* -------------------------------------------------------------------- */
648
649 static void
defint(PyObject * d,const char * name,int value)650 defint(PyObject* d, const char* name, int value)
651 {
652 PyObject* v = PyInt_FromLong((long) value);
653 if (v) {
654 PyDict_SetItemString(d, (char*) name, v);
655 Py_DECREF(v);
656 }
657 }
658
659 #if PY_VERSION_HEX >= 0x02030000
660 PyMODINIT_FUNC
661 #else
662 DL_EXPORT(void)
663 #endif
init_subprocess()664 init_subprocess()
665 {
666 PyObject *d;
667 PyObject *m;
668
669 /* patch up object descriptors */
670 sp_handle_type.ob_type = &PyType_Type;
671 sp_handle_as_number.nb_int = (unaryfunc) sp_handle_as_int;
672
673 m = Py_InitModule("_subprocess", sp_functions);
674 if (m == NULL)
675 return;
676 d = PyModule_GetDict(m);
677
678 /* constants */
679 defint(d, "STD_INPUT_HANDLE", STD_INPUT_HANDLE);
680 defint(d, "STD_OUTPUT_HANDLE", STD_OUTPUT_HANDLE);
681 defint(d, "STD_ERROR_HANDLE", STD_ERROR_HANDLE);
682 defint(d, "DUPLICATE_SAME_ACCESS", DUPLICATE_SAME_ACCESS);
683 defint(d, "STARTF_USESTDHANDLES", STARTF_USESTDHANDLES);
684 defint(d, "STARTF_USESHOWWINDOW", STARTF_USESHOWWINDOW);
685 defint(d, "SW_HIDE", SW_HIDE);
686 defint(d, "INFINITE", INFINITE);
687 defint(d, "WAIT_OBJECT_0", WAIT_OBJECT_0);
688 defint(d, "CREATE_NEW_CONSOLE", CREATE_NEW_CONSOLE);
689 defint(d, "CREATE_NEW_PROCESS_GROUP", CREATE_NEW_PROCESS_GROUP);
690 defint(d, "STILL_ACTIVE", STILL_ACTIVE);
691 }
692