Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 56055a4

Browse files
author
Fredrik Lundh
committed
-- added code to the new Windows popen functions to make close
return the exit code. Only works on Windows NT/2000, due to limitations in the Win9X shell. (based on patch #100941 by David Bolen)
1 parent 8315ea5 commit 56055a4

1 file changed

Lines changed: 90 additions & 8 deletions

File tree

Modules/posixmodule.c

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,6 +2117,7 @@ posix_popen(PyObject *self, PyObject *args)
21172117
*
21182118
* Written by Bill Tutt <[email protected]>. Minor tweaks
21192119
* and 2.0 integration by Fredrik Lundh <[email protected]>
2120+
* Return code handling by David Bolen.
21202121
*/
21212122

21222123
#include <malloc.h>
@@ -2130,6 +2131,15 @@ posix_popen(PyObject *self, PyObject *args)
21302131
#define POPEN_4 4
21312132

21322133
static PyObject *_PyPopen(char *, int, int);
2134+
static int _PyPclose(FILE *file);
2135+
2136+
/*
2137+
* Internal dictionary mapping popen* file pointers to process handles,
2138+
* in order to maintain a link to the process handle until the file is
2139+
* closed, at which point the process exit code is returned to the caller.
2140+
*/
2141+
static PyObject *_PyPopenProcs = NULL;
2142+
21332143

21342144
/* popen that works from a GUI.
21352145
*
@@ -2285,7 +2295,7 @@ win32_popen4(PyObject *self, PyObject *args)
22852295
}
22862296

22872297
static int
2288-
_PyPopenCreateProcess(char *cmdstring,
2298+
_PyPopenCreateProcess(char *cmdstring, FILE *file,
22892299
HANDLE hStdin,
22902300
HANDLE hStdout,
22912301
HANDLE hStderr)
@@ -2361,8 +2371,28 @@ _PyPopenCreateProcess(char *cmdstring,
23612371
&siStartInfo,
23622372
&piProcInfo) ) {
23632373
/* Close the handles now so anyone waiting is woken. */
2364-
CloseHandle(piProcInfo.hProcess);
23652374
CloseHandle(piProcInfo.hThread);
2375+
2376+
/*
2377+
* Try to insert our process handle into the internal
2378+
* dictionary so we can find it later when trying
2379+
* to close this file.
2380+
*/
2381+
if (!_PyPopenProcs)
2382+
_PyPopenProcs = PyDict_New();
2383+
if (_PyPopenProcs) {
2384+
PyObject *hProcessObj, *fileObj;
2385+
2386+
hProcessObj = PyLong_FromVoidPtr(piProcInfo.hProcess);
2387+
fileObj = PyLong_FromVoidPtr(file);
2388+
2389+
if (!hProcessObj || !fileObj ||
2390+
PyDict_SetItem(_PyPopenProcs,
2391+
fileObj, hProcessObj) < 0) {
2392+
/* Insert failure - close handle to prevent leak */
2393+
CloseHandle(piProcInfo.hProcess);
2394+
}
2395+
}
23662396
return TRUE;
23672397
}
23682398
return FALSE;
@@ -2439,7 +2469,7 @@ _PyPopen(char *cmdstring, int mode, int n)
24392469
/* Case for writing to child Stdin in text mode. */
24402470
fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
24412471
f1 = _fdopen(fd1, "w");
2442-
f = PyFile_FromFile(f1, cmdstring, "w", fclose);
2472+
f = PyFile_FromFile(f1, cmdstring, "w", _PyPclose);
24432473
PyFile_SetBufSize(f, 0);
24442474
/* We don't care about these pipes anymore, so close them. */
24452475
CloseHandle(hChildStdoutRdDup);
@@ -2450,7 +2480,7 @@ _PyPopen(char *cmdstring, int mode, int n)
24502480
/* Case for reading from child Stdout in text mode. */
24512481
fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode);
24522482
f1 = _fdopen(fd1, "r");
2453-
f = PyFile_FromFile(f1, cmdstring, "r", fclose);
2483+
f = PyFile_FromFile(f1, cmdstring, "r", _PyPclose);
24542484
PyFile_SetBufSize(f, 0);
24552485
/* We don't care about these pipes anymore, so close them. */
24562486
CloseHandle(hChildStdinWrDup);
@@ -2461,7 +2491,7 @@ _PyPopen(char *cmdstring, int mode, int n)
24612491
/* Case for readinig from child Stdout in binary mode. */
24622492
fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode);
24632493
f1 = _fdopen(fd1, "rb");
2464-
f = PyFile_FromFile(f1, cmdstring, "rb", fclose);
2494+
f = PyFile_FromFile(f1, cmdstring, "rb", _PyPclose);
24652495
PyFile_SetBufSize(f, 0);
24662496
/* We don't care about these pipes anymore, so close them. */
24672497
CloseHandle(hChildStdinWrDup);
@@ -2472,7 +2502,7 @@ _PyPopen(char *cmdstring, int mode, int n)
24722502
/* Case for writing to child Stdin in binary mode. */
24732503
fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
24742504
f1 = _fdopen(fd1, "wb");
2475-
f = PyFile_FromFile(f1, cmdstring, "wb", fclose);
2505+
f = PyFile_FromFile(f1, cmdstring, "wb", _PyPclose);
24762506
PyFile_SetBufSize(f, 0);
24772507
/* We don't care about these pipes anymore, so close them. */
24782508
CloseHandle(hChildStdoutRdDup);
@@ -2499,7 +2529,7 @@ _PyPopen(char *cmdstring, int mode, int n)
24992529
f1 = _fdopen(fd1, m2);
25002530
fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode);
25012531
f2 = _fdopen(fd2, m1);
2502-
p1 = PyFile_FromFile(f1, cmdstring, m2, fclose);
2532+
p1 = PyFile_FromFile(f1, cmdstring, m2, _PyPclose);
25032533
PyFile_SetBufSize(p1, 0);
25042534
p2 = PyFile_FromFile(f2, cmdstring, m1, fclose);
25052535
PyFile_SetBufSize(p2, 0);
@@ -2530,7 +2560,7 @@ _PyPopen(char *cmdstring, int mode, int n)
25302560
f2 = _fdopen(fd2, m1);
25312561
fd3 = _open_osfhandle((long)hChildStderrRdDup, mode);
25322562
f3 = _fdopen(fd3, m1);
2533-
p1 = PyFile_FromFile(f1, cmdstring, m2, fclose);
2563+
p1 = PyFile_FromFile(f1, cmdstring, m2, _PyPclose);
25342564
p2 = PyFile_FromFile(f2, cmdstring, m1, fclose);
25352565
p3 = PyFile_FromFile(f3, cmdstring, m1, fclose);
25362566
PyFile_SetBufSize(p1, 0);
@@ -2543,13 +2573,15 @@ _PyPopen(char *cmdstring, int mode, int n)
25432573

