Mercurial > ~astiob > upreckon > hgweb
view _unixmodule.cpp @ 136:ed4035661b85
Added a C implementation of the unix module (called _unix)
author | Oleg Oshmyan <chortos@inbox.lv> |
---|---|
date | Tue, 24 May 2011 20:51:01 +0100 |
parents | |
children | f4361d557929 |
line wrap: on
line source
// Copyright (c) 2011 Chortos-2 <chortos@inbox.lv> #include <Python.h> #include <structmember.h> #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif #include <limits.h> #ifdef HAVE_SIGNAL_H #include <signal.h> #endif #ifdef HAVE_SPAWN_H #include <spawn.h> #ifdef __APPLE__ #pragma weak_import posix_spawnp #endif #endif #ifdef HAVE_SYS_RESOURCE_H #include <sys/resource.h> #endif #ifdef HAVE_SYS_WAIT_H #include <sys/wait.h> #endif #ifdef HAVE_TERMIOS_H #include <termios.h> #endif #if !(defined __cplusplus) && !(defined bool) #ifdef HAVE_C99_BOOL #define bool _Bool #else #define bool char #endif #undef true #define true 1 #undef false #define false 0 #endif // On Python 2.5, SIGINT handling may get delayed until we return to Python #if PY_MAJOR_VERSION > 2 || PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6 #define USE_WAKEUP_FD #endif #if !(defined RLIMIT_AS) && defined RLIMIT_VMEM #define RLIMIT_AS RLIMIT_VMEM #endif // Condition stolen from posixmodule.c of Python 2.7.1 #if defined __USLC__ && defined __SCO_VERSION__ // SCO UDK Compiler //#ifdef HAVE_FORK1 #define fork fork1 #endif // Stolen from posixmodule.c of Python 2.7.1 #ifdef WITH_NEXT_FRAMEWORK #include <crt_externs.h> static char **environ = NULL; #elif !(defined _MSC_VER) && (!(defined __WATCOMC__) || defined __QNX__) extern char **environ; #endif #ifndef Py_PYTIME_H typedef struct timeval _PyTime_timeval; #ifndef GETTIMEOFDAY_NO_TZ #define _PyTime_gettimeofday(tvp) gettimeofday((tvp), NULL) #else #define _PyTime_gettimeofday(tvp) gettimeofday((tvp)) #endif #endif #if PY_MAJOR_VERSION >= 3 #define PyInt_AsLong PyLong_AsLong #define PyInt_FromLong PyLong_FromLong #define PyNumber_Int PyNumber_Long #endif #define TESTEE_SPAWNED 0 #define TESTEE_SPAWN_FAILED 1 #define TESTEE_REPORT_STATUS(status) \ do \ { \ const char c = (status); \ write(c2ppipe[1], &c, 1); \ } \ while (0) #if !(defined SIGKILL) && defined SIGTERM #define SIGKILL SIGTERM #endif #if defined HAVE_KILL && defined SIGKILL #ifdef HAVE_WAITPID #define TERM_TESTEE \ do \ { \ kill(-curpid, SIGKILL); \ kill(-curpid, SIGCONT); \ while (waitpid(curpid, &retstat, 0) != curpid); \ } \ while (0) #else #define TERM_TESTEE \ do \ { \ kill(-curpid, SIGKILL); \ kill(-curpid, SIGCONT); \ while (wait(&retstat) != curpid); \ } \ while (0) #endif #else #define TERM_TESTEE #endif #if defined HAVE_KILL && defined SIGINT #define PROPAGATE_SIGINT ((void) kill(-curpid, SIGINT)) #else #define PROPAGATE_SIGINT #endif struct child_stats { int returncode; _PyTime_timeval walltime; #if defined HAVE_SYS_RESOURCE_H || defined HAVE_WAIT4 || defined HAVE_WAIT3 _PyTime_timeval cputime; Py_ssize_t memory; #endif }; static pid_t curpid; static const struct child_stats zero_stats = { 0 }; static PyObject *CannotStartTestee, *CanceledByUser, *WallTimeLimitExceeded, *CPUTimeLimitExceeded, *MemoryLimitExceeded; static _PyTime_timeval time_end; #ifdef USE_WAKEUP_FD static char dont_care_buffer[512]; static int intpipe[2] = { 0 }; #endif #ifdef HAVE_TERMIOS_H static bool catch_escape = false; static struct termios orig_termios; #endif typedef struct { PyObject_HEAD int returncode; } _unix__PopenPlaceholderObject; static PyMemberDef _PopenPlaceholder_members[] = { { "returncode", T_INT, offsetof(_unix__PopenPlaceholderObject, returncode), READONLY, NULL }, { NULL } }; static PyTypeObject _unix__PopenPlaceholderType = { #if PY_MAJOR_VERSION >= 3 PyVarObject_HEAD_INIT(NULL, 0) #else PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ #endif "_unix._PopenPlaceholder", /*tp_name*/ sizeof(_unix__PopenPlaceholderObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ 0, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 0, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ 0, /*tp_methods*/ _PopenPlaceholder_members, /*tp_members*/ }; #ifndef timeradd #define timeradd(a, b, res) \ do \ { \ (res)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ (res)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ if ((res)->tv_usec >= 1000000) \ { \ ++(res)->tv_sec; \ (res)->tv_usec -= 1000000; \ } \ } \ while (0) #endif #ifndef timersub #define timersub(a, b, res) \ do \ { \ (res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((res)->tv_usec < 0) \ { \ --(res)->tv_sec; \ (res)->tv_usec += 1000000; \ } \ } \ while (0) #endif #ifndef timerclear #define timerclear(tvp) ((void) ((tvp)->tv_sec = (tvp)->tv_usec = 0)) #endif #ifndef timerisset #define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) #endif #ifndef timercmp #define timercmp(a, b, cmp) \ (((a)->tv_sec == (b)->tv_sec) \ ? ((a)->tv_usec cmp (b)->tv_usec) \ : ((a)->tv_sec cmp (b)->tv_sec)) #endif // Stolen from posixmodule.c of Python 2.7.1 static void free_string_array(char **array, Py_ssize_t count) { Py_ssize_t i; for (i = 0; i < count; ++i) PyMem_Free(array[i]); PyMem_DEL(array); } // Stolen from termios.c of Python 2.7.1 static int fdconv(PyObject *obj, void *p) { int fd = PyObject_AsFileDescriptor(obj); if (fd >= 0) { *((int *) p) = fd; return 1; } return 0; } // Parts stolen from bltinmodule.c, posixmodule.c and termios.c of Python 2.7.1 static int my_spawn(PyObject *args, PyObject *kwds, int c2ppipe[2], int maxcputime, Py_ssize_t maxmemory) { static const char *const kwlist[] = { "stdin", "stdout", "stderr", NULL }; static PyObject *dummy_args = NULL; Py_ssize_t i, argc; char **argv; bool own_args = false; int fdin = 0, fdout = 1, fderr = 2; if (dummy_args == NULL) { if (!(dummy_args = PyTuple_New(0))) { return -1; } } if (!PyArg_ParseTuple(args, "O:call", &args)) { return -1; } if (!PyArg_ParseTupleAndKeywords(dummy_args, kwds, "|O&O&O&:call", (char **) kwlist, fdconv, &fdin, fdconv, &fdout, fdconv, &fderr)) { return -1; } #if PY_MAJOR_VERSION >= 3 if (PyUnicode_Check(args)) #else if (PyString_Check(args) || PyUnicode_Check(args)) #endif { argc = 1; args = PyTuple_Pack(1, args); if (args == NULL) { return -1; } own_args = true; } else if (!PySequence_Check(args)) { PyErr_SetString(PyExc_TypeError, "call() argument must be a sequence or string"); return -1; } else { argc = PySequence_Size(args); if (argc < 1) { PyErr_SetString(PyExc_TypeError, "call() argument must not be empty"); return -1; } } argv = PyMem_NEW(char *, argc + 1); if (argv == NULL) { if (own_args) { Py_DECREF(args); } PyErr_NoMemory(); return -1; } for (i = 0; i < argc; ++i) { if (!PyArg_Parse(PySequence_ITEM(args, i), "et", Py_FileSystemDefaultEncoding, &argv[i])) { free_string_array(argv, i); if (own_args) { Py_DECREF(args); } PyErr_SetString(PyExc_TypeError, "call() argument must contain only strings"); return -1; } } argv[argc] = NULL; curpid = fork(); if (!curpid) { pid_t pid; int spawn_errno, status, fd, fddupped[3]; struct child_stats stats; _PyTime_timeval tvstart, tvend; #if defined HAVE_SYS_RESOURCE_H || defined HAVE_WAIT4 || defined HAVE_WAIT3 struct rusage rusage; #endif #if defined RLIMIT_AS || defined RLIMIT_CPU struct rlimit rlimit; #endif /* Assume no errors occur: * POSIX:2008 doesn't even define any errors for setpgrp, nor does the (probably copied-verbatim-from-FreeBSD) man page on Mac OS X 10.6; * none of the error conditions POSIX:2008 does define for setpgid can occur. */ #ifdef HAVE_SETPGID setpgid(0, 0); #else //if defined HAVE_SETPGRP #ifdef SETPGRP_HAVE_ARG setpgrp(0, 0); #else setpgrp(); #endif #endif #ifdef SIGINT signal(SIGINT, SIG_DFL); #endif #if PY_MAJOR_VERSION > 3 || PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2 _Py_RestoreSignals(); #else #ifdef SIGPIPE signal(SIGPIPE, SIG_DFL); #endif #ifdef SIGXFSZ signal(SIGXFSZ, SIG_DFL); #endif #ifdef SIGXFZ signal(SIGXFZ, SIG_DFL); #endif #endif if (c2ppipe[1] < 3) { int newfd; #ifdef F_DUPFD_CLOEXEC newfd = fcntl(c2ppipe[1], F_DUPFD_CLOEXEC, 3); if (newfd == -1) { spawn_errno = errno; TESTEE_REPORT_STATUS(TESTEE_SPAWN_FAILED); write(c2ppipe[1], &spawn_errno, sizeof spawn_errno); _exit(127); } c2ppipe[1] = newfd; #else newfd = fcntl(c2ppipe[1], F_DUPFD, 3); // Other threads should not fork/spawn right now if (newfd == -1 || fcntl(newfd, F_SETFD, fcntl(newfd, F_GETFD) | FD_CLOEXEC) == -1) { spawn_errno = errno; TESTEE_REPORT_STATUS(TESTEE_SPAWN_FAILED); write(c2ppipe[1], &spawn_errno, sizeof spawn_errno); _exit(127); } c2ppipe[1] = newfd; #endif } // Yes, this works as intended even if fdin == fdout == fderr == 0 // and there are no open file descriptors except 0 and c2ppipe // FIXME: error handling fddupped[0] = dup(fdin); fddupped[1] = dup(fdout); fddupped[2] = dup(fderr); dup2(fddupped[0], 0); dup2(fddupped[1], 1); dup2(fddupped[2], 2); // FIXME: close() may fail with EINTR or EIO; is setting CLOEXEC safer? // Bear in mind we still want to close them in _this_ process for (fd = sysconf(_SC_OPEN_MAX); --fd > c2ppipe[1]; ) { close(fd); } while (--fd >= 3) { close(fd); } #ifdef RLIMIT_AS if (maxmemory) { rlimit.rlim_cur = rlimit.rlim_max = maxmemory; setrlimit(RLIMIT_AS, &rlimit); } #endif #ifdef RLIMIT_CPU if (maxcputime) { rlimit.rlim_cur = rlimit.rlim_max = maxcputime; setrlimit(RLIMIT_CPU, &rlimit); } #endif #ifdef HAVE_SPAWN_H #ifdef __APPLE__ if (posix_spawnp != NULL) { #endif spawn_errno = posix_spawnp(&pid, argv[0], NULL, NULL, argv, environ); _PyTime_gettimeofday(&tvstart); if (spawn_errno) { TESTEE_REPORT_STATUS(TESTEE_SPAWN_FAILED); write(c2ppipe[1], &spawn_errno, sizeof spawn_errno); _exit(127); } #ifdef __APPLE__ } else #endif #endif #if !(defined HAVE_SPAWN_H) || defined __APPLE__ { pid = fork(); if (!pid) { execvp(argv[0], argv); spawn_errno = errno; TESTEE_REPORT_STATUS(TESTEE_SPAWN_FAILED); write(c2ppipe[1], &spawn_errno, sizeof spawn_errno); _exit(127); } else if (pid == -1) { spawn_errno = errno; TESTEE_REPORT_STATUS(TESTEE_SPAWN_FAILED); write(c2ppipe[1], &spawn_errno, sizeof spawn_errno); _exit(127); } else { _PyTime_gettimeofday(&tvstart); } } #endif TESTEE_REPORT_STATUS(TESTEE_SPAWNED); write(c2ppipe[1], &tvstart, sizeof tvstart); #ifdef HAVE_WAIT4 while (wait4(pid, &status, 0, &rusage) != pid); #elif defined HAVE_WAIT3 while (wait3(&status, 0, &rusage) != pid); #elif defined HAVE_WAITPID while (waitpid(pid, &status, 0) != pid); #else while (wait(&status) != pid); #endif _PyTime_gettimeofday(&tvend); #if defined HAVE_SYS_RESOURCE_H && !(defined HAVE_WAIT4 || defined HAVE_WAIT3) getrusage(RUSAGE_CHILDREN, &rusage); #endif stats = zero_stats; if (WIFEXITED(status) && WEXITSTATUS(status) == 127) _exit(127); else if (WIFSIGNALED(status)) stats.returncode = -WTERMSIG(status); else stats.returncode = WEXITSTATUS(status); timersub(&tvend, &tvstart, &stats.walltime); #if defined HAVE_SYS_RESOURCE_H || defined HAVE_WAIT4 || defined HAVE_WAIT3 timeradd(&rusage.ru_utime, &rusage.ru_stime, &stats.cputime); #ifdef __APPLE__ stats.memory = rusage.ru_maxrss; #else stats.memory = rusage.ru_maxrss << 10; #endif #endif write(c2ppipe[1], &stats, sizeof stats); _exit(0); } else if (curpid == -1) { PyErr_SetFromErrno(PyExc_OSError); free_string_array(argv, argc); if (own_args) { Py_DECREF(args); } return 0; } /* Assume no errors occur if the child is still alive: * the (probably copied-verbatim-from-FreeBSD) man page on Mac OS X 10.6 doesn't even define any errors for setpgrp; * none of the error conditions POSIX:2008 defines for setpgid can occur. */ #ifdef HAVE_SETPGID setpgid(curpid, 0); #elif defined SETPGRP_HAVE_ARG setpgrp(curpid, 0); #endif free_string_array(argv, argc); if (own_args) { Py_DECREF(args); } return 1; } static inline bool attr_to_timeval(PyObject *obj, const char *attr, _PyTime_timeval *ptv) { #ifdef HAVE_LONG_LONG long long i_whole; #else long i_whole; #endif PyObject *whole, *frac, *million, *usec, *usec_whole; PyObject *member = PyObject_GetAttrString(obj, attr); if (member == NULL) { return false; } if (member == Py_None) { Py_DECREF(member); timerclear(ptv); return true; } whole = PyNumber_Int(member); if (whole == NULL) { Py_DECREF(member); return false; } #ifdef HAVE_LONG_LONG i_whole = PyLong_AsLongLong(whole); #else i_whole = PyInt_AsLong(whole); #endif if (i_whole == -1 && PyErr_Occurred() != NULL) { Py_DECREF(whole); Py_DECREF(member); return false; } // FIXME: detect time_t overflow ptv->tv_sec = i_whole; frac = PyNumber_Subtract(member, whole); Py_DECREF(whole); Py_DECREF(member); if (frac == NULL) { return false; } million = PyInt_FromLong(1000000); if (million == NULL) { Py_DECREF(frac); return false; } usec = PyNumber_InPlaceMultiply(frac, million); Py_DECREF(million); Py_DECREF(frac); if (usec == NULL) { return false; } usec_whole = PyNumber_Int(usec); Py_DECREF(usec); if (usec_whole == NULL) { return false; } // FIXME: a sanity check (0 <= value < 1000000) here wouldn't harm ptv->tv_usec = PyInt_AsLong(usec_whole); Py_DECREF(usec_whole); return ptv->tv_usec != -1 || PyErr_Occurred() == NULL; } #ifdef __cplusplus typedef struct { char a[2]; } two_chars; static char is_int(char); static char is_int(signed char); static char is_int(unsigned char); static char is_int(short); static char is_int(unsigned short); static char is_int(int); static char is_int(unsigned); static char is_int(long); static char is_int(unsigned long); #ifdef HAVE_LONG_LONG static char is_int(long long); static char is_int(unsigned long long); #endif static two_chars is_int(...); #endif static inline bool timeval_to_attr(_PyTime_timeval *ptv, PyObject *obj, const char *attr) { PyObject *value; #ifdef __cplusplus // If tv_sec has an integral type and !tv_usec, try to create a Python int if (sizeof is_int(ptv->tv_sec) == sizeof(char) && !ptv->tv_usec) { if (ptv->tv_sec <= LONG_MAX) { value = PyInt_FromLong(ptv->tv_sec); } // FIXME: signed/unsigned comparisons ruin everything #ifdef HAVE_LONG_LONG else// if (ptv->tv_sec <= ULLONG_MAX) { value = PyLong_FromUnsignedLongLong(ptv->tv_sec); } #else // else if (ptv->tv_sec <= ULONG_MAX) // { // value = PyLong_FromUnsignedLong(ptv->tv_sec); // } //#endif else { value = PyFloat_FromDouble(ptv->tv_sec); } // #endif // } else #endif { // TODO: use decimal.Decimal or fractions.Fraction value = PyFloat_FromDouble(ptv->tv_sec + ptv->tv_usec * 0.000001); } if (value == NULL) { return false; } if (PyObject_SetAttrString(obj, attr, value) == -1) { return false; } Py_DECREF(value); return true; } /* TODO/FIXME: * Replace timeval->timespec and select->pselect if pselect is available (preferably only if pselect is not a wrapper around select). * File descriptors might be >= FD_SETSIZE? */ static PyObject *_unix_call(PyObject *self, PyObject *args, PyObject *kwds) { PyObject *testcase = NULL, *obj; _unix__PopenPlaceholderObject *Popen_placeholder; int spawn_errno = 0, spawn_status, s, c2ppipe[2], retstat; struct child_stats stats = zero_stats; _PyTime_timeval maxwalltime, maxcputime, timeout, time_start; Py_ssize_t maxmemory, r; size_t stats_read = 0; fd_set readfds; char c; bool have_maxwalltime; if (kwds != NULL) { testcase = PyDict_GetItemString(kwds, "case"); } if (testcase == NULL) { PyErr_SetString(PyExc_TypeError, "call() requires a keyword argument 'case'"); return NULL; } Py_INCREF(testcase); PyDict_DelItemString(kwds, "case"); if (!attr_to_timeval(testcase, "maxwalltime", &maxwalltime) || !attr_to_timeval(testcase, "maxcputime", &maxcputime)) { Py_DECREF(testcase); return NULL; } obj = PyObject_GetAttrString(testcase, "maxmemory"); if (obj == NULL) { Py_DECREF(testcase); return NULL; } if (PyObject_IsTrue(obj)) { PyObject *factor, *bytes; factor = PyInt_FromLong(1024 * 1024); if (factor == NULL) { Py_DECREF(testcase); return NULL; } bytes = PyNumber_Multiply(obj, factor); Py_DECREF(factor); if (bytes == NULL) { Py_DECREF(testcase); return NULL; } maxmemory = PyNumber_AsSsize_t(bytes, PyExc_OverflowError); Py_DECREF(bytes); if (maxmemory == -1 && PyErr_Occurred() != NULL) { Py_DECREF(testcase); return NULL; } } else { maxmemory = 0; } Py_DECREF(obj); #ifdef HAVE_PIPE2 if (pipe2(c2ppipe, O_CLOEXEC)) { PyErr_SetFromErrno(PyExc_IOError); Py_DECREF(testcase); return NULL; } #else if (pipe(c2ppipe)) { PyErr_SetFromErrno(PyExc_IOError); Py_DECREF(testcase); return NULL; } // Does any other thread fork/spawn right now? Please shoot it in the head // (well, if this ends up causing trouble, anyway) if (fcntl(c2ppipe[0], F_SETFD, fcntl(c2ppipe[0], F_GETFD) | FD_CLOEXEC) == -1 || fcntl(c2ppipe[1], F_SETFD, fcntl(c2ppipe[1], F_GETFD) | FD_CLOEXEC) == -1) { PyErr_SetFromErrno(PyExc_IOError); close(c2ppipe[0]); close(c2ppipe[1]); Py_DECREF(testcase); return NULL; } #endif spawn_status = my_spawn(args, kwds, c2ppipe, maxcputime.tv_sec + (maxcputime.tv_usec > 0), maxmemory); close(c2ppipe[1]); if (!spawn_status) { PyObject *type, *value, *traceback, *e; close(c2ppipe[0]); Py_DECREF(testcase); PyErr_Fetch(&type, &value, &traceback); PyErr_NormalizeException(&type, &value, &traceback); Py_XDECREF(traceback); Py_DECREF(type); e = PyObject_CallFunctionObjArgs(CannotStartTestee, value, NULL); Py_DECREF(value); PyErr_SetObject(CannotStartTestee, e); Py_DECREF(e); return NULL; } else if (spawn_status < 0) { close(c2ppipe[0]); Py_DECREF(testcase); return NULL; } // FIXME: use select in order not to miss SIGINT while ((r = read(c2ppipe[0], &c, 1)) == -1 && errno == EINTR) { if (PyErr_CheckSignals() == -1) { PROPAGATE_SIGINT; close(c2ppipe[0]); Py_DECREF(testcase); TERM_TESTEE; return NULL; } } if (r == 1) { if (c == TESTEE_SPAWNED) { size_t got = 0; while (got < sizeof time_start) { r = read(c2ppipe[0], got + (char *) &time_start, sizeof time_start - got); if (r > 0) { got += r; } else if (!r) { errno = 0; PyErr_SetFromErrno(PyExc_IOError); goto spawn_failed; } else if (errno == EINTR) { if (PyErr_CheckSignals() == -1) { PROPAGATE_SIGINT; close(c2ppipe[0]); Py_DECREF(testcase); TERM_TESTEE; return NULL; } } else { PyErr_SetFromErrno(PyExc_IOError); goto spawn_failed; } } if (!timeval_to_attr(&time_start, testcase, "time_started")) { close(c2ppipe[0]); Py_DECREF(testcase); TERM_TESTEE; return NULL; } } else // if (c == TESTEE_SPAWN_FAILED) { size_t got = 0; while (got < sizeof spawn_errno) { r = read(c2ppipe[0], got + (char *) &spawn_errno, sizeof spawn_errno - got); if (r > 0) { got += r; } else if (!r) { // Can't get the real error; use zero instead spawn_errno = 0; break; } else if (errno == EINTR) { if (PyErr_CheckSignals() == -1) { PROPAGATE_SIGINT; close(c2ppipe[0]); Py_DECREF(testcase); TERM_TESTEE; return NULL; } } else { PyErr_SetFromErrno(PyExc_IOError); goto spawn_failed; } } errno = spawn_errno; /* if (errno == EACCES || errno == EINVAL || errno == ELOOP || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == ENOEXEC || errno == ETXTBSY) { PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, PySequence_ITEM(args, 0)); } else {*/ PyErr_SetFromErrno(PyExc_OSError); //} goto spawn_failed; } } else { PyObject *type, *value, *traceback, *e; if (!r) errno = 0; PyErr_SetFromErrno(PyExc_IOError); spawn_failed: Py_DECREF(testcase); close(c2ppipe[0]); TERM_TESTEE; PyErr_Fetch(&type, &value, &traceback); PyErr_NormalizeException(&type, &value, &traceback); Py_XDECREF(traceback); Py_DECREF(type); e = PyObject_CallFunctionObjArgs(CannotStartTestee, value, NULL); Py_DECREF(value); PyErr_SetObject(CannotStartTestee, e); Py_DECREF(e); return NULL; } Py_BEGIN_ALLOW_THREADS timeradd(&time_start, &maxwalltime, &time_end); FD_ZERO(&readfds); have_maxwalltime = timerisset(&maxwalltime); /* Implementations may place limitations on the maximum timeout interval supported. All implementations shall support a maximum timeout interval of at least 31 days. If the timeout argument specifies a timeout interval greater than the implementation- defined maximum value, the maximum value shall be used as the actual timeout value. (POSIX:2008) Therefore the loop and the && timercmp(&time_end, &now, <). */ for (;;) { _PyTime_timeval now; int maxfd = c2ppipe[0]; #ifdef HAVE_TERMIOS_H if (catch_escape) FD_SET(0, &readfds); #endif #ifdef USE_WAKEUP_FD FD_SET(intpipe[0], &readfds); if (intpipe[0] > maxfd) maxfd = intpipe[0]; #endif FD_SET(c2ppipe[0], &readfds); if (have_maxwalltime) { _PyTime_gettimeofday(&now); if (timercmp(&time_end, &now, <)) { timerclear(&timeout); } else { timersub(&time_end, &now, &timeout); } s = select(maxfd + 1, &readfds, NULL, NULL, &timeout); if (!s && timercmp(&time_end, &now, <)) { close(c2ppipe[0]); TERM_TESTEE; Py_BLOCK_THREADS Py_DECREF(testcase); PyErr_SetObject(WallTimeLimitExceeded, NULL); return NULL; } } else { s = select(maxfd + 1, &readfds, NULL, NULL, NULL); } if (s < 0 && errno == EINTR) { Py_BLOCK_THREADS if (PyErr_CheckSignals() == -1) { PROPAGATE_SIGINT; close(c2ppipe[0]); Py_DECREF(testcase); TERM_TESTEE; return NULL; } Py_UNBLOCK_THREADS } else if (s < 0 && errno != EAGAIN) { Py_BLOCK_THREADS PyErr_SetFromErrno(PyExc_IOError); close(c2ppipe[0]); Py_DECREF(testcase); TERM_TESTEE; return NULL; } #ifdef USE_WAKEUP_FD else if (s > 0 && FD_ISSET(intpipe[0], &readfds)) { // FIXME: is error handling needed? while (read(intpipe[0], dont_care_buffer, sizeof dont_care_buffer) > 0); Py_BLOCK_THREADS if (PyErr_CheckSignals() == -1) { PROPAGATE_SIGINT; close(c2ppipe[0]); Py_DECREF(testcase); TERM_TESTEE; return NULL; } Py_UNBLOCK_THREADS } #endif #ifdef HAVE_TERMIOS_H else if (s > 0 && !FD_ISSET(c2ppipe[0], &readfds)) { // FIXME: is error and EOF handling needed? if ((r = read(0, &c, 1)) == 1) { if (c == '\33') { close(c2ppipe[0]); TERM_TESTEE; Py_BLOCK_THREADS Py_DECREF(testcase); PyErr_SetObject(CanceledByUser, NULL); return NULL; } } else if (r == -1 && errno == EINTR) { if (PyErr_CheckSignals() == -1) { PROPAGATE_SIGINT; close(c2ppipe[0]); Py_DECREF(testcase); TERM_TESTEE; return NULL; } } } #endif else if (s > 0) { bool blocked_threads = false; while ((r = read(c2ppipe[0], stats_read + (char *) &stats, sizeof stats - stats_read)) == -1 && errno == EINTR) { Py_BLOCK_THREADS blocked_threads = true; if (PyErr_CheckSignals() == -1) { PROPAGATE_SIGINT; close(c2ppipe[0]); Py_DECREF(testcase); TERM_TESTEE; return NULL; } } if (r > 0) { stats_read += r; } else if (!r) { break; } else { close(c2ppipe[0]); TERM_TESTEE; if (!blocked_threads) { Py_BLOCK_THREADS } Py_DECREF(testcase); PyErr_SetFromErrno(PyExc_IOError); return NULL; } if (blocked_threads) { Py_UNBLOCK_THREADS } } } close(c2ppipe[0]); Py_END_ALLOW_THREADS #ifdef HAVE_WAITPID while (waitpid(curpid, &retstat, 0) != curpid) #else while (wait(&retstat) != curpid) #endif { if (PyErr_CheckSignals() == -1) { Py_DECREF(testcase); return NULL; } } if (WIFEXITED(retstat) && WEXITSTATUS(retstat) == 127) { PyObject *type, *value, *traceback, *e; Py_DECREF(testcase); errno = 0; PyErr_SetFromErrno(PyExc_OSError); PyErr_Fetch(&type, &value, &traceback); PyErr_NormalizeException(&type, &value, &traceback); Py_XDECREF(traceback); Py_DECREF(type); e = PyObject_CallFunctionObjArgs(CannotStartTestee, value, NULL); Py_DECREF(value); PyErr_SetObject(CannotStartTestee, e); Py_DECREF(e); return NULL; } else if (!WIFEXITED(retstat) || WEXITSTATUS(retstat)) { Py_DECREF(testcase); if (WIFSTOPPED(retstat)) { return PyErr_Format(PyExc_EnvironmentError, "unexpected exit status from worker: stopped by signal %d", WSTOPSIG(retstat)); } else if (WIFSIGNALED(retstat)) { return PyErr_Format(PyExc_EnvironmentError, "unexpected exit status from worker: terminated by signal %d", WTERMSIG(retstat)); } else if (WIFEXITED(retstat)) { return PyErr_Format(PyExc_EnvironmentError, "unexpected exit status from worker: %d", WEXITSTATUS(retstat)); } else { PyErr_SetString(PyExc_EnvironmentError, "unexpected exit status from worker: not exited, signaled or stopped"); return NULL; } } if (stats_read != sizeof stats) { Py_DECREF(testcase); PyErr_SetString(PyExc_EnvironmentError, "unexpectedly early end of output from worker"); return NULL; } if (timerisset(&maxwalltime) && timercmp(&stats.walltime, &maxwalltime, >)) { Py_DECREF(testcase); PyErr_SetObject(WallTimeLimitExceeded, NULL); return NULL; } obj = PyInt_FromLong(0); if (obj == NULL) { Py_DECREF(testcase); return NULL; } if (PyObject_SetAttrString(testcase, "time_started", obj) == -1) { Py_DECREF(testcase); return NULL; } Py_DECREF(obj); #if defined HAVE_SYS_RESOURCE_H || defined HAVE_WAIT4 || defined HAVE_WAIT3 if (timerisset(&maxcputime) || !timerisset(&maxwalltime)) { PyObject *cputls; if (!timeval_to_attr(&stats.cputime, testcase, "time_stopped")) { Py_DECREF(testcase); return NULL; } cputls = PyObject_GetAttrString(testcase, "cpu_time_limit_string"); if (cputls == NULL) { Py_DECREF(testcase); return NULL; } if (PyObject_SetAttrString(testcase, "time_limit_string", cputls) == -1) { Py_DECREF(testcase); return NULL; } Py_DECREF(cputls); if (timerisset(&maxcputime) && timercmp(&stats.cputime, &maxcputime, >)) { Py_DECREF(testcase); PyErr_SetObject(CPUTimeLimitExceeded, NULL); return NULL; } } else #endif { if (!timeval_to_attr(&stats.walltime, testcase, "time_stopped")) { Py_DECREF(testcase); return NULL; } } #if defined HAVE_SYS_RESOURCE_H || defined HAVE_WAIT4 || defined HAVE_WAIT3 if (maxmemory && stats.memory > maxmemory) { Py_DECREF(testcase); PyErr_SetObject(MemoryLimitExceeded, NULL); return NULL; } #endif Popen_placeholder = PyObject_New(_unix__PopenPlaceholderObject, &_unix__PopenPlaceholderType); if (Popen_placeholder == NULL) { return NULL; } Popen_placeholder->returncode = stats.returncode; PyObject_SetAttrString(testcase, "process", (PyObject *) Popen_placeholder); Py_DECREF(Popen_placeholder); Py_DECREF(testcase); Py_RETURN_NONE; } static PyObject *_unix_pause(PyObject *self) { #ifdef HAVE_TERMIOS_H if (catch_escape) { char c; while (read(0, &c, 1) == -1 && errno == EINTR); } #endif Py_RETURN_NONE; } static PyMethodDef _unixMethods[] = { { "call", (PyCFunction) _unix_call, METH_VARARGS | METH_KEYWORDS, "Call a process." }, { "pause", (PyCFunction) _unix_pause, METH_NOARGS, "Block until a key is pressed." }, { NULL } }; #ifdef USE_WAKEUP_FD static void close_intpipe(void) { close(intpipe[0]); close(intpipe[1]); intpipe[0] = intpipe[1] = 0; } #endif #ifdef HAVE_TERMIOS_H static void restore_termios(void) { tcsetattr(0, TCSAFLUSH, &orig_termios); #ifdef USE_WAKEUP_FD close_intpipe(); #endif } #endif #if PY_MAJOR_VERSION >= 3 #define INIT_FAIL return NULL static PyModuleDef _unixmodule = { PyModuleDef_HEAD_INIT, "_unix", NULL, -1, _unixMethods }; PyMODINIT_FUNC PyInit__unix(void) #else #define INIT_FAIL return PyMODINIT_FUNC init_unix(void) #endif { struct termios new_termios; PyObject *testcases; _unix__PopenPlaceholderType.tp_new = PyType_GenericNew; if (PyType_Ready(&_unix__PopenPlaceholderType) == -1) { INIT_FAIL; } testcases = PyImport_ImportModule("testcases"); if (testcases == NULL) { INIT_FAIL; } if ((CannotStartTestee = PyObject_GetAttrString(testcases, "CannotStartTestee")) == NULL || (CanceledByUser = PyObject_GetAttrString(testcases, "CanceledByUser")) == NULL || (WallTimeLimitExceeded = PyObject_GetAttrString(testcases, "WallTimeLimitExceeded")) == NULL || (CPUTimeLimitExceeded = PyObject_GetAttrString(testcases, "CPUTimeLimitExceeded")) == NULL || (MemoryLimitExceeded = PyObject_GetAttrString(testcases, "MemoryLimitExceeded")) == NULL) { Py_XDECREF(MemoryLimitExceeded); Py_XDECREF(CPUTimeLimitExceeded); Py_XDECREF(WallTimeLimitExceeded); Py_XDECREF(CanceledByUser); Py_XDECREF(CannotStartTestee); Py_DECREF(testcases); INIT_FAIL; } Py_DECREF(testcases); #ifdef WITH_NEXT_FRAMEWORK if (environ == NULL) { environ = *_NSGetEnviron(); } #endif #ifdef USE_WAKEUP_FD if (!intpipe[0] || !intpipe[1]) { #ifdef HAVE_PIPE2 if (pipe2(intpipe, O_CLOEXEC | O_NONBLOCK)) { PyErr_SetFromErrno(PyExc_IOError); Py_DECREF(MemoryLimitExceeded); Py_DECREF(CPUTimeLimitExceeded); Py_DECREF(WallTimeLimitExceeded); Py_DECREF(CanceledByUser); Py_DECREF(CannotStartTestee); INIT_FAIL; } #else if (pipe(intpipe)) { PyErr_SetFromErrno(PyExc_IOError); Py_DECREF(MemoryLimitExceeded); Py_DECREF(CPUTimeLimitExceeded); Py_DECREF(WallTimeLimitExceeded); Py_DECREF(CanceledByUser); Py_DECREF(CannotStartTestee); INIT_FAIL; } // Other threads must not fork now if (fcntl(intpipe[0], F_SETFD, fcntl(intpipe[0], F_GETFD) | FD_CLOEXEC) == -1 || fcntl(intpipe[1], F_SETFD, fcntl(intpipe[1], F_GETFD) | FD_CLOEXEC) == -1 || fcntl(intpipe[0], F_SETFL, fcntl(intpipe[0], F_GETFL) | O_NONBLOCK) == -1 || fcntl(intpipe[1], F_SETFL, fcntl(intpipe[1], F_GETFL) | O_NONBLOCK) == -1) { PyErr_SetFromErrno(PyExc_IOError); close(intpipe[0]); close(intpipe[1]); Py_DECREF(MemoryLimitExceeded); Py_DECREF(CPUTimeLimitExceeded); Py_DECREF(WallTimeLimitExceeded); Py_DECREF(CanceledByUser); Py_DECREF(CannotStartTestee); INIT_FAIL; } #endif } PySignal_SetWakeupFd(intpipe[1]); #endif #ifdef HAVE_TERMIOS_H if (!tcgetattr(0, &orig_termios)) { new_termios = orig_termios; // Stolen from tty.py of Python 2.7.1 new_termios.c_lflag &= ~(ECHO | ICANON); new_termios.c_cc[VMIN] = 1; new_termios.c_cc[VTIME] = 0; if (!Py_AtExit(restore_termios) && !tcsetattr(0, TCSAFLUSH, &new_termios)) { catch_escape = true; } } #ifdef USE_WAKEUP_FD else { Py_AtExit(close_intpipe); } #endif #elif defined USE_WAKEUP_FD Py_AtExit(close_intpipe); #endif #if PY_MAJOR_VERSION >= 3 PyObject *module = PyModule_Create(&_unixmodule); if (module == NULL) { Py_DECREF(MemoryLimitExceeded); Py_DECREF(CPUTimeLimitExceeded); Py_DECREF(WallTimeLimitExceeded); Py_DECREF(CanceledByUser); Py_DECREF(CannotStartTestee); } return module; #else Py_InitModule("_unix", _unixMethods); #endif }