blob: 1f73daa64ae8281041e0d56bb9cc95f12b95593e [file] [log] [blame]
Fredrik Lundh5b3687d2004-10-12 15:26:28 +00001/*
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/* TODO: handle unicode command lines? */
36/* TODO: handle unicode environment? */
37
38#include "Python.h"
39
40#define WINDOWS_LEAN_AND_MEAN
41#include "windows.h"
42
43/* -------------------------------------------------------------------- */
44/* handle wrapper. note that this library uses integers when passing
45 handles to a function, and handle wrappers when returning handles.
46 the wrapper is used to provide Detach and Close methods */
47
48typedef struct {
49 PyObject_HEAD
50 HANDLE handle;
51} sp_handle_object;
52
53staticforward PyTypeObject sp_handle_type;
54
55static PyObject*
56sp_handle_new(HANDLE handle)
57{
58 sp_handle_object* self;
59
60 self = PyObject_NEW(sp_handle_object, &sp_handle_type);
61 if (self == NULL)
62 return NULL;
63
64 self->handle = handle;
65
66 return (PyObject*) self;
67}
68
69static PyObject*
70sp_handle_detach(sp_handle_object* self, PyObject* args)
71{
72 HANDLE handle;
73
74 if (!PyArg_ParseTuple(args, ":Detach"))
75 return NULL;
76
77 handle = self->handle;
78
79 self->handle = NULL;
80
81 /* note: return the current handle, as an integer */
82 return PyInt_FromLong((long) handle);
83}
84
85static PyObject*
86sp_handle_close(sp_handle_object* self, PyObject* args)
87{
88 if (!PyArg_ParseTuple(args, ":Close"))
89 return NULL;
90
91 if (self->handle != INVALID_HANDLE_VALUE) {
92 CloseHandle(self->handle);
93 self->handle = INVALID_HANDLE_VALUE;
94 }
95 Py_INCREF(Py_None);
96 return Py_None;
97}
98
99static void
100sp_handle_dealloc(sp_handle_object* self)
101{
102 if (self->handle != INVALID_HANDLE_VALUE)
103 CloseHandle(self->handle);
104 PyMem_DEL(self);
105}
106
107static PyMethodDef sp_handle_methods[] = {
108 {"Detach", (PyCFunction) sp_handle_detach, 1},
109 {"Close", (PyCFunction) sp_handle_close, 1},
110 {NULL, NULL}
111};
112
113static PyObject*
114sp_handle_getattr(sp_handle_object* self, char* name)
115{
116 return Py_FindMethod(sp_handle_methods, (PyObject*) self, name);
117}
118
119static PyObject*
120sp_handle_as_int(sp_handle_object* self)
121{
122 return PyInt_FromLong((long) self->handle);
123}
124
125static PyNumberMethods sp_handle_as_number;
126
127statichere PyTypeObject sp_handle_type = {
128 PyObject_HEAD_INIT(NULL)
129 0, /*ob_size*/
130 "_subprocess_handle", sizeof(sp_handle_object), 0,
131 (destructor) sp_handle_dealloc, /*tp_dealloc*/
132 0, /*tp_print*/
133 (getattrfunc) sp_handle_getattr, /*tp_getattr*/
134 0, /*tp_setattr*/
135 0, /*tp_compare*/
136 0, /*tp_repr*/
137 &sp_handle_as_number, /*tp_as_number */
138 0, /*tp_as_sequence */
139 0, /*tp_as_mapping */
140 0 /*tp_hash*/
141};
142
143/* -------------------------------------------------------------------- */
144/* windows API functions */
145
146static PyObject *
147sp_GetStdHandle(PyObject* self, PyObject* args)
148{
149 HANDLE handle;
150
151 int std_handle;
152 if (!PyArg_ParseTuple(args, "i:GetStdHandle", &std_handle))
153 return NULL;
154
155 Py_BEGIN_ALLOW_THREADS
156 handle = GetStdHandle((DWORD) std_handle);
157 Py_END_ALLOW_THREADS
158
159 if (handle == INVALID_HANDLE_VALUE)
160 return PyErr_SetFromWindowsErr(GetLastError());
161
162 if (!handle) {
163 Py_INCREF(Py_None);
164 return Py_None;
165 }
166
167 /* note: returns integer, not handle object */
168 return PyInt_FromLong((long) handle);
169}
170
171static PyObject *
172sp_GetCurrentProcess(PyObject* self, PyObject* args)
173{
174 if (!PyArg_ParseTuple(args, ":GetCurrentProcess"))
175 return NULL;
176
177 return sp_handle_new(GetCurrentProcess());
178}
179
180static PyObject *
181sp_DuplicateHandle(PyObject* self, PyObject* args)
182{
183 HANDLE target_handle;
184 BOOL result;
185
186 long source_process_handle;
187 long source_handle;
188 long target_process_handle;
189 int desired_access;
190 int inherit_handle;
191 int options = 0;
192 if (!PyArg_ParseTuple(args, "lllii|i:DuplicateHandle",
193 &source_process_handle, &source_handle,
194 &target_process_handle,
195 &desired_access, &inherit_handle, &options))
196 return NULL;
197
198 Py_BEGIN_ALLOW_THREADS
199 result = DuplicateHandle(
200 (HANDLE) source_process_handle, (HANDLE) source_handle,
201 (HANDLE) target_process_handle, &target_handle, desired_access,
202 inherit_handle, options
203 );
204 Py_END_ALLOW_THREADS
205
206 if (!result)
207 return PyErr_SetFromWindowsErr(GetLastError());
208
209 return sp_handle_new(target_handle);
210}
211
212static PyObject *
213sp_CreatePipe(PyObject* self, PyObject* args)
214{
215 HANDLE read_pipe;
216 HANDLE write_pipe;
217 BOOL result;
218
219 PyObject* pipe_attributes; /* ignored */
220 int size;
221 if (!PyArg_ParseTuple(args, "Oi:CreatePipe", &pipe_attributes, &size))
222 return NULL;
223
224 Py_BEGIN_ALLOW_THREADS
225 result = CreatePipe(&read_pipe, &write_pipe, NULL, size);
226 Py_END_ALLOW_THREADS
227
228 if (!result)
229 return PyErr_SetFromWindowsErr(GetLastError());
230
231 return Py_BuildValue(
232 "NN", sp_handle_new(read_pipe), sp_handle_new(write_pipe)
233 );
234}
235
236/* helpers for createprocess */
237
238static int
239getint(PyObject* obj, char* name)
240{
241 PyObject* value;
242 value = PyObject_GetAttrString(obj, name);
243 if (!value) {
244 PyErr_Clear(); /* FIXME: propagate error? */
245 return 0;
246 }
247 return (int) PyInt_AsLong(value);
248}
249
250static HANDLE
251gethandle(PyObject* obj, char* name)
252{
253 sp_handle_object* value;
254 value = (sp_handle_object*) PyObject_GetAttrString(obj, name);
255 if (!value) {
256 PyErr_Clear(); /* FIXME: propagate error? */
257 return NULL;
258 }
259 if (value->ob_type != &sp_handle_type)
260 return NULL;
261 return value->handle;
262}
263
264static PyObject*
265getenvironment(PyObject* environment)
266{
267 int i, envsize;
268 PyObject* out = NULL;
269 PyObject* keys;
270 PyObject* values;
271 char* p;
272
273 /* convert environment dictionary to windows enviroment string */
274
275 if (!PyMapping_Check(environment)) {
276 PyErr_SetString(
277 PyExc_TypeError, "environment must be dictionary or None"
278 );
279 return NULL;
280 }
281
282 envsize = PyMapping_Length(environment);
283
284 keys = PyMapping_Keys(environment);
285 values = PyMapping_Values(environment);
286 if (!keys || !values)
287 goto error;
288
289 out = PyString_FromStringAndSize(NULL, 2048);
290 if (!out)
291 goto error;
292
293 p = PyString_AS_STRING(out);
294
295 for (i = 0; i < envsize; i++) {
296 int ksize, vsize, totalsize;
297 PyObject* key = PyList_GET_ITEM(keys, i);
298 PyObject* value = PyList_GET_ITEM(values, i);
299 if (!PyString_Check(key) || !PyString_Check(value)) {
300 PyErr_SetString(
301 PyExc_TypeError, "environment can only contain strings"
302 );
303 goto error;
304 }
305 ksize = PyString_GET_SIZE(key);
306 vsize = PyString_GET_SIZE(value);
307 totalsize = (p - PyString_AS_STRING(out)) + ksize + 1 + vsize + 1 + 1;
308 if (totalsize > PyString_GET_SIZE(out)) {
309 int offset = p - PyString_AS_STRING(out);
310 _PyString_Resize(&out, totalsize + 1024);
311 p = PyString_AS_STRING(out) + offset;
312 }
313 memcpy(p, PyString_AS_STRING(key), ksize); p += ksize; *p++ = '=';
314 memcpy(p, PyString_AS_STRING(value), vsize); p += vsize; *p++ = '\0';
315 }
316
317 /* add trailing null byte */
318 *p++ = '\0';
319
320 _PyString_Resize(&out, p - PyString_AS_STRING(out));
321
322 /* PyObject_Print(out, stdout, 0); */
323
324 return out;
325
326error:
327 Py_XDECREF(out);
328 Py_XDECREF(keys);
329 Py_XDECREF(values);
330 return NULL;
331}
332
333static PyObject *
334sp_CreateProcess(PyObject* self, PyObject* args)
335{
336 BOOL result;
337 PROCESS_INFORMATION pi;
338 STARTUPINFO si;
339 PyObject* environment;
340
341 char* application_name;
342 char* command_line;
343 PyObject* process_attributes; /* ignored */
344 PyObject* thread_attributes; /* ignored */
345 int inherit_handles;
346 int creation_flags;
347 PyObject* env_mapping;
348 char* current_directory;
349 PyObject* startup_info;
350 if (!PyArg_ParseTuple(args, "zzOOiiOzO:CreateProcess",
351 &application_name, &command_line,
352 &process_attributes, &thread_attributes,
353 &inherit_handles, &creation_flags,
354 &env_mapping, &current_directory, &startup_info))
355 return NULL;
356
357 ZeroMemory(&si, sizeof(si));
358 si.cb = sizeof(si);
359
360 /* note: we only support a small subset of all SI attributes */
361 si.dwFlags = getint(startup_info, "dwFlags");
362 si.hStdInput = gethandle(startup_info, "hStdInput");
363 si.hStdOutput = gethandle(startup_info, "hStdOutput");
364 si.hStdError = gethandle(startup_info, "hStdError");
365
366 if (env_mapping == Py_None)
367 environment = NULL;
368 else {
369 environment = getenvironment(env_mapping);
370 if (!environment)
371 return NULL;
372 }
373
374 Py_BEGIN_ALLOW_THREADS
375 result = CreateProcess(
376 application_name, command_line, NULL, NULL, inherit_handles,
377 creation_flags, environment ? PyString_AS_STRING(environment) : NULL,
378 current_directory, &si, &pi
379 );
380 Py_END_ALLOW_THREADS
381
382 Py_XDECREF(environment);
383
384 if (!result)
385 return PyErr_SetFromWindowsErr(GetLastError());
386
387 return Py_BuildValue(
388 "NNii", sp_handle_new(pi.hProcess), sp_handle_new(pi.hThread),
389 pi.dwProcessId, pi.dwThreadId
390 );
391}
392
393static PyObject *
394sp_GetExitCodeProcess(PyObject* self, PyObject* args)
395{
396 DWORD exit_code;
397 BOOL result;
398
399 long process;
400 if (!PyArg_ParseTuple(args, "l:GetExitCodeProcess", &process))
401 return NULL;
402
403 result = GetExitCodeProcess((HANDLE) process, &exit_code);
404
405 if (!result)
406 return PyErr_SetFromWindowsErr(GetLastError());
407
408 return PyInt_FromLong(exit_code);
409}
410
411static PyObject *
412sp_WaitForSingleObject(PyObject* self, PyObject* args)
413{
414 DWORD result;
415
416 long handle;
417 int milliseconds;
418 if (!PyArg_ParseTuple(args, "li:WaitForSingleObject",
419 &handle, &milliseconds))
420 return NULL;
421
422 Py_BEGIN_ALLOW_THREADS
423 result = WaitForSingleObject((HANDLE) handle, (DWORD) milliseconds);
424 Py_END_ALLOW_THREADS
425
426 if (result == WAIT_FAILED)
427 return PyErr_SetFromWindowsErr(GetLastError());
428
429 return PyInt_FromLong((int) result);
430}
431
432static PyObject *
433sp_GetVersion(PyObject* self, PyObject* args)
434{
435 if (!PyArg_ParseTuple(args, ":GetVersion"))
436 return NULL;
437
438 return PyInt_FromLong((int) GetVersion());
439}
440
441static PyObject *
442sp_GetModuleFileName(PyObject* self, PyObject* args)
443{
444 BOOL result;
445 long module;
446 TCHAR filename[MAX_PATH];
447
448 if (!PyArg_ParseTuple(args, "l:GetModuleFileName", &module))
449 return NULL;
450
451 result = GetModuleFileName((HMODULE)module, filename, MAX_PATH);
452 filename[MAX_PATH-1] = '\0';
453
454 if (!result)
455 return PyErr_SetFromWindowsErr(GetLastError());
456
457 return PyString_FromString(filename);
458}
459
460static PyMethodDef sp_functions[] = {
461 {"GetStdHandle", sp_GetStdHandle, METH_VARARGS},
462 {"GetCurrentProcess", sp_GetCurrentProcess, METH_VARARGS},
463 {"DuplicateHandle", sp_DuplicateHandle, METH_VARARGS},
464 {"CreatePipe", sp_CreatePipe, METH_VARARGS},
465 {"CreateProcess", sp_CreateProcess, METH_VARARGS},
466 {"GetExitCodeProcess", sp_GetExitCodeProcess, METH_VARARGS},
467 {"WaitForSingleObject", sp_WaitForSingleObject, METH_VARARGS},
468 {"GetVersion", sp_GetVersion, METH_VARARGS},
469 {"GetModuleFileName", sp_GetModuleFileName, METH_VARARGS},
470 {NULL, NULL}
471};
472
473/* -------------------------------------------------------------------- */
474
475static void
476defint(PyObject* d, const char* name, int value)
477{
478 PyObject* v = PyInt_FromLong((long) value);
479 if (v) {
480 PyDict_SetItemString(d, (char*) name, v);
481 Py_DECREF(v);
482 }
483}
484
485#if PY_VERSION_HEX >= 0x02030000
486PyMODINIT_FUNC
487#else
488DL_EXPORT(void)
489#endif
490init_subprocess()
491{
492 PyObject *d;
493 PyObject *m;
494
495 /* patch up object descriptors */
496 sp_handle_type.ob_type = &PyType_Type;
497 sp_handle_as_number.nb_int = (unaryfunc) sp_handle_as_int;
498
499 m = Py_InitModule("_subprocess", sp_functions);
500 d = PyModule_GetDict(m);
501
502 /* constants */
503 defint(d, "STD_INPUT_HANDLE", STD_INPUT_HANDLE);
504 defint(d, "STD_OUTPUT_HANDLE", STD_OUTPUT_HANDLE);
505 defint(d, "STD_ERROR_HANDLE", STD_ERROR_HANDLE);
506 defint(d, "DUPLICATE_SAME_ACCESS", DUPLICATE_SAME_ACCESS);
507 defint(d, "STARTF_USESTDHANDLES", STARTF_USESTDHANDLES);
508 defint(d, "INFINITE", INFINITE);
509 defint(d, "WAIT_OBJECT_0", WAIT_OBJECT_0);
510 defint(d, "CREATE_NEW_CONSOLE", CREATE_NEW_CONSOLE);
511}