25442574
if (n == POPEN_4) {
25452575
if (!_PyPopenCreateProcess(cmdstring,
2576+
f1,
25462577
hChildStdinRd,
25472578
hChildStdoutWr,
25482579
hChildStdoutWr))
25492580
return win32_error("CreateProcess", NULL);
25502581
}
25512582
else {
25522583
if (!_PyPopenCreateProcess(cmdstring,
2584+
f1,
25532585
hChildStdinRd,
25542586
hChildStdoutWr,
25552587
hChildStderrWr))
@@ -2573,6 +2605,56 @@ _PyPopen(char *cmdstring, int mode, int n)
25732605

25742606
return f;
25752607
}
2608+
2609+
/*
2610+
* Wrapper for fclose() to use for popen* files, so we can retrieve the
2611+
* exit code for the child process and return as a result of the close.
2612+
*/
2613+
static int _PyPclose(FILE *file)
2614+
{
2615+
int result = 0;
2616+
DWORD exit_code;
2617+
HANDLE hProcess;
2618+
PyObject *hProcessObj, *fileObj;
2619+
2620+
if (_PyPopenProcs) {
2621+
fileObj = PyLong_FromVoidPtr(file);
2622+
if (fileObj) {
2623+
hProcessObj = PyDict_GetItem(_PyPopenProcs, fileObj);
2624+
if (hProcessObj) {
2625+
hProcess = PyLong_AsVoidPtr(hProcessObj);
2626+
if (GetExitCodeProcess(hProcess, &exit_code)) {
2627+
/* Possible truncation here in 16-bit environments, but
2628+
* real exit codes are just the lower byte in any event.
2629+
*/
2630+
result = exit_code;
2631+
if (result == STILL_ACTIVE)
2632+
result = 0; /* Minimize confusion */
2633+
} else {
2634+
/* No good way to bubble up an error, so instead we just
2635+
* return the Windows last error shifted above standard
2636+
* exit codes. This will truncate in 16-bits but should
2637+
* be fine in 32 and at least distinguishes the problem.
2638+
*/
2639+
result = (GetLastError() << 8);
2640+
}
2641+
2642+
/* Free up the native handle at this point */
2643+
CloseHandle(hProcess);
2644+
2645+
/* Remove from dictionary and flush dictionary if empty */
2646+
PyDict_DelItem(_PyPopenProcs, fileObj);
2647+
if (PyDict_Size(_PyPopenProcs) == 0) {
2648+
Py_DECREF(_PyPopenProcs);
2649+
_PyPopenProcs = NULL;
2650+
}
2651+
} /* if hProcessObj */
2652+
} /* if fileObj */
2653+
} /* if _PyPopenProcs */
2654+
2655+
fclose(file);
2656+
return result;
2657+
}
25762658
#else
25772659
static PyObject *
25782660
posix_popen(PyObject *self, PyObject *args)

0 commit comments

Comments
 (0)