• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     values = PyMapping_Values(environment);
345     if (!keys || !values)
346         goto error;
347 
348     out = PyString_FromStringAndSize(NULL, 2048);
349     if (! out)
350         goto error;
351 
352     p = PyString_AS_STRING(out);
353 
354     for (i = 0; i < envsize; i++) {
355         int ksize, vsize, totalsize;
356         PyObject* key = PyList_GET_ITEM(keys, i);
357         PyObject* value = PyList_GET_ITEM(values, i);
358 
359         if (! PyString_Check(key) || ! PyString_Check(value)) {
360             PyErr_SetString(PyExc_TypeError,
361                 "environment can only contain strings");
362             goto error;
363         }
364         ksize = PyString_GET_SIZE(key);
365         vsize = PyString_GET_SIZE(value);
366         totalsize = (p - PyString_AS_STRING(out)) + ksize + 1 +
367                                                      vsize + 1 + 1;
368         if (totalsize > PyString_GET_SIZE(out)) {
369             int offset = p - PyString_AS_STRING(out);
370             if (_PyString_Resize(&out, totalsize + 1024))
371                 goto exit;
372             p = PyString_AS_STRING(out) + offset;
373         }
374         memcpy(p, PyString_AS_STRING(key), ksize);
375         p += ksize;
376         *p++ = '=';
377         memcpy(p, PyString_AS_STRING(value), vsize);
378         p += vsize;
379         *p++ = '\0';
380     }
381 
382     /* add trailing null byte */
383     *p++ = '\0';
384     _PyString_Resize(&out, p - PyString_AS_STRING(out));
385 
386     /* PyObject_Print(out, stdout, 0); */
387 exit:
388     Py_XDECREF(keys);
389     Py_XDECREF(values);
390 
391     return out;
392 
393  error:
394     Py_XDECREF(out);
395     Py_XDECREF(keys);
396     Py_XDECREF(values);
397     return NULL;
398 }
399 
400 PyDoc_STRVAR(CreateProcess_doc,
401 "CreateProcess(app_name, cmd_line, proc_attrs, thread_attrs,\n\
402                inherit, flags, env_mapping, curdir,\n\
403                startup_info) -> (proc_handle, thread_handle,\n\
404                                  pid, tid)\n\
405 \n\
406 Create a new process and its primary thread. The return\n\
407 value is a tuple of the process handle, thread handle,\n\
408 process ID, and thread ID.\n\
409 \n\
410 proc_attrs and thread_attrs are ignored internally and can be None.");
411 
412 static PyObject *
sp_CreateProcess(PyObject * self,PyObject * args)413 sp_CreateProcess(PyObject* self, PyObject* args)
414 {
415     BOOL result;
416     PROCESS_INFORMATION pi;
417     STARTUPINFO si;
418     PyObject* environment;
419 
420     char* application_name;
421     char* command_line;
422     PyObject* process_attributes; /* ignored */
423     PyObject* thread_attributes; /* ignored */
424     int inherit_handles;
425     int creation_flags;
426     PyObject* env_mapping;
427     char* current_directory;
428     PyObject* startup_info;
429 
430     if (! PyArg_ParseTuple(args, "zzOOiiOzO:CreateProcess",
431                            &application_name,
432                            &command_line,
433                            &process_attributes,
434                            &thread_attributes,
435                            &inherit_handles,
436                            &creation_flags,
437                            &env_mapping,
438                            &current_directory,
439                            &startup_info))
440         return NULL;
441 
442     ZeroMemory(&si, sizeof(si));
443     si.cb = sizeof(si);
444 
445     /* note: we only support a small subset of all SI attributes */
446     si.dwFlags = getint(startup_info, "dwFlags");
447     si.wShowWindow = getint(startup_info, "wShowWindow");
448     si.hStdInput = gethandle(startup_info, "hStdInput");
449     si.hStdOutput = gethandle(startup_info, "hStdOutput");
450     si.hStdError = gethandle(startup_info, "hStdError");
451 
452     if (PyErr_Occurred())
453         return NULL;
454 
455     if (env_mapping == Py_None)
456         environment = NULL;
457     else {
458         environment = getenvironment(env_mapping);
459         if (! environment)
460             return NULL;
461     }
462 
463     Py_BEGIN_ALLOW_THREADS
464     result = CreateProcess(application_name,
465                            command_line,
466                            NULL,
467                            NULL,
468                            inherit_handles,
469                            creation_flags,
470                            environment ? PyString_AS_STRING(environment) : NULL,
471                            current_directory,
472                            &si,
473                            &pi);
474     Py_END_ALLOW_THREADS
475 
476     Py_XDECREF(environment);
477 
478     if (! result)
479         return PyErr_SetFromWindowsErr(GetLastError());
480 
481     return Py_BuildValue("NNii",
482                          sp_handle_new(pi.hProcess),
483                          sp_handle_new(pi.hThread),
484                          pi.dwProcessId,
485                          pi.dwThreadId);
486 }
487 
488 PyDoc_STRVAR(TerminateProcess_doc,
489 "TerminateProcess(handle, exit_code) -> None\n\
490 \n\
491 Terminate the specified process and all of its threads.");
492 
493 static PyObject *
sp_TerminateProcess(PyObject * self,PyObject * args)494 sp_TerminateProcess(PyObject* self, PyObject* args)
495 {
496     BOOL result;
497 
498     HANDLE process;
499     int exit_code;
500     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM "i:TerminateProcess",
501                            &process, &exit_code))
502         return NULL;
503 
504     result = TerminateProcess(process, exit_code);
505 
506     if (! result)
507         return PyErr_SetFromWindowsErr(GetLastError());
508 
509     Py_INCREF(Py_None);
510     return Py_None;
511 }
512 
513 PyDoc_STRVAR(GetExitCodeProcess_doc,
514 "GetExitCodeProcess(handle) -> Exit code\n\
515 \n\
516 Return the termination status of the specified process.");
517 
518 static PyObject *
sp_GetExitCodeProcess(PyObject * self,PyObject * args)519 sp_GetExitCodeProcess(PyObject* self, PyObject* args)
520 {
521     DWORD exit_code;
522     BOOL result;
523 
524     HANDLE process;
525     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM ":GetExitCodeProcess", &process))
526         return NULL;
527 
528     result = GetExitCodeProcess(process, &exit_code);
529 
530     if (! result)
531         return PyErr_SetFromWindowsErr(GetLastError());
532 
533     return PyInt_FromLong(exit_code);
534 }
535 
536 PyDoc_STRVAR(WaitForSingleObject_doc,
537 "WaitForSingleObject(handle, timeout) -> result\n\
538 \n\
539 Wait until the specified object is in the signaled state or\n\
540 the time-out interval elapses. The timeout value is specified\n\
541 in milliseconds.");
542 
543 static PyObject *
sp_WaitForSingleObject(PyObject * self,PyObject * args)544 sp_WaitForSingleObject(PyObject* self, PyObject* args)
545 {
546     DWORD result;
547 
548     HANDLE handle;
549     int milliseconds;
550     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM "i:WaitForSingleObject",
551                                  &handle,
552                                  &milliseconds))
553         return NULL;
554 
555     Py_BEGIN_ALLOW_THREADS
556     result = WaitForSingleObject(handle, (DWORD) milliseconds);
557     Py_END_ALLOW_THREADS
558 
559     if (result == WAIT_FAILED)
560         return PyErr_SetFromWindowsErr(GetLastError());
561 
562     return PyInt_FromLong((int) result);
563 }
564 
565 PyDoc_STRVAR(GetVersion_doc,
566 "GetVersion() -> version\n\
567 \n\
568 Return the version number of the current operating system.");
569 
570 static PyObject *
sp_GetVersion(PyObject * self,PyObject * args)571 sp_GetVersion(PyObject* self, PyObject* args)
572 {
573     if (! PyArg_ParseTuple(args, ":GetVersion"))
574         return NULL;
575 
576     return PyInt_FromLong((int) GetVersion());
577 }
578 
579 PyDoc_STRVAR(GetModuleFileName_doc,
580 "GetModuleFileName(module) -> path\n\
581 \n\
582 Return the fully-qualified path for the file that contains\n\
583 the specified module. The module must have been loaded by the\n\
584 current process.\n\
585 \n\
586 The module parameter should be a handle to the loaded module\n\
587 whose path is being requested. If this parameter is 0, \n\
588 GetModuleFileName retrieves the path of the executable file\n\
589 of the current process.");
590 
591 static PyObject *
sp_GetModuleFileName(PyObject * self,PyObject * args)592 sp_GetModuleFileName(PyObject* self, PyObject* args)
593 {
594     BOOL result;
595     HMODULE module;
596     TCHAR filename[MAX_PATH];
597 
598     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM ":GetModuleFileName",
599                            &module))
600         return NULL;
601 
602     result = GetModuleFileName(module, filename, MAX_PATH);
603     filename[MAX_PATH-1] = '\0';
604 
605     if (! result)
606         return PyErr_SetFromWindowsErr(GetLastError());
607 
608     return PyString_FromString(filename);
609 }
610 
611 static PyMethodDef sp_functions[] = {
612     {"GetStdHandle", sp_GetStdHandle, METH_VARARGS, GetStdHandle_doc},
613     {"GetCurrentProcess", sp_GetCurrentProcess,         METH_VARARGS,
614                                               GetCurrentProcess_doc},
615     {"DuplicateHandle",         sp_DuplicateHandle,     METH_VARARGS,
616                                             DuplicateHandle_doc},
617     {"CreatePipe", sp_CreatePipe, METH_VARARGS, CreatePipe_doc},
618     {"CreateProcess", sp_CreateProcess, METH_VARARGS, CreateProcess_doc},
619     {"TerminateProcess", sp_TerminateProcess, METH_VARARGS,
620                                              TerminateProcess_doc},
621     {"GetExitCodeProcess", sp_GetExitCodeProcess, METH_VARARGS,
622                                                GetExitCodeProcess_doc},
623     {"WaitForSingleObject", sp_WaitForSingleObject, METH_VARARGS,
624                                                     WaitForSingleObject_doc},
625     {"GetVersion", sp_GetVersion, METH_VARARGS, GetVersion_doc},
626     {"GetModuleFileName", sp_GetModuleFileName, METH_VARARGS,
627                                               GetModuleFileName_doc},
628     {NULL, NULL}
629 };
630 
631 /* -------------------------------------------------------------------- */
632 
633 static void
defint(PyObject * d,const char * name,int value)634 defint(PyObject* d, const char* name, int value)
635 {
636     PyObject* v = PyInt_FromLong((long) value);
637     if (v) {
638         PyDict_SetItemString(d, (char*) name, v);
639         Py_DECREF(v);
640     }
641 }
642 
643 #if PY_VERSION_HEX >= 0x02030000
644 PyMODINIT_FUNC
645 #else
646 DL_EXPORT(void)
647 #endif
init_subprocess()648 init_subprocess()
649 {
650     PyObject *d;
651     PyObject *m;
652 
653     /* patch up object descriptors */
654     sp_handle_type.ob_type = &PyType_Type;
655     sp_handle_as_number.nb_int = (unaryfunc) sp_handle_as_int;
656 
657     m = Py_InitModule("_subprocess", sp_functions);
658     if (m == NULL)
659         return;
660     d = PyModule_GetDict(m);
661 
662     /* constants */
663     defint(d, "STD_INPUT_HANDLE", STD_INPUT_HANDLE);
664     defint(d, "STD_OUTPUT_HANDLE", STD_OUTPUT_HANDLE);
665     defint(d, "STD_ERROR_HANDLE", STD_ERROR_HANDLE);
666     defint(d, "DUPLICATE_SAME_ACCESS", DUPLICATE_SAME_ACCESS);
667     defint(d, "STARTF_USESTDHANDLES", STARTF_USESTDHANDLES);
668     defint(d, "STARTF_USESHOWWINDOW", STARTF_USESHOWWINDOW);
669     defint(d, "SW_HIDE", SW_HIDE);
670     defint(d, "INFINITE", INFINITE);
671     defint(d, "WAIT_OBJECT_0", WAIT_OBJECT_0);
672     defint(d, "CREATE_NEW_CONSOLE", CREATE_NEW_CONSOLE);
673     defint(d, "CREATE_NEW_PROCESS_GROUP", CREATE_NEW_PROCESS_GROUP);
674     defint(d, "STILL_ACTIVE", STILL_ACTIVE);
675 }
676