From 56055a474955811fd672e406d0a175349879759d Mon Sep 17 00:00:00 2001 From: Fredrik Lundh Date: Sun, 23 Jul 2000 19:47:12 +0000 Subject: -- 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) --- Modules/posixmodule.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 8 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index fef4bbb..c342dcf 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -2117,6 +2117,7 @@ posix_popen(PyObject *self, PyObject *args) * * Written by Bill Tutt . Minor tweaks * and 2.0 integration by Fredrik Lundh + * Return code handling by David Bolen. */ #include @@ -2130,6 +2131,15 @@ posix_popen(PyObject *self, PyObject *args) #define POPEN_4 4 static PyObject *_PyPopen(char *, int, int); +static int _PyPclose(FILE *file); + +/* + * Internal dictionary mapping popen* file pointers to process handles, + * in order to maintain a link to the process handle until the file is + * closed, at which point the process exit code is returned to the caller. + */ +static PyObject *_PyPopenProcs = NULL; + /* popen that works from a GUI. * @@ -2285,7 +2295,7 @@ win32_popen4(PyObject *self, PyObject *args) } static int -_PyPopenCreateProcess(char *cmdstring, +_PyPopenCreateProcess(char *cmdstring, FILE *file, HANDLE hStdin, HANDLE hStdout, HANDLE hStderr) @@ -2361,8 +2371,28 @@ _PyPopenCreateProcess(char *cmdstring, &siStartInfo, &piProcInfo) ) { /* Close the handles now so anyone waiting is woken. */ - CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); + + /* + * Try to insert our process handle into the internal + * dictionary so we can find it later when trying + * to close this file. + */ + if (!_PyPopenProcs) + _PyPopenProcs = PyDict_New(); + if (_PyPopenProcs) { + PyObject *hProcessObj, *fileObj; + + hProcessObj = PyLong_FromVoidPtr(piProcInfo.hProcess); + fileObj = PyLong_FromVoidPtr(file); + + if (!hProcessObj || !fileObj || + PyDict_SetItem(_PyPopenProcs, + fileObj, hProcessObj) < 0) { + /* Insert failure - close handle to prevent leak */ + CloseHandle(piProcInfo.hProcess); + } + } return TRUE; } return FALSE; @@ -2439,7 +2469,7 @@ _PyPopen(char *cmdstring, int mode, int n) /* Case for writing to child Stdin in text mode. */ fd1 = _open_osfhandle((long)hChildStdinWrDup, mode); f1 = _fdopen(fd1, "w"); - f = PyFile_FromFile(f1, cmdstring, "w", fclose); + f = PyFile_FromFile(f1, cmdstring, "w", _PyPclose); PyFile_SetBufSize(f, 0); /* We don't care about these pipes anymore, so close them. */ CloseHandle(hChildStdoutRdDup); @@ -2450,7 +2480,7 @@ _PyPopen(char *cmdstring, int mode, int n) /* Case for reading from child Stdout in text mode. */ fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode); f1 = _fdopen(fd1, "r"); - f = PyFile_FromFile(f1, cmdstring, "r", fclose); + f = PyFile_FromFile(f1, cmdstring, "r", _PyPclose); PyFile_SetBufSize(f, 0); /* We don't care about these pipes anymore, so close them. */ CloseHandle(hChildStdinWrDup); @@ -2461,7 +2491,7 @@ _PyPopen(char *cmdstring, int mode, int n) /* Case for readinig from child Stdout in binary mode. */ fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode); f1 = _fdopen(fd1, "rb"); - f = PyFile_FromFile(f1, cmdstring, "rb", fclose); + f = PyFile_FromFile(f1, cmdstring, "rb", _PyPclose); PyFile_SetBufSize(f, 0); /* We don't care about these pipes anymore, so close them. */ CloseHandle(hChildStdinWrDup); @@ -2472,7 +2502,7 @@ _PyPopen(char *cmdstring, int mode, int n) /* Case for writing to child Stdin in binary mode. */ fd1 = _open_osfhandle((long)hChildStdinWrDup, mode); f1 = _fdopen(fd1, "wb"); - f = PyFile_FromFile(f1, cmdstring, "wb", fclose); + f = PyFile_FromFile(f1, cmdstring, "wb", _PyPclose); PyFile_SetBufSize(f, 0); /* We don't care about these pipes anymore, so close them. */ CloseHandle(hChildStdoutRdDup); @@ -2499,7 +2529,7 @@ _PyPopen(char *cmdstring, int mode, int n) f1 = _fdopen(fd1, m2); fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode); f2 = _fdopen(fd2, m1); - p1 = PyFile_FromFile(f1, cmdstring, m2, fclose); + p1 = PyFile_FromFile(f1, cmdstring, m2, _PyPclose); PyFile_SetBufSize(p1, 0); p2 = PyFile_FromFile(f2, cmdstring, m1, fclose); PyFile_SetBufSize(p2, 0); @@ -2530,7 +2560,7 @@ _PyPopen(char *cmdstring, int mode, int n) f2 = _fdopen(fd2, m1); fd3 = _open_osfhandle((long)hChildStderrRdDup, mode); f3 = _fdopen(fd3, m1); - p1 = PyFile_FromFile(f1, cmdstring, m2, fclose); + p1 = PyFile_FromFile(f1, cmdstring, m2, _PyPclose); p2 = PyFile_FromFile(f2, cmdstring, m1, fclose); p3 = PyFile_FromFile(f3, cmdstring, m1, fclose); PyFile_SetBufSize(p1, 0); @@ -2543,6 +2573,7 @@ _PyPopen(char *cmdstring, int mode, int n) if (n == POPEN_4) { if (!_PyPopenCreateProcess(cmdstring, + f1, hChildStdinRd, hChildStdoutWr, hChildStdoutWr)) @@ -2550,6 +2581,7 @@ _PyPopen(char *cmdstring, int mode, int n) } else { if (!_PyPopenCreateProcess(cmdstring, + f1, hChildStdinRd, hChildStdoutWr, hChildStderrWr)) @@ -2573,6 +2605,56 @@ _PyPopen(char *cmdstring, int mode, int n) return f; } + +/* + * Wrapper for fclose() to use for popen* files, so we can retrieve the + * exit code for the child process and return as a result of the close. + */ +static int _PyPclose(FILE *file) +{ + int result = 0; + DWORD exit_code; + HANDLE hProcess; + PyObject *hProcessObj, *fileObj; + + if (_PyPopenProcs) { + fileObj = PyLong_FromVoidPtr(file); + if (fileObj) { + hProcessObj = PyDict_GetItem(_PyPopenProcs, fileObj); + if (hProcessObj) { + hProcess = PyLong_AsVoidPtr(hProcessObj); + if (GetExitCodeProcess(hProcess, &exit_code)) { + /* Possible truncation here in 16-bit environments, but + * real exit codes are just the lower byte in any event. + */ + result = exit_code; + if (result == STILL_ACTIVE) + result = 0; /* Minimize confusion */ + } else { + /* No good way to bubble up an error, so instead we just + * return the Windows last error shifted above standard + * exit codes. This will truncate in 16-bits but should + * be fine in 32 and at least distinguishes the problem. + */ + result = (GetLastError() << 8); + } + + /* Free up the native handle at this point */ + CloseHandle(hProcess); + + /* Remove from dictionary and flush dictionary if empty */ + PyDict_DelItem(_PyPopenProcs, fileObj); + if (PyDict_Size(_PyPopenProcs) == 0) { + Py_DECREF(_PyPopenProcs); + _PyPopenProcs = NULL; + } + } /* if hProcessObj */ + } /* if fileObj */ + } /* if _PyPopenProcs */ + + fclose(file); + return result; +} #else static PyObject * posix_popen(PyObject *self, PyObject *args) -- cgit v0.12