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 ¤t_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