def run(self):
        """
        self.exit_status = os.waitpid(self.pid, os.WNOHANG | os.WUNTRACED)
        while self.exit_status == (0, 0):
            self.exit_status = os.waitpid(self.pid, os.WNOHANG | os.WUNTRACED)
        """
        self.spawn_target()

        self.finished_starting.set()
        if self.proc_name:
            gone, _ = psutil.wait_procs([self._psutil_proc])
            self.exit_status = gone[0].returncode
        else:
            exit_info = os.waitpid(self.pid, 0)
            self.exit_status = exit_info[1]  # [0] is the pid

        default_reason = 'Process died for unknown reason'
        if self.exit_status is not None:
            if os.WCOREDUMP(self.exit_status):
                reason = 'Segmentation fault'
            elif os.WIFSTOPPED(self.exit_status):
                reason = 'Stopped with signal ' + str(
                    os.WTERMSIG(self.exit_status))
            elif os.WIFSIGNALED(self.exit_status):
                reason = 'Terminated with signal ' + str(
                    os.WTERMSIG(self.exit_status))
            elif os.WIFEXITED(self.exit_status):
                reason = 'Exit with code - ' + str(
                    os.WEXITSTATUS(self.exit_status))
            else:
                reason = default_reason
        else:
            reason = default_reason

        self.process_monitor.last_synopsis = '[{0}] Crash. Exit code: {1}. Reason - {2}\n'.format(
            time.strftime("%I:%M.%S"),
            self.exit_status if self.exit_status is not None else '<unknown>',
            reason)
Example #2
0
 def _wait_for_child(self, childpid):
     self.__child_exited.acquire()
     if self.__child_exited.wait(60) == False:  # Py2, <Py3.2: always None
         raise getmailOperationError('waiting child pid %d timed out' %
                                     childpid)
     self.__child_exited.release()
     if self.__child_pid != childpid:
         #self.log.error('got child pid %d, not %d' % (pid, childpid))
         raise getmailOperationError('got child pid %d, not %d' %
                                     (self.__child_pid, childpid))
     if os.WIFSTOPPED(self.__child_status):
         raise getmailOperationError(
             'child pid %d stopped by signal %d' %
             (self.__child_pid, os.WSTOPSIG(self.__child_status)))
     if os.WIFSIGNALED(self.__child_status):
         raise getmailOperationError(
             'child pid %d killed by signal %d' %
             (self.__child_pid, os.WTERMSIG(self.__child_status)))
     if not os.WIFEXITED(self.__child_status):
         raise getmailOperationError('child pid %d failed to exit' %
                                     self.__child_pid)
     exitcode = os.WEXITSTATUS(self.__child_status)
     return exitcode
Example #3
0
def wait_pid(pid=0, mode=os.WNOHANG):
    while True:
        try:
            exit_pid, exit_status = os.waitpid(pid, mode)
        except OSError as exc:
            if exc.errno == errno.EINTR:
                continue
            if exc.errno != errno.ECHILD:
                raise
            if IS_PY2:
                sys.exc_clear()
            break
        else:
            if not exit_pid:
                break

            elif os.WIFEXITED(exit_status):
                return ProcessExit(exit_pid, os.WEXITSTATUS(exit_status))
            elif os.WIFSIGNALED(exit_status):
                return ProcessExit(exit_pid, -os.WTERMSIG(exit_status))
            elif os.WIFSTOPPED(exit_status):
                return ProcessExit(exit_pid, os.WSTOPSIG(exit_status))
    return None
    def verify(self, filename, sigfilename=None):

        args = []
        for keyring in self._keyrings:
            args.append('--keyring')
            args.append(keyring)
        if sigfilename:
            args.append(sigfilename)
        args = [self._gpgv] + args + [filename]
        msg = ""
        (status, output) = commands.getstatusoutput(string.join(args))
        if not (status is None or
                (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0)):
            if os.WIFEXITED(status):
                msg = "gpgv exited with error code %d" % (
                    os.WEXITSTATUS(status), )
            elif os.WIFSTOPPED(status):
                msg = "gpgv stopped unexpectedly with signal %d" % (
                    os.WSTOPSIG(status), )
            elif os.WIFSIGNALED(status):
                msg = "gpgv died with signal %d" % (os.WTERMSIG(status), )
            raise GPGSigVerificationFailure(msg, output)
        return output.splitlines()
Example #5
0
    def post_send(self):
        """
        This routine is called after the fuzzer transmits a test case and returns the status of the target.

        @rtype:  bool
        @return: Return True if the target is still active, False otherwise.
        """

        if not self.dbg.is_alive():
            exit_status = self.dbg.get_exit_status()
            rec_file = open(self.crash_bin, 'a')
            if os.WCOREDUMP(exit_status):
                reason = 'Segmentation fault'
            elif os.WIFSTOPPED(exit_status):
                reason = 'Stopped with signal ' + str(os.WTERMSIG(exit_status))
            elif os.WIFSIGNALED(exit_status):
                reason = 'Terminated with signal ' + str(
                    os.WTERMSIG(exit_status))
            elif os.WIFEXITED(exit_status):
                reason = 'Exit with code - ' + str(os.WEXITSTATUS(exit_status))
            else:
                reason = 'Process died for unknown reason'

            self.last_synopsis = '[%s] Crash : Test - %d Reason - %s\n' % (
                time.strftime("%I:%M.%S"), self.test_number, reason)
            rec_file.write(self.last_synopsis)
            rec_file.close()

            if self.coredump_dir is not None:
                dest = os.path.join(self.coredump_dir, str(self.test_number))
                src = self._get_coredump_path()

                if src is not None:
                    self.log("moving core dump %s -> %s" % (src, dest))
                    os.rename(src, dest)

        return self.dbg.is_alive()
 def _get_file_md5sum(self, filename):
     if os.access('/usr/bin/md5sum', os.X_OK):
         cmd = '/usr/bin/md5sum %s' % (filename, )
         self._logger.debug("Running: %s" % (cmd, ))
         child = popen2.Popen3(cmd, 1)
         child.tochild.close()
         erroutput = child.childerr.read()
         child.childerr.close()
         if erroutput != '':
             child.fromchild.close()
             raise ChangeFileException(
                 "md5sum returned error output \"%s\"" % (erroutput, ))
         (md5sum, filename) = string.split(child.fromchild.read(), None, 1)
         child.fromchild.close()
         status = child.wait()
         if not (status is None or
                 (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0)):
             if os.WIFEXITED(status):
                 msg = "md5sum exited with error code %d" % (
                     os.WEXITSTATUS(status), )
             elif os.WIFSTOPPED(status):
                 msg = "md5sum stopped unexpectedly with signal %d" % (
                     os.WSTOPSIG(status), )
             elif os.WIFSIGNALED(status):
                 msg = "md5sum died with signal %d" % (
                     os.WTERMSIG(status), )
             raise ChangeFileException(msg)
         return md5sum.strip()
     import md5
     fhdl = open(filename)
     md5sum = md5.new()
     buf = fhdl.read(8192)
     while buf != '':
         md5sum.update(buf)
         buf = fhdl.read(8192)
     fhdl.close()
     return md5sum.hexdigest()
Example #7
0
    def __handle_event(self, pid, status):
        if os.WCOREDUMP(status):
            logger.warning("Core dump created for %s" % (self.pid))

        if os.WIFEXITED(status):
            self.service.instances.pop(self.pid, None)
            self.log_event(0)
            self.__spawn()

        elif os.WIFSIGNALED(status):
            self.service.instances.pop(self.pid, None)
            sig = os.WTERMSIG(status)
            self.log_event(sig)
            self.__spawn()

        elif os.WIFSTOPPED(status):
            self.coverage.capture(self.pid)
            sig = os.WSTOPSIG(status)

            #Don't log breakpoints
            if sig == signal.SIGTRAP:
                self.exitbp.desinstall(set_ip = True)
                ptrace_cont(self.pid, 0)
                return

            self.log_event(sig)
            if sig == signal.SIGPIPE:
                ptrace_detach(self.pid)
                os.waitpid(self.pid, 0) #Clean up Zombie
                self.running = False

            elif sig == signal.SIGSEGV:
                self.__capturecore()
                self.__spawn()
            else:
                ptrace_cont(self.pid, sig)
Example #8
0
def child_handler():
    while True:  #make sure to clean up all children
        try:
            child = os.waitpid(-1, os.WNOHANG | os.WCONTINUED | os.WUNTRACED)
            # child will be a tuple of (pid, exit_status)
            if child == (0, 0):
                break
            pid = child[0]
            exit_status = child[1]

            # get the job from the global joblist that contains
            # subprocess with pid pid
            j = joblist.get_job_with_process(pid)
            # get the subprocess from the job that matches pid pid

            p = j.get_subprocess(pid)

            # updates the status of the child process
            if (os.WIFEXITED(exit_status)):
                p.set_status(STATUS.TERMINATED)
            elif (os.WIFSTOPPED(exit_status)):
                p.set_status(STATUS.STPPED)
            elif (os.WIFCONTINUED(exit_status)):
                p.set_status(STATUS.RUNNING)

            # if the child was terminated by signal, print the relevant information
            elif (os.WIFSIGNALED(exit_status)):
                print("Job pid: " + str(pid) + " terminated by signal: " +
                      str(os.WTERMSIG(exit_status)))
                p.set_status(STATUS.TERMINATED)

            # updates job list by looking at subprocesses
            joblist.synchronize(j)

        except OSError:
            break
Example #9
0
    def platformProcessEvent(self, status):

        if os.WIFEXITED(status):
            tid = self.getMeta("ThreadId", -1)
            if tid != self.getPid():
                # Set the selected thread ID to the pid cause
                # the old one's invalid
                if tid in self.pthreads:
                    self.pthreads.remove(tid)
                self.setMeta("ThreadId", self.getPid())
                self._fireExitThread(tid, os.WEXITSTATUS(status))
            else:
                self._fireExit(os.WEXITSTATUS(status))

        elif os.WIFSIGNALED(status):
            self.setMeta("ExitCode", os.WTERMSIG(status))
            self.fireNotifiers(vtrace.NOTIFY_EXIT)

        elif os.WIFSTOPPED(status):
            sig = os.WSTOPSIG(status)
            self.handlePosixSignal(sig)

        else:
            print "OMG WTF JUST HAPPENED??!?11/!?1?>!"
Example #10
0
def wait_status_to_string(status):
    def num_to_sig(num):
        sigs = ["SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO", "SIGPWR", "SIGSYS"]
        if num-1 < len(sigs):
            return sigs[num-1]
        else:
            return str(num)


    ff = [os.WCOREDUMP, os.WIFSTOPPED, os.WIFSIGNALED, os.WIFEXITED, os.WIFCONTINUED]
    status_list = []
    status_list.append(str(status))
    for f in ff:
        if f(status):
            status_list.append(f.__name__)
    status_list.append(num_to_sig(os.WEXITSTATUS(status)))
    status_list.append(num_to_sig(os.WSTOPSIG(status)))
    status_list.append(num_to_sig(os.WTERMSIG(status)))
    if(os.WIFSTOPPED(status)):
        ss = (status & 0xfff00) >> 8
        ptrace_sigs = ["PTRACE_EVENT_FORK", "PTRACE_EVENT_VFORK", "PTRACE_EVENT_CLONE", "PTRACE_EVENT_EXEC", "PTRACE_EVENT_VFORK_DONE", "PTRACE_EVENT_EXIT"]
        if ss > 0 and ss-1 < len(ptrace_sigs):
            status_list.append(ptrace_sigs[ss-1])
    return "|".join(status_list)
Example #11
0
 def _wait_for_child(self, childpid):
     while not self.__child_exited:
         # Could implement a maximum wait time here
         self.log.trace('waiting for child %d' % childpid)
         time.sleep(1.0)
         #raise getmailDeliveryError('failed waiting for commands %s %d (%s)'
         #                           % (self.conf['command'], childpid, o))
     if self.__child_pid != childpid:
         #self.log.error('got child pid %d, not %d' % (pid, childpid))
         raise getmailOperationError('got child pid %d, not %d' %
                                     (self.__child_pid, childpid))
     if os.WIFSTOPPED(self.__child_status):
         raise getmailOperationError(
             'child pid %d stopped by signal %d' %
             (self.__child_pid, os.WSTOPSIG(self.__child_status)))
     if os.WIFSIGNALED(self.__child_status):
         raise getmailOperationError(
             'child pid %d killed by signal %d' %
             (self.__child_pid, os.WTERMSIG(self.__child_status)))
     if not os.WIFEXITED(self.__child_status):
         raise getmailOperationError('child pid %d failed to exit' %
                                     self.__child_pid)
     exitcode = os.WEXITSTATUS(self.__child_status)
     return exitcode
Example #12
0
def _debugger_thread_inner(main_pid, dbgproc_started, dbgthread_stop,
                           stack_request_pipe, stack_queue, syscall_queue,
                           syscall_filter):
    ptrace_options = ptrace.PTRACE_O_TRACECLONE
    # Attach to the tracee and wait for it to stop.
    ptrace.attach_and_wait(main_pid, ptrace_options)

    if syscall_filter is not None:
        filter_ = lambda sc: any(m.match(sc) for m in syscall_filter)
    else:
        filter_ = None

    syscall_trap = signal.SIGTRAP | 0x80
    enabled = False
    signum = 0
    syscall_state = {}
    sigstop_received = set()

    processes = {main_pid}
    mem_fds = {}
    mem_fds[main_pid] = _open_procmem(main_pid)

    # Notify the parent that we are ready to start tracing.
    dbgproc_started.set()

    try:
        # Restart the tracee and enter the tracing loop.
        ptrace.syscall(main_pid)

        while True:
            if dbgthread_stop.is_set():
                break

            pid, status = ptrace.wait(-1)

            if os.WIFEXITED(status) or os.WIFSIGNALED(status):
                # Traced thread has died.
                processes.discard(pid)
                mem_fd = mem_fds.get(pid)
                if mem_fd is not None:
                    try:
                        os.close(mem_fd)
                    except IOError:
                        pass
                if not processes:
                    break
                else:
                    continue

            elif os.WIFSTOPPED(status):
                ptrace_event = ptrace.WPTRACEEVENT(status)
                if ptrace_event == ptrace.PTRACE_EVENT_CLONE:
                    # A new thread has been created.
                    new_pid = ptrace.geteventmsg(pid)
                    # See the comment below for the explanation of this check.
                    if new_pid not in sigstop_received:
                        ptrace.wait_for_trace_stop(new_pid)
                        try:
                            ptrace.syscall(new_pid)
                        except OSError as e:
                            if e.errno != errno.ESRCH:
                                # The new thread might have already died.
                                raise
                    else:
                        sigstop_received.discard(new_pid)

                    mem_fds[new_pid] = _open_procmem(new_pid)

                    processes.add(new_pid)
                    ptrace.syscall(pid)
                    continue

                stopsig = os.WSTOPSIG(status)
                if stopsig != syscall_trap:
                    # Signal-delivery-stop.

                    # The special condition below is for cases when we
                    # receive a SIGSTOP for a newly created thread _before_
                    # receiving the PTRACE_EVENT_CLONE event for its parent.
                    # In this case we must not forward the signal, but
                    # must record its receipt so that once we _do_ receive
                    # PTRACE_EVENT_CLONE for the parent, we don't wait for
                    # SIGSTOP in the child again.
                    if (stopsig != signal.SIGSTOP or pid in processes
                            or all(syscall.name != 'clone'
                                   for syscall in syscall_state.values()
                                   if syscall is not None)):
                        # forward the signal
                        signum = stopsig
                    else:
                        sigstop_received.add(pid)
                else:
                    # Syscall-stop.
                    syscall = syscall_state.get(pid)
                    regs = ptrace.getregs(pid)
                    mem_fd = mem_fds.get(pid)

                    if syscall is None:
                        # Syscall-enter-stop.
                        syscall_state[pid] = ptrace.syscall_enter(
                            pid, regs, mem_fd)
                    else:
                        # Syscall-exit-stop.
                        ptrace.syscall_exit(syscall, regs, mem_fd)

                        if enabled:
                            # Stop tracing once the tracee executes
                            # the magic open() in ptracer.disable().
                            stop_tracing = (
                                syscall.name == 'open'
                                and syscall.args[0].value == b'\x03\x02\x01'
                            ) or (syscall.name == 'openat'
                                  and syscall.args[1].value == b'\x03\x02\x01')

                            if stop_tracing:
                                break
                            elif filter_ is None or filter_(syscall):
                                # Wait for the traceback to arrive.
                                os.write(stack_request_pipe,
                                         struct.pack('!Q', pid))
                                stack = stack_queue.get()
                                if stack is None:
                                    ptrace.cont(pid)
                                    break

                                syscall.traceback = stack
                                syscall_queue.put_nowait(syscall)

                        elif not enabled:
                            # Start tracing once the tracee executes
                            # the magic open() in ptracer.enable().
                            start_tracing = (
                                syscall.name == 'open'
                                and syscall.args[0].value == b'\x01\x02\x03'
                            ) or (syscall.name == 'openat'
                                  and syscall.args[1].value == b'\x01\x02\x03')

                            if start_tracing:
                                enabled = True

                        syscall_state[pid] = None
            else:
                logger.error('unexpected status of traced process %s: %s', pid,
                             status)

            # Continue until next syscall.
            ptrace.syscall(pid, signum)
            signum = 0
    finally:
        for process in processes:
            try:
                ptrace.detach(process)
            except OSError as e:
                if e.errno == errno.ESRCH:
                    pass
                else:
                    raise

        for fd in mem_fds.values():
            try:
                os.close(fd)
            except (OSError, IOError):
                pass
Example #13
0
    def isalive(self):
        '''This tests if the child process is running or not. This is
        non-blocking. If the child was terminated then this will read the
        exitstatus or signalstatus of the child. This returns True if the child
        process appears to be running or False if not. It can take literally
        SECONDS for Solaris to return the right status. '''

        if self.terminated:
            return False

        if self.flag_eof:
            # This is for Linux, which requires the blocking form
            # of waitpid to get the status of a defunct process.
            # This is super-lame. The flag_eof would have been set
            # in read_nonblocking(), so this should be safe.
            waitpid_options = 0
        else:
            waitpid_options = os.WNOHANG

        try:
            pid, status = os.waitpid(self.pid, waitpid_options)
        except OSError as e:
            # No child processes
            if e.errno == errno.ECHILD:
                raise PtyProcessError(
                    'isalive() encountered condition ' +
                    'where "terminated" is 0, but there was no child ' +
                    'process. Did someone else call waitpid() ' +
                    'on our process?')
            else:
                raise

        # I have to do this twice for Solaris.
        # I can't even believe that I figured this out...
        # If waitpid() returns 0 it means that no child process
        # wishes to report, and the value of status is undefined.
        if pid == 0:
            try:
                ### os.WNOHANG) # Solaris!
                pid, status = os.waitpid(self.pid, waitpid_options)
            except OSError as e:  # pragma: no cover
                # This should never happen...
                if e.errno == errno.ECHILD:
                    raise PtyProcessError(
                        'isalive() encountered condition ' +
                        'that should never happen. There was no child ' +
                        'process. Did someone else call waitpid() ' +
                        'on our process?')
                else:
                    raise

            # If pid is still 0 after two calls to waitpid() then the process
            # really is alive. This seems to work on all platforms, except for
            # Irix which seems to require a blocking call on waitpid or select,
            # so I let read_nonblocking take care of this situation
            # (unfortunately, this requires waiting through the timeout).
            if pid == 0:
                return True

        if pid == 0:
            return True

        if os.WIFEXITED(status):
            self.status = status
            self.exitstatus = os.WEXITSTATUS(status)
            self.signalstatus = None
            self.terminated = True
        elif os.WIFSIGNALED(status):
            self.status = status
            self.exitstatus = None
            self.signalstatus = os.WTERMSIG(status)
            self.terminated = True
        elif os.WIFSTOPPED(status):
            raise PtyProcessError(
                'isalive() encountered condition ' +
                'where child process is stopped. This is not ' +
                'supported. Is some other process attempting ' +
                'job control with our child pid?')
        return False
Example #14
0
class ParTestCase(unittest.TestCase):
    """A class whose instances are single test cases.

    The ParTestCase starts a new process for each test
    this enables the isolation of tests one from the other.
    """
    def __init__(self, methodName='runTest'):
        """Create an instance of the class that will use the named test
           method when executed. Raises a ValueError if the instance does
           not have a method with the specified name.
        """
        try:
            self.__testMethodName = methodName
            testMethod = getattr(self, methodName)
            self.__testMethodDoc = testMethod.__doc__
        except AttributeError:
            raise ValueError, "no such test method in %s: %s" % \
                  (self.__class__, methodName)

    def shortDescription(self):
        """Returns a one-line description of the test, or None if no
        description has been provided.

        The default implementation of this method returns the first line of
        the specified test method's docstring.
        """
        doc = self.__testMethodDoc
        return doc and string.strip(string.split(doc, "\n")[0]) or None

    def id(self):
        return "%s.%s" % (self.__class__, self.__testMethodName)

    def __str__(self):
        return "%s (%s)" % (self.__testMethodName, self.__class__)

    def __repr__(self):
        return "<%s testMethod=%s>" % \
               (self.__class__, self.__testMethodName)

    def __call__(self, result=None):
        if result is None: result = self.defaultTestResult()
        result.startTest(self)
        testMethod = getattr(self, self.__testMethodName)
        try:
            result.createPipe()
            tpid = os.fork()
            if tpid == 0:
                #
                # The child processes should close the read side of the pipe.
                #
                result.closePipeRd()

                try:
                    self.setUp()
                except KeyboardInterrupt:
                    os._exit(-1)
                except:
                    result.addError(self, self.__exc_info())
                    os._exit(0)

                ok = 0
                try:
                    testMethod()
                    ok = 1
                except self.failureException, e:
                    result.addFailure(self, self.__exc_info())
                except KeyboardInterrupt:
                    os._exit(-1)
                except:
                    result.addError(self, self.__exc_info())

                try:
                    self.tearDown()
                except KeyboardInterrupt:
                    os._exit(-1)
                except:
                    result.addError(self, self.__exc_info())
                    ok = 0

                if ok:
                    result.addSuccess(self)

                #
                # IMPORTANT NOTE:
                # child processses of the test processes (tpid), can throw
                # exceptions either explicitly or implicitly through assert_
                # and other unittest functions. This means that they reach
                # the os._exit command below. This exit command avoids that
                # the exceptions propogate further 'up' in the code.
                #
                os._exit(0)

            #
            # The parent process should close the write side of the pipe.
            #
            result.closePipeWr()

            #
            # Set the watchdog
            #
            test_timeout = getattr(self, '_TEST_TIMEOUT', TEST_TIMEOUT)
            wd = WatchDog(timeout=test_timeout)

            try:
                try:
                    cpid, status = os.waitpid(tpid, 0)
                except KeyboardInterrupt:
                    raise
                except:
                    result.addError(self, self.__exc_info())
            finally:
                #
                # Turn of the watchdog
                #
                wd.close()

            #
            # Check the exit status. This part is wraped in a try caluse
            # so that I can use the addError method.
            #
            try:
                if os.WIFEXITED(status) and os.WEXITSTATUS(status) != 0:
                    if os.WEXITSTATUS(status) == 255:
                        raise KeyboardInterrupt
                    else:
                        raise TestError, 'The test process exited unexpectedly with code %d' % (
                            os.WEXITSTATUS(status) - 256)

                if os.WIFSTOPPED(status):
                    sig = os.WSTOPSIG(status)
                    if sig in SIGNALS_DICT.keys():
                        sig_str = '%s(%d)' % (SIGNALS_DICT[sig], sig)
                    else:
                        sig_str = 'None(%d)' % sig

                    raise TestError, 'The test process stopped unexpectedly by signal %s' % sig_str

                if os.WIFSIGNALED(status):
                    sig = os.WTERMSIG(status)
                    if sig in SIGNALS_DICT.keys():
                        sig_str = '%s(%d)' % (SIGNALS_DICT[sig], sig)
                    else:
                        sig_str = 'None(%d)' % sig

                    raise TestError, 'The test process terminated unexpectedly by signal %s' % sig_str

            except KeyboardInterrupt:
                raise
            except:
                result.addError(self, self.__exc_info())
Example #15
0
        try:
            os.execvp("./" + path, args)
        except Exception, e:
            # print traceback if exception occurs
            import traceback
            traceback.print_exc(file=os.sys.stderr)
        # always exit
        os._exit(1)
    else:  # parent

        t = timeout
        while t >= 0:
            # wait for completion
            child_pid, status = os.waitpid(pid, os.WNOHANG)
            if child_pid == pid:
                if os.WIFSTOPPED(status) or \
                   os.WIFSIGNALED(status):
                    return None
                elif os.WIFEXITED(status):
                    return os.WEXITSTATUS(status)
            # wait for a second
            time.sleep(1)
            t -= 1

        # not completed within timeout seconds
        TimedOut = 1
        os.kill(pid, 9)
        os.kill(pid + 1, 9)


from os import walk
Example #16
0
    def RunProgram(self, program, arguments, context, result):
        """Run the 'program'.

        'program' -- The path to the program to run.

        'arguments' -- A list of the arguments to the program.  This
        list must contain a first argument corresponding to 'argv[0]'.

        'context' -- A 'Context' giving run-time parameters to the
        test.

        'result' -- A 'Result' object.  The outcome will be
        'Result.PASS' when this method is called.  The 'result' may be
        modified by this method to indicate outcomes other than
        'Result.PASS' or to add annotations."""

        # Construct the environment.
        environment = self.MakeEnvironment(context)
        # Create the executable.
        if self.timeout >= 0:
            timeout = self.timeout
        else:
            # If no timeout was specified, we sill run this process in a
            # separate process group and kill the entire process group
            # when the child is done executing.  That means that
            # orphaned child processes created by the test will be
            # cleaned up.
            timeout = -2
        e = qm.executable.Filter(self.stdin, timeout)
        # Run it.
        exit_status = e.Run(arguments, environment, path=program)

        # If the process terminated normally, check the outputs.
        if sys.platform == "win32" or os.WIFEXITED(exit_status):
            # There are no causes of failure yet.
            causes = []
            # The target program terminated normally.  Extract the
            # exit code, if this test checks it.
            if self.exit_code is None:
                exit_code = None
            elif sys.platform == "win32":
                exit_code = exit_status
            else:
                exit_code = os.WEXITSTATUS(exit_status)
            # Get the output generated by the program.
            stdout = e.stdout
            stderr = e.stderr
            # Record the results.
            result["RoseTest.exit_code"] = str(exit_code)
            result["RoseTest.stdout"] = result.Quote(stdout)
            result["RoseTest.stderr"] = result.Quote(stderr)
            # Check to see if the exit code matches.
            if exit_code != self.exit_code:
                causes.append("exit_code")
                result["RoseTest.expected_exit_code"] = str(self.exit_code)

            # Validate the output.
            causes += self.ValidateOutput(stdout, stderr, result,
                                          arguments[len(arguments) - 1])

            # If anything went wrong, the test failed.
            if causes:
                result.Fail("Unexpected %s." % string.join(causes, ", "))

        elif os.WIFSIGNALED(exit_status):
            # The target program terminated with a signal.  Construe
            # that as a test failure.
            signal_number = str(os.WTERMSIG(exit_status))
            result.Fail("Program terminated by signal.")
            result["RoseTest.signal_number"] = signal_number

            # START MODIFICATION GMY 7/26/2006

            # Get the output generated by the program.
            stdout = e.stdout
            stderr = e.stderr

            result["RoseTest.stdout"] = stdout
            result["RoseTest.stderr"] = stderr

# END MODIFICATION GMY 7/26/2006

        elif os.WIFSTOPPED(exit_status):
            # The target program was stopped.  Construe that as a
            # test failure.
            signal_number = str(os.WSTOPSIG(exit_status))
            result.Fail("Program stopped by signal.")
            result["RoseTest.signal_number"] = signal_number
        else:
            # The target program terminated abnormally in some other
            # manner.  (This shouldn't normally happen...)
            result.Fail("Program did not terminate normally.")
Example #17
0
                raise DistutilsExecError, \
                      "command %r terminated by signal %d" % \
                      (cmd, os.WTERMSIG(status))

            elif os.WIFEXITED(status):
                exit_status = os.WEXITSTATUS(status)
                if exit_status == 0:
                    return  # hey, it succeeded!
                else:
                    if not DEBUG:
                        cmd = executable
                    raise DistutilsExecError, \
                          "command %r failed with exit status %d" % \
                          (cmd, exit_status)

            elif os.WIFSTOPPED(status):
                continue

            else:
                if not DEBUG:
                    cmd = executable
                raise DistutilsExecError, \
                      "unknown error executing %r: termination status %d" % \
                      (cmd, status)


def find_executable(executable, path=None):
    """Tries to find 'executable' in the directories listed in 'path'.

    A string listing directories separated by 'os.pathsep'; defaults to
    os.environ['PATH'].  Returns the complete filename or None if not found.
Example #18
0
def runCommandWithOutput(command):
    """
    _runCommand_

    Run the command without deadlocking stdout and stderr,
    echo all output to sys.stdout and sys.stderr

    Returns the exitCode and the a string containing std out & error

    """
    # capture stdout and stderr from command
    child = Popen(command,
                  shell=True,
                  bufsize=1,
                  stdin=PIPE,
                  stdout=PIPE,
                  stderr=PIPE,
                  close_fds=True)
    child.stdin.close()  # don't need to talk to child
    outfile = child.stdout
    outfd = outfile.fileno()
    errfile = child.stderr
    errfd = errfile.fileno()
    makeNonBlocking(outfd)  # don't deadlock!
    makeNonBlocking(errfd)
    outdata = errdata = ''
    outeof = erreof = 0
    output = ''
    while True:
        ready = select.select([outfd, errfd], [], [])  # wait for input
        if outfd in ready[0]:
            try:
                outchunk = outfile.read()
            except Exception as ex:
                msg = "Unable to read stdout chunk... skipping"
                print(msg)
                outchunk = ''
            if outchunk == '': outeof = 1
            sys.stdout.write(outchunk)
            output += outchunk
        if errfd in ready[0]:
            try:
                errchunk = errfile.read()
            except Exception as ex:
                msg = "Unable to read stderr chunk... skipping"
                print(msg, str(ex))
                errchunk = ""
            if errchunk == '': erreof = 1
            output += errchunk
            sys.stderr.write(errchunk)
        if outeof and erreof: break
        select.select([], [], [], .1)  # give a little time for buffers to fill

    err = child.wait()
    if os.WIFEXITED(err):
        err = os.WEXITSTATUS(err)
    elif os.WIFSIGNALED(err):
        err = os.WTERMSIG(err)
    elif os.WIFSTOPPED(err):
        err = os.WSTOPSIG(err)
    return err, output
Example #19
0
def main_run_server(args):
    import io
    import signal
    import socket

    benchmark_dir, socket_name, = args

    update_sys_path(benchmark_dir)

    # Socket I/O
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    s.bind(socket_name)
    s.listen(1)

    # Read and act on commands from socket
    while True:
        stdout_file = None

        try:
            conn, addr = s.accept()
        except KeyboardInterrupt:
            break

        try:
            fd, stdout_file = tempfile.mkstemp()
            os.close(fd)

            # Read command
            read_size, = struct.unpack('<Q', recvall(conn, 8))
            command_text = recvall(conn, read_size)
            if sys.version_info[0] >= 3:
                command_text = command_text.decode('utf-8')

            # Parse command
            command = json.loads(command_text)
            action = command.pop('action')

            if action == 'quit':
                break
            elif action == 'preimport':
                # Import benchmark suite before forking.
                # Capture I/O to a file during import.
                with posix_redirect_output(stdout_file, permanent=False):
                    for benchmark in disc_benchmarks(benchmark_dir, ignore_import_errors=True):
                        pass

                # Report result
                with io.open(stdout_file, 'r', errors='replace') as f:
                    out = f.read()
                out = json.dumps(out)
                if sys.version_info[0] >= 3:
                    out = out.encode('utf-8')
                conn.sendall(struct.pack('<Q', len(out)))
                conn.sendall(out)
                continue

            benchmark_id = command.pop('benchmark_id')
            params_str = command.pop('params_str')
            profile_path = command.pop('profile_path')
            result_file = command.pop('result_file')
            timeout = command.pop('timeout')
            cwd = command.pop('cwd')

            if command:
                raise RuntimeError('Command contained unknown data: {!r}'.format(command_text))

            # Spawn benchmark
            run_args = (benchmark_dir, benchmark_id, params_str, profile_path, result_file)
            pid = os.fork()
            if pid == 0:
                conn.close()
                sys.stdin.close()
                exitcode = 1
                try:
                    with posix_redirect_output(stdout_file, permanent=True):
                        try:
                            os.chdir(cwd)
                            main_run(run_args)
                            exitcode = 0
                        except BaseException as ec:
                            import traceback
                            traceback.print_exc()
                finally:
                    os._exit(exitcode)

            # Wait for results
            # (Poll in a loop is simplest --- also used by subprocess.py)
            start_time = wall_timer()
            is_timeout = False
            while True:
                res, status = os.waitpid(pid, os.WNOHANG)
                if res != 0:
                    break

                if timeout is not None and wall_timer() > start_time + timeout:
                    # Timeout
                    if is_timeout:
                        os.kill(pid, signal.SIGKILL)
                    else:
                        os.kill(pid, signal.SIGTERM)
                    is_timeout = True

                time.sleep(0.05)

            # Report result
            with io.open(stdout_file, 'r', errors='replace') as f:
                out = f.read()

            # Emulate subprocess
            if os.WIFSIGNALED(status):
                retcode = -os.WTERMSIG(status)
            elif os.WIFEXITED(status):
                retcode = os.WEXITSTATUS(status)
            elif os.WIFSTOPPED(status):
                retcode = -os.WSTOPSIG(status)
            else:
                # shouldn't happen, but fail silently
                retcode = -128

            info = {'out': out,
                    'errcode': -256 if is_timeout else retcode}

            result_text = json.dumps(info)
            if sys.version_info[0] >= 3:
                result_text = result_text.encode('utf-8')

            conn.sendall(struct.pack('<Q', len(result_text)))
            conn.sendall(result_text)
        except KeyboardInterrupt:
            break
        finally:
            conn.close()
            if stdout_file is not None:
                os.unlink(stdout_file)
Example #20
0
    def platformProcessEvent(self, event):
        # Skim some linux specific events before passing to posix
        tid, status = event
        if os.WIFSTOPPED(status):
            sig = status >> 8  # Cant use os.WSTOPSIG() here...
            #print('STOPPED: %d %d %.8x %d' % (self.pid, tid, status, sig))

            # Ok... this is a crazy little state engine that tries
            # to account for the discrepancies in how linux posts
            # signals to the debugger...

            # Thread Creation:
            # In each case below, the kernel may deliver
            # any of the 3 signals in any order...  ALSO
            # (and more importantly) *if* the kernel sends
            # SIGSTOP to the thread first, the debugger
            # will get a SIGSTOP *instead* of SIG_LINUX_CLONE
            # ( this will go back and forth on *ONE BOX* with
            #   the same kernel version... Finally squished it
            #   because it presents more frequently ( 1 in 10 )
            #   on my new ARM linux dev board. WTF?!1?!one?!? )
            #
            # Case 1 (SIG_LINUX_CLONE):
            #     debugger gets SIG_LINUX CLONE as expected
            #     and can then use ptrace(PT_GETEVENTMSG)
            #     to get new TID and attach as normal
            # Case 2 (SIGSTOP delivered to thread)
            #     Thread is already stoped and attached but
            #     parent debugger doesn't know yet.  We add
            #     the tid to the stopped_cache so when the
            #     kernel gets around to telling the debugger
            #     we don't wait on him again.
            # Case 3 (SIGSTOP delivered to debugger)
            #     In both case 2 and case 3, this will cause
            #     the SIG_LINUX_CLONE to be skipped.  Either
            #     way, we should head down into thread attach.
            #     ( The thread may be already stopped )
            if sig == SIG_LINUX_SYSCALL:
                self.fireNotifiers(vtrace.NOTIFY_SYSCALL)

            elif sig == SIG_LINUX_EXIT:

                ecode = self.getPtraceEvent() >> 8

                if tid == self.getPid():
                    self._fireExit(ecode)
                    self.platformDetach()

                else:
                    self.detachThread(tid, ecode)

            elif sig == SIG_LINUX_CLONE:
                # Handle a new thread here!
                newtid = self.getPtraceEvent()
                #print('CLONE (new tid: %d)' % newtid)
                self.attachThread(newtid, attached=True)

            elif sig == signal.SIGSTOP and tid != self.pid:
                #print('OMG IM THE NEW THREAD! %d' % tid)
                # We're not even a real event right now...
                self.runAgain()
                self._stopped_cache[tid] = True

            elif sig == signal.SIGSTOP:
                # If we are still 'exec()'ing, we havent hit the SIGTRAP
                # yet ( so our process info is still python, lets skip it )
                if self.execing:
                    self._stopped_hack = True
                    self.setupPtraceOptions(tid)
                    self.runAgain()

                elif self._stopped_hack:
                    newtid = self.getPtraceEvent(tid)
                    #print("WHY DID WE GET *ANOTHER* STOP?: %d" % tid)
                    #print('PTRACE EVENT: %d' % newtid)
                    self.attachThread(newtid, attached=True)

                else:  # on first attach...
                    self._stopped_hack = True
                    self.setupPtraceOptions(tid)
                    self.handlePosixSignal(sig)

            #FIXME eventually implement child catching!
            else:
                self.handlePosixSignal(sig)

            return

        v_posix.PosixMixin.platformProcessEvent(self, event)
Example #21
0
    def alive(self, recover=False):
        """
        try to determine if the child process is still active.  If not, mark 
        the child as dead and close all IO descriptors etc ("func:`finalize`).

        If `recover` is `True` and the child is indeed dead, we attempt to
        re-initialize it (:func:`initialize`).  We only do that for so many
        times (`self.recover_max`) before giving up -- at that point it seems
        likely that the child exits due to a re-occurring operations condition.

        Note that upstream consumers of the :class:`PTYProcess` should be
        careful to only use `recover=True` when they can indeed handle
        a disconnected/reconnected client at that point, i.e. if there are no
        assumptions on persistent state beyond those in control of the upstream
        consumers themselves.
        """

        with self.rlock:

            # do we have a child which we can check?
            if self.child:

                wstat = None

                while True:
                    # print 'waitpid %s' % self.child

                    # hey, kiddo, whats up?
                    try:
                        wpid, wstat = os.waitpid(self.child, os.WNOHANG)
                    # print 'waitpid %s : %s - %s' % (self.child, wpid, wstat)

                    except OSError as e:

                        if e.errno == errno.ECHILD:
                            # child disappeared, go to zombie cleanup routine
                            break

                        raise ("waitpid failed on wait (%s)" % e)

                    # did we get a note about child termination?
                    if 0 == wpid:
                        # print 'waitpid %s : %s - %s -- none' % (self.child, wpid, wstat)
                        # nope, all is well - carry on
                        return True

                    # Yes, we got a note.
                    # Well, maybe the child fooled us and is just playing dead?
                    if os.WIFSTOPPED   (wstat) or \
                       os.WIFCONTINUED (wstat)    :
                        # print 'waitpid %s : %s - %s -- stop/cont' % (self.child, wpid, wstat)
                        # we don't care if someone stopped/resumed the child -- that is up
                        # to higher powers.  For our purposes, the child is alive.  Ha!
                        continue

                    break

                # so its dead -- make sure it stays dead, to avoid zombie
                # apocalypse...
            # print "he's dead, honeybunny, jim is dead..."
                self.child = None
                self.finalize(wstat=wstat)

            # check if we can attempt a post-mortem revival though
            if not recover:
                # print 'not alive, not recover'
                # nope, we are on holy ground - revival not allowed.
                return False

            # we are allowed to revive!  So can we try one more time...  pleeeease??
            # (for cats, allow up to 9 attempts; for Buddhists, always allow to
            # reincarnate, etc.)
            if self.recover_attempts >= self.recover_max:
                # nope, its gone for good - just report the sad news
                # print 'not alive, no recover anymore'
                return False

            # MEDIIIIC!!!!
            self.recover_attempts += 1
            self.initialize()

            # well, now we don't trust the child anymore, of course!  So we check
            # again.  Yes, this is recursive -- but note that recover_attempts get
            # incremented on every iteration, and this will eventually lead to
            # call termination (tm).
            # print 'alive, or not alive?  Check again!'
            return self.alive(recover=True)
Example #22
0
                errchunk = errfile.read()
            except Exception, ex:
                msg = "Unable to read stderr chunk... skipping"
                print msg, str(ex)
                errchunk = ""
            if errchunk == '': erreof = 1
            sys.stderr.write(errchunk)
        if outeof and erreof: break
        select.select([], [], [], .1)  # give a little time for buffers to fill

    err = child.wait()
    if os.WIFEXITED(err):
        return os.WEXITSTATUS(err)
    elif os.WIFSIGNALED(err):
        return os.WTERMSIG(err)
    elif os.WIFSTOPPED(err):
        return os.WSTOPSIG(err)
    return err


def runCommandWithOutput(command):
    """
    _runCommand_

    Run the command without deadlocking stdout and stderr,
    echo all output to sys.stdout and sys.stderr

    Returns the exitCode and the a string containing std out & error

    """
    child = popen2.Popen3(command, 1)  # capture stdout and stderr from command
Example #23
0
def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0):
    global _cfg_target
    global _cfg_target_split
    log.info(' '.join(cmd))
    if dry_run:
        return
    else:
        executable = cmd[0]
        exec_fn = search_path and os.execvp or os.execv
        env = None
        if sys.platform == 'darwin':
            if _cfg_target is None:
                _cfg_target = sysconfig.get_config_var(
                    'MACOSX_DEPLOYMENT_TARGET') or ''
                if _cfg_target:
                    _cfg_target_split = [
                        int(x) for x in _cfg_target.split('.')
                    ]
            if _cfg_target:
                cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET',
                                            _cfg_target)
                if _cfg_target_split > [int(x) for x in cur_target.split('.')]:
                    my_msg = '$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' % (
                        cur_target, _cfg_target)
                    raise DistutilsPlatformError(my_msg)
                env = dict(os.environ, MACOSX_DEPLOYMENT_TARGET=cur_target)
                exec_fn = search_path and os.execvpe or os.execve
        pid = os.fork()
        if pid == 0:
            try:
                if env is None:
                    exec_fn(executable, cmd)
                else:
                    exec_fn(executable, cmd, env)
            except OSError as e:
                if not DEBUG:
                    cmd = executable
                sys.stderr.write('unable to execute %r: %s\n' %
                                 (cmd, e.strerror))
                os._exit(1)

            if not DEBUG:
                cmd = executable
            sys.stderr.write('unable to execute %r for unknown reasons' % cmd)
            os._exit(1)
        else:
            while 1:
                try:
                    pid, status = os.waitpid(pid, 0)
                except OSError as exc:
                    import errno
                    if exc.errno == errno.EINTR:
                        continue
                    if not DEBUG:
                        cmd = executable
                    raise DistutilsExecError, 'command %r failed: %s' % (
                        cmd, exc[-1])

                if os.WIFSIGNALED(status):
                    if not DEBUG:
                        cmd = executable
                    raise DistutilsExecError, 'command %r terminated by signal %d' % (
                        cmd, os.WTERMSIG(status))
                elif os.WIFEXITED(status):
                    exit_status = os.WEXITSTATUS(status)
                    if exit_status == 0:
                        return
                    if not DEBUG:
                        cmd = executable
                    raise DistutilsExecError, 'command %r failed with exit status %d' % (
                        cmd, exit_status)
                elif os.WIFSTOPPED(status):
                    continue
                else:
                    if not DEBUG:
                        cmd = executable
                    raise DistutilsExecError, 'unknown error executing %r: termination status %d' % (
                        cmd, status)

        return
Example #24
0
    os.chdir('/')
    os.setsid()
    os.umask(0)

    try:
        pid = os.fork()
        if pid > 0:
            time.sleep(0.2)

            id, status = os.waitpid(pid, os.WNOHANG)
            if id == 0: sys.exit(0)

            print '-->', id, status
            print 'WCOREDUMP', os.WCOREDUMP(status)
            print 'WIFCONTINUED', os.WIFCONTINUED(status)
            print 'WIFSTOPPED', os.WIFSTOPPED(status)
            print 'WIFSIGNALED', os.WIFSIGNALED(status)
            print 'WIFEXITED', os.WIFEXITED(status)
            print 'WEXITSTATUS', os.WEXITSTATUS(status)
            print 'WSTOPSIG', os.WSTOPSIG(status)
            print 'WTERMSIG', os.WTERMSIG(status)

            sys.exit(0)
    except OSError, e:
        print >> sys.stderr, 'fork() ERROR:', e.errno, e.strerror
        sys.exit(1)

    pid = os.getpid()
    print 'River daemon pid', pid

    os.close(0)
Example #25
0
    def wait(self):
        """ 
        blocks forever until the child finishes on its own, or is getting
        killed.  

        Actully, we might just as well try to figure out what is going on on the
        remote end of things -- so we read the pipe until the child dies...
        """

        output = ""
        # yes, for ever and ever...
        while True:
            try:
                output += self.read()
            except:
                break

        # yes, for ever and ever...
        while True:

            if not self.child:
                # this was quick ;-)
                return output

            # we need to lock, as the SIGCHLD will only arrive once
            with self.rlock:
                # hey, kiddo, whats up?
                try:
                    wpid, wstat = os.waitpid(self.child, 0)

                except OSError as e:

                    if e.errno == errno.ECHILD:

                        # child disappeared
                        self.exit_code = None
                        self.exit_signal = None
                        self.finalize()
                        return output

                    # no idea what happened -- it is likely bad
                    raise se.NoSuccess("waitpid failed on wait")

                # did we get a note about child termination?
                if 0 == wpid:

                    # nope, all is well - carry on
                    continue

                # Yes, we got a note.
                # Well, maybe the child fooled us and is just playing dead?
                if os.WIFSTOPPED   (wstat) or \
                   os.WIFCONTINUED (wstat)    :
                    # we don't care if someone stopped/resumed the child -- that is up
                    # to higher powers.  For our purposes, the child is alive.  Ha!
                    continue

                # not stopped, poor thing... - soooo, what happened??  But hey,
                # either way, its dead -- make sure it stays dead, to avoid
                # zombie apocalypse...
                self.child = None
                self.finalize(wstat=wstat)

                return output
Example #26
0
    def fork_worker(self, job):
        """Invoked by ``work`` method. ``fork_worker`` does the actual forking to create the child
        process that will process the job. It's also responsible for monitoring the child process
        and handling hangs and crashes.

        Finally, the ``process`` method actually processes the job by eventually calling the Job
        instance's ``perform`` method.

        """
        logger.debug('picked up job')
        logger.debug('job details: %s' % job)
        self.before_fork(job)
        self.child = os.fork()
        if self.child:
            self._setproctitle("Forked %s at %s" %
                               (self.child, datetime.datetime.now()))
            logger.info('Forked %s at %s' %
                        (self.child, datetime.datetime.now()))

            try:
                start = datetime.datetime.now()

                # waits for the result or times out
                while True:
                    pid, status = os.waitpid(self.child, os.WNOHANG)
                    if pid != 0:
                        if os.WIFEXITED(status) and os.WEXITSTATUS(
                                status) == 0:
                            break
                        if os.WIFSTOPPED(status):
                            logger.warning("Process stopped by signal %d" %
                                           os.WSTOPSIG(status))
                        else:
                            if os.WIFSIGNALED(status):
                                raise CrashError(
                                    "Unexpected exit by signal %d" %
                                    os.WTERMSIG(status))
                            raise CrashError("Unexpected exit status %d" %
                                             os.WEXITSTATUS(status))

                    time.sleep(0.5)

                    now = datetime.datetime.now()
                    if self.timeout and ((now - start).seconds > self.timeout):
                        os.kill(self.child, signal.SIGKILL)
                        os.waitpid(-1, os.WNOHANG)
                        raise TimeoutError("Timed out after %d seconds" %
                                           self.timeout)

            except OSError as ose:
                import errno

                if ose.errno != errno.EINTR:
                    raise ose
            except JobError:
                self._handle_job_exception(job)
            finally:
                # If the child process' job called os._exit manually we need to
                # finish the clean up here.
                if self.job():
                    self.done_working(job)

            logger.debug('done waiting')
        else:
            self._setproctitle("Processing %s since %s" %
                               (job, datetime.datetime.now()))
            logger.info('Processing %s since %s' %
                        (job, datetime.datetime.now()))
            self.after_fork(job)

            # re-seed the Python PRNG after forking, otherwise
            # all job process will share the same sequence of
            # random numbers
            random.seed()

            self.process(job)
            os._exit(0)
        self.child = None
Example #27
0
def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0):
    log.info(' '.join(cmd))
    if dry_run:
        return
    executable = cmd[0]
    exec_fn = search_path and os.execvp or os.execv
    env = None
    if sys.platform == 'darwin':
        global _cfg_target, _cfg_target_split
        if _cfg_target is None:
            _cfg_target = sysconfig.get_config_var(
                                  'MACOSX_DEPLOYMENT_TARGET') or ''
            if _cfg_target:
                _cfg_target_split = [int(x) for x in _cfg_target.split('.')]
        if _cfg_target:
            # ensure that the deployment target of build process is not less
            # than that used when the interpreter was built. This ensures
            # extension modules are built with correct compatibility values
            cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target)
            if _cfg_target_split > [int(x) for x in cur_target.split('.')]:
                my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: '
                          'now "%s" but "%s" during configure'
                                % (cur_target, _cfg_target))
                raise DistutilsPlatformError(my_msg)
            env = dict(os.environ,
                       MACOSX_DEPLOYMENT_TARGET=cur_target)
            exec_fn = search_path and os.execvpe or os.execve
    pid = os.fork()
    if pid == 0: # in the child
        try:
            if env is None:
                exec_fn(executable, cmd)
            else:
                exec_fn(executable, cmd, env)
        except OSError as e:
            if not DEBUG:
                cmd = executable
            sys.stderr.write("unable to execute %r: %s\n"
                             % (cmd, e.strerror))
            os._exit(1)

        if not DEBUG:
            cmd = executable
        sys.stderr.write("unable to execute %r for unknown reasons" % cmd)
        os._exit(1)
    else: # in the parent
        # Loop until the child either exits or is terminated by a signal
        # (ie. keep waiting if it's merely stopped)
        while True:
            try:
                pid, status = os.waitpid(pid, 0)
            except OSError as exc:
                if not DEBUG:
                    cmd = executable
                raise DistutilsExecError(
                      "command %r failed: %s" % (cmd, exc.args[-1]))
            if os.WIFSIGNALED(status):
                if not DEBUG:
                    cmd = executable
                raise DistutilsExecError(
                      "command %r terminated by signal %d"
                      % (cmd, os.WTERMSIG(status)))
            elif os.WIFEXITED(status):
                exit_status = os.WEXITSTATUS(status)
                if exit_status == 0:
                    return   # hey, it succeeded!
                else:
                    if not DEBUG:
                        cmd = executable
                    raise DistutilsExecError(
                          "command %r failed with exit status %d"
                          % (cmd, exit_status))
            elif os.WIFSTOPPED(status):
                continue
            else:
                if not DEBUG:
                    cmd = executable
                raise DistutilsExecError(
                      "unknown error executing %r: termination status %d"
                      % (cmd, status))
Example #28
0
    def _run(self, pid):
        # This is the entry point after running either `start` or `attach`.  In
        # both cases we are already the tracer of `pid`, and the process is
        # running.

        # The initial tracee is the only tracee that may stop for other reasons
        # before `PTRACE_EVENT_STOP`, so we handle it specially here.  If the
        # tracee was created with `Engine.start` then it will stop itself with
        # `SIGSTOP`, in which case we will observe group-stop.  But
        # `WPTRACEEVENT(status) will also be `PTRACE_EVENT_STOP` in that case,
        # so there's no reason to distinguish between them.
        while True:
            pid_, status = self._wait()
            if pid != pid_:
                _debug('child <PID:%d> is not initial tracee' % pid_)
                continue

            e = WPTRACEEVENT(status)
            s = os.WSTOPSIG(status)
            if os.WIFSTOPPED(status) and e == PTRACE_EVENT_STOP:
                _debug('seized initial tracee <PID:%d>' % pid)
                self._new_tracee(pid)
                break

            _debug('still waiting for <PID:%d>' % pid)
            # If this is not group-stop (`e` != 0 and `s` != `SIGTRAP`),
            # event-stop (`e` != 0 and `s` == `SIGTRAP`) or syscall-stop (`s` &
            # 0x80), then it must be signal-stop.
            if not e and not s & 0x80:
                cont_signal = s
            else:
                cont_signal = 0

            ptrace_cont(pid, cont_signal)

        # For a child to become a tracee two things must happen: 1)
        # `PTRACE_EVENT_STOP` is observed in the child and 2)
        # `PTRACE_EVENT_{FORK,VFORK,CLONE}`is observed in the parent.  The first
        # condition ensures that the tracee is running and the second lets us
        # know the parent.
        #
        # In the case of the `clone` syscall we must also save the flags used,
        # as they decide the thread group and parent of the child.
        #
        # Caveat: I have only seen `PTRACE_EVENT_STOP` before
        #   `PTRACE_EVENT_{FORK,VFORK,CLONE}` in the case of `vfork`, and not
        #   consistently, but the ptrace man page doesn't say anything about the
        #   order.  Besides, there's no reason to rely on it anyway.

        # `stop_seen` records the children in whom we have observed
        # `PTRACE_EVENT_STOP`.
        stop_seen = set()

        # `parent_seen` maps children to their "parents".  Here parent refers to
        # the process that spawned the child, not the child's parent as reported
        # by `getppid`.
        parent_seen = {}

        # `clone_flags` maps parents to the flags used in the `clone` syscall.
        # When `clone` is called the PID of the child is not yet known, so the
        # mapping cannot be from children.
        clone_flags = {}

        # This function creates a tracee if the conditions discussed above are
        # met.
        def maybe_tracee(pid):
            if pid not in stop_seen:
                _debug('PTRACE_EVENT_STOP has not yet been observed in '
                       '<PID:%d>' % pid)
                return
            if pid not in parent_seen:
                _debug('parent of <PID:%d> has not yet observed '
                       'PTRACE_EVENT_{FORK,VFORK,CLONE}' % pid)
                return
            parent = parent_seen.pop(pid)
            cflags = clone_flags.pop(parent.pid, 0)
            self._new_tracee(pid, parent, cflags)

        while self.tracees:
            try:
                pid, status = self._wait()
            except OSError as e:
                if e.errno == errno.ECHILD:
                    # This may happen if all the tracees are killed by SIGKILL,
                    # so we didn't get a change to observe their death.
                    _debug('no children, exiting')
                    break
                raise

            _debug('wait() -> %d, %02x|%02x|%02x' % \
                   (pid,
                    status >> 16,
                    (status >> 8) & 0xff,
                    status & 0xff))

            if pid not in self.tracees:
                # The child is not a tracee.  That can happen because 1) it was
                # created with follow mode disabled, and is not meant to be a
                # tracee, or 2) this is the first time we see the tracee in
                # which case we expect to observe `PTRACE_EVENT_STOP`

                # According to the `wait(2)` man page `WIFSTOPPED(status)` can
                # only be true if `wait` was called with `UNTRACED` or if the
                # child is a ptrace tracee.  In the second case we must observe
                # `PTRACE_EVENT_STOP`.
                if os.WIFSTOPPED(status):
                    assert WPTRACEEVENT(status) == PTRACE_EVENT_STOP, \
                        'non-tracee child stopped without PTRACE_EVENT_STOP'
                    assert pid not in stop_seen, \
                        'already saw PTRACE_EVENT_STOP for child <PID:%d>' % pid
                    stop_seen.add(pid)
                    # Create and start tracee if we already know the parent
                    maybe_tracee(pid)
                continue

            # OK, this is a proper tracee, continue to the real logic.  First we
            # figure out what happened to it.
            tracee = self.tracees.get(pid)

            s = os.WSTOPSIG(status)
            e = WPTRACEEVENT(status)

            # When we continue the tracee, this is the signal we should send it.
            cont_signal = 0

            # Why did `wait` return this tracee?
            stopped = False
            signalled = False
            continued = False
            exited = False

            # If the tracee was stopped, which kind of stop was it?
            signal_stop = False
            group_stop = False
            event_stop = False
            syscall_stop = False

            # This will be set if we have event-stop.
            event = None

            # Tracers can return a value on syscall-enter in which case the
            # syscall is "emulated"
            # XXX: For some reason I couldn't get ptrace_sysemu to work, so I'm
            # XXX: using User-Mode-Linux's trick and replacing it by a syscall
            # XXX: to `getpid` instead.  See code further down.
            # XXX: Link to UML's SYSEMU patches: http://sysemu.sourceforge.net/
            sysemu = False

            # Here we just set the variables.  The real logic follows below.
            if os.WIFSTOPPED(status):
                stopped = True

                if s == SIGTRAP | 0x80:
                    assert e == 0, \
                        'WPTRACEEVENT(status) should be 0 in syscall-stop'
                    syscall_stop = True
                    syscall = tracee.syscall

                elif e:
                    if s == SIGTRAP:
                        event_stop = True
                        event = e
                    else:
                        assert e == PTRACE_EVENT_STOP, \
                            'WPTRACEEVENT(status) should be ' \
                            'PTRACE_EVENT_STOP in group-stop'
                        group_stop = True

                else:
                    signal_stop = True
                    tracee.siginfo._init()
                    siginfo = tracee.siginfo
                    cont_signal = s

            if os.WIFSIGNALED(status):
                signalled = True
                signal = os.WTERMSIG(status)

            if os.WIFCONTINUED(status):
                continued = True

            if os.WIFEXITED(status):
                exited = True
                status = os.WEXITSTATUS(status)

            # Tracee exited or was killed by a signal, so remove it.  No need to
            # report this exit as we have already done so when we got
            # `PTRACE_EVENT_EXIT`.
            #
            # Caveat: At the moment (kernel 4.5.0) tracees stop in event-stop
            #   with `PTRACE_EVENT_EXIT` even if they are killed by `SIGKILL`.
            #   According to the man page that may change in the future.  We
            #   handle that hypothetical situation below, if ptrace fails with
            #   `ESRCH` when we continue the tracee.
            if exited or signalled:
                _debug('<PID:%d> terminated' % pid)
                self._del_tracee(tracee)
                continue

            # Log events.
            if event:
                _debug('event %d:%s' % (event, event_names[event]))

            # Handle `execve`'s: when a thread which is not the thread group
            # leader executes `execve`, all other threads in the thread group
            # die and the `execve`'ing thread becomes leader.  This code must be
            # executed early as `tracee` is in fact the wrong tracee at this
            # point, and we need to correct that.
            if event == PTRACE_EVENT_EXEC:
                oldpid = ptrace_geteventmsg(pid)
                if pid != oldpid:
                    _debug('repid (%d -> %d)' % (oldpid, pid))
                    # Neither this tracee nor the thread group leader will
                    # report death, so we must do the clean-up here
                    self._del_tracee(tracee)
                    # This is the correct tracee, it just changed its pid
                    tracee = self.tracees.pop(oldpid)
                    tracee.pid = pid
                    tracee.thread_group = set([tracee])
                    tracee.tgid = tracee.pid
                    self.tracees[pid] = tracee
                    self._run_callbacks('repid', tracee, oldpid)

            # We're entering or exiting a syscall so update `in_syscall`.
            # Invariant: a callback for `syscall` will always see `in_syscall`
            # as being true, and a callback for `syscall_return` will always see
            # it as being false.
            #
            # This check must be placed here, before the personality is
            # detected, because a syscall on Linux x86_64 (XXX: and others?) be
            # run in 32 bit mode depending on how it was called (specifically
            # through `int 0x80` or 32 bit `syscall` [which apparently only
            # exists on AMD CPU's and is all but undocumented; see comment in
            # `/linux/arch/x86/entry/entry_32.S`]).
            if syscall_stop:
                # Go from syscall to not in syscall or vice versa
                tracee.in_syscall ^= True

            # See if the tracee changed personality.  This check may depend on
            # `in_syscall` (see comment above).
            self._detect_personality(tracee)

            # OK, now all the tracee's state variables has been set and we're
            # ready to fire callbacks, etc.

            # Trigger single step callbacks.  We do not reset
            # `_was_singlestepped` here because we need it to be set in order to
            # suppress callbacks for `SIGTRAP`.
            if tracee._was_singlestepped:
                _debug('step')
                self._run_callbacks('step', tracee)

            # This tracee was stopped, but now it's running again!
            if not tracee.is_running:
                tracee.is_running = True
                self._run_callbacks('cont', tracee)

            # Handle syscalls.
            if syscall_stop:

                # This is syscall-enter.
                if tracee.in_syscall:
                    # The `nr` and `name` attributes are what the tracer sees
                    # and not the real values in case of a restarted or emulated
                    # syscall.
                    realnr = syscall._get_nr()
                    realname = tracee.syscalls.syscall_names[realnr]

                    _debug('syscal-enter %d:%s' % (realnr, realname))

                    if self.trace_restart or realname != 'restart_syscall':
                        # Initialize syscall object.
                        syscall._init()

                        args = syscall.args
                        retval = self._run_syscall_callbacks(tracee)

                        # We were supposed to enter a syscall, but a tracer
                        # returned a value, so we'll "emulate" the syscall
                        # instead.
                        if retval != None:
                            _debug('emulating syscall %d:%s -> 0x%x' % \
                                   (syscall.nr, syscall.name, retval))
                            # XXX: For some reason `ptrace_sysemu` doesn't seem
                            # XXX: to work for me, so I replace the syscall with
                            # XXX: a "nop" syscall in the form of `getpid`
                            syscall.emulated = True
                            syscall.emu_nr = syscall.nr
                            syscall.emu_retval = retval
                            syscall.name = 'getpid'

                        # We are about to enter an un-emulated (if it was
                        # emulated `name` would be "getpid") `clone` syscall and
                        # we want to follow the child.  We must save the flags
                        # used in the syscall so we can correctly set the parent
                        # and thread group of the newly created process/thread
                        # when it arrives.  Also, if the `CLONE_UNTRACED` flag
                        # is set, we unset it so we become a tracer of the
                        # child.  This check needs to be placed here, after the
                        # callbacks have run, as the syscall may be changed or
                        # simulated by a tracer.  `CLONE_UNTRACED` is defined in
                        # /usr/include/linux/sched.h
                        CLONE_UNTRACED = 0x00800000
                        if self.follow and syscall.name == 'clone':
                            if syscall.args[0] & CLONE_UNTRACED:
                                _debug('removed CLONE_UNTRACED in clone ' \
                                       'syscall')

                            clone_flags[pid] = syscall.args[0]
                            syscall.args[0] &= ~CLONE_UNTRACED

                    else:
                        _debug('ignoring syscall-enter due to restart')

                # This is syscall-exit.
                else:
                    _debug('syscall-exit %d:%s -> %#x' % \
                           (syscall.nr, syscall.name, syscall.retval))

                    if self.trace_restart or \
                       syscall.retval not in RETVAL_RESTART:
                        # Finalize syscall object, i.e. stop the timer.
                        syscall._fini()

                        # This is the initial tracee returning from `execve`
                        if self._wait_initial:
                            self._wait_initial = False
                            self._run_callbacks('birth', tracee)

                        else:
                            # Set syscall number and return value if the syscall
                            # was "emulated", i.e. `getpid`.
                            if syscall.emulated:
                                syscall.nr = syscall.emu_nr
                                syscall.retval = syscall.emu_retval

                            retval = self._run_syscall_callbacks(tracee)

                            if retval != None:
                                _debug('overriding syscall %d:%s -> 0x%x' % \
                                       (syscall.nr, syscall.name, retval))

                                syscall.retval = retval

                            # The `emulated` flag is reset here, after the
                            # callbacks have run, so they can see whether the
                            # syscall was emulated or not.
                            syscall.emulated = False

                    else:
                        _debug('ignoring syscall-exit due to restart')

            # Run callbacks for signals and single stepping.
            elif signal_stop:
                # A single stepped tracee will signal-stop with `SIGTRAP` when
                # executing the next instruction, so we need to suppress that
                # signal.  Otherwise run callbacks and deliver signals as usual.
                if siginfo.signo == SIGTRAP and tracee._was_singlestepped:
                    cont_signal = 0

                else:
                    _debug('signal %d:%s' % (siginfo.signo, siginfo.signame))

                    retval = self._run_signal_callbacks(tracee)
                    if retval != None:
                        if retval == 0:
                            _debug('supressing signal %d:%s' % \
                                   (siginfo.signo, siginfo.signame))
                        else:
                            _debug('overriding signal %d:%s -> %d:%s' % \
                                   (siginfo.signo, siginfo.signame, retval,
                                    signal_names.get(retval, 'SIG???')))

                        cont_signal = retval

            # Ditto for group-stops.
            elif group_stop:
                _debug('group-stop')

                tracee.is_running = False
                self._run_callbacks('stop', tracee)

            # Handle births.
            elif event in PTRACE_EVENTS_FOLLOW:
                newpid = ptrace_geteventmsg(pid)

                # Even if the child is not the result of a `clone` syscall, we
                # may have saved clone flags if a previous `clone` failed.  In
                # that case we must remove the stale flags.
                if event != PTRACE_EVENT_CLONE:
                    clone_flags.pop(pid, None)

                # Record this tracee as the parent.
                parent_seen[newpid] = tracee

                # And finally create and start the new tracee if
                # `PTRACE_EVENT_STOP` was already seen (as mentioned above, I've
                # only seen this behavior from `vfork`).
                maybe_tracee(newpid)

            # Handle deaths.
            elif event == PTRACE_EVENT_EXIT:
                status = ptrace_geteventmsg(pid)
                tracee.is_running = False
                tracee.is_alive = False
                self._run_callbacks('death', tracee, status)

            # Should the tracee be single stepped?
            do_singlestep = self.singlestep or tracee.singlestep or \
                            tracee.singlesteps > 0
            # If so, we need a to know whether we're about to make a syscall or
            # not, and figuring that out probably requires reading the tracee's
            # registers and/or memory, so we should do it here, before we flush
            # the register, memory and siginfo caches.
            if do_singlestep:
                at_syscall = tracee.at_syscall

            # And now we can flush them.
            tracee._cacheflush()

            # Continue the tracee.
            try:
                if group_stop:
                    ptrace_listen(pid)

                elif sysemu:
                    # XXX: See comments about `ptrace_sysemu` above.
                    ptrace_sysemu(pid, cont_signal)

                elif tracee._do_detach:
                    _debug('detached <PID:%d>' % pid)
                    ptrace_detach(pid, cont_signal)
                    # Continue the tracee.
                    _tgkill(tracee.tgid, tracee.tid, SIGCONT)

                elif do_singlestep:
                    # For each tracee we record whether it was single stepped
                    # since any of the variables above may change before we
                    # observe SIGTRAP and thus cannot be relied on
                    tracee._was_singlestepped = True

                    # We decrement `singlesteps` here so changes made by later
                    # callbacks will not be affected.
                    if tracee.singlesteps > 0:
                        tracee.singlesteps -= 1

                    # If we're entering or exiting a syscall we must continue
                    # the tracee with `PTRACE_SYSCALL` in order to observe that.
                    if at_syscall or tracee.in_syscall:
                        ptrace_syscall(pid, cont_signal)
                    else:
                        ptrace_singlestep(pid, cont_signal)

                else:
                    tracee._was_singlestepped = False
                    ptrace_syscall(pid, cont_signal)

            except OSError as e:
                if e.errno == errno.ESRCH:
                    # This doesn't happen at the moment (kernel 4.5.0), but it
                    # may in the future.  See the BUGS section in the ptrace man
                    # page.
                    del self.tracees[pid]
                    self._run_callbacks('kill', tracee)
                else:
                    raise

        self._run_callbacks('finish')
Example #29
0
def ikos_analyzer(db_path, pp_path, opt):
    if settings.BUILD_MODE == 'Debug':
        log.warning('ikos was built in debug mode, the analysis might be slow')

    # Fix huge slow down when ikos-analyzer uses DROP TABLE on an existing db
    if os.path.isfile(db_path):
        os.remove(db_path)

    cmd = [settings.ikos_analyzer()]

    # analysis options
    cmd += [
        '-a=%s' % ','.join(opt.analyses),
        '-d=%s' % opt.domain,
        '-entry-points=%s' % ','.join(opt.entry_points),
        '-globals-init=%s' % opt.globals_init,
        '-proc=%s' % opt.procedural,
        '-j=%d' % opt.jobs,
        '-widening-strategy=%s' % opt.widening_strategy,
        '-widening-delay=%d' % opt.widening_delay,
        '-widening-period=%d' % opt.widening_period
    ]

    if opt.narrowing_strategy == 'auto':
        if opt.domain in domains_without_narrowing:
            cmd.append('-narrowing-strategy=meet')
        else:
            cmd.append('-narrowing-strategy=narrow')
    else:
        cmd.append('-narrowing-strategy=%s' % opt.narrowing_strategy)

    if opt.narrowing_iterations is not None:
        cmd.append('-narrowing-iterations=%d' % opt.narrowing_iterations)
    elif (opt.narrowing_strategy == 'auto'
          and opt.domain in domains_without_narrowing):
        cmd.append('-narrowing-iterations=%d' %
                   args.meet_iterations_if_no_narrowing)

    if opt.widening_delay_functions:
        cmd.append('-widening-delay-functions=%s' %
                   ','.join(opt.widening_delay_functions))

    if opt.no_init_globals:
        cmd.append('-no-init-globals=%s' % ','.join(opt.no_init_globals))
    if opt.no_liveness:
        cmd.append('-no-liveness')
    if opt.no_pointer:
        cmd.append('-no-pointer')
    if opt.no_widening_hints:
        cmd.append('-no-widening-hints')
    if opt.partitioning != 'no':
        cmd.append('-enable-partitioning-domain')
    if opt.no_fixpoint_cache:
        cmd.append('-no-fixpoint-cache')
    if opt.no_checks:
        cmd.append('-no-checks')
    if opt.hardware_addresses:
        cmd.append('-hardware-addresses=%s' % ','.join(opt.hardware_addresses))
    if opt.hardware_addresses_file:
        cmd.append('-hardware-addresses-file=%s' % opt.hardware_addresses_file)
    if opt.argc is not None:
        cmd.append('-argc=%d' % opt.argc)

    # import options
    cmd.append('-allow-dbg-mismatch')
    if opt.no_bc_verify:
        cmd.append('-no-verify')
    if opt.no_libc:
        cmd.append('-no-libc')
    if opt.no_libcpp:
        cmd.append('-no-libcpp')
    if opt.no_libikos:
        cmd.append('-no-libikos')

    # AR passes options
    if opt.no_type_check:
        cmd.append('-no-type-check')
    if opt.no_simplify_cfg:
        cmd.append('-no-simplify-cfg')
    if opt.no_simplify_upcast_comparison:
        cmd.append('-no-simplify-upcast-comparison')
    if 'gauge' in opt.domain:
        cmd.append('-add-loop-counters')
    if opt.partitioning == 'return':
        cmd.append('-add-partitioning-variables')

    # debug options
    cmd += [
        '-display-checks=%s' % opt.display_checks,
        '-display-inv=%s' % opt.display_inv
    ]

    if opt.display_ar:
        cmd.append('-display-ar')
    if opt.display_liveness:
        cmd.append('-display-liveness')
    if opt.display_function_pointer:
        cmd.append('-display-function-pointer')
    if opt.display_pointer:
        cmd.append('-display-pointer')
    if opt.display_fixpoint_parameters:
        cmd.append('-display-fixpoint-parameters')
    if opt.generate_dot:
        cmd += ['-generate-dot', '-generate-dot-dir', opt.generate_dot_dir]

    # add -name-values if necessary
    if (opt.display_checks in ('all', 'fail')
            or opt.display_inv in ('all', 'fail') or opt.display_liveness
            or opt.display_fixpoint_parameters or opt.display_function_pointer
            or opt.display_pointer or opt.display_raw_checks):
        cmd.append('-name-values')

    # misc. options
    if opt.color == 'yes':
        cmd.append('-color=1')
    elif opt.color == 'no':
        cmd.append('-color=0')

    cmd.append('-log=%s' % opt.log_level)
    cmd.append('-progress=%s' % opt.progress)

    # input/output
    cmd += [pp_path, '-o', db_path]

    # set resource limit, if requested
    if opt.mem:
        import resource  # fails on Windows

        def set_limits():
            mem_bytes = opt.mem * 1024 * 1024
            resource.setrlimit(resource.RLIMIT_AS, [mem_bytes, mem_bytes])
    else:
        set_limits = None

    # called after timeout
    def kill(p):
        try:
            log.error('Timeout')
            p.send_signal(signal.SIGALRM)
        except OSError:
            pass

    log.info('Running ikos analyzer')
    log.debug('Running %s' % command_string(cmd))
    p = subprocess.Popen(cmd, preexec_fn=set_limits)
    timer = threading.Timer(opt.cpu, kill, [p])

    if opt.cpu:
        timer.start()

    try:
        if sys.platform.startswith('win'):
            return_status = p.wait()
        else:
            _, return_status = os.waitpid(p.pid, 0)
    finally:
        # kill the timer if the process has terminated already
        if timer.isAlive():
            timer.cancel()

    # special case for Windows, since it does not define WIFEXITED & co.
    if sys.platform.startswith('win'):
        if return_status != 0:
            raise AnalyzerError('a run-time error occurred', cmd,
                                return_status)
        else:
            return

    # if it did not terminate properly, propagate this error code
    if os.WIFEXITED(return_status) and os.WEXITSTATUS(return_status) != 0:
        exit_status = os.WEXITSTATUS(return_status)
        raise AnalyzerError('a run-time error occurred', cmd, exit_status)

    if os.WIFSIGNALED(return_status):
        signum = os.WTERMSIG(return_status)
        raise AnalyzerError('exited with signal %s' % signal_name(signum), cmd,
                            signum)

    if os.WIFSTOPPED(return_status):
        signum = os.WSTOPSIG(return_status)
        raise AnalyzerError('exited with signal %d' % signal_name(signum), cmd,
                            signum)
if pid == 0:
    os.execv(sys.argv[1], sys.argv[1:])
    print('execv didnt work', file=sys.stderr)
else:
    '''
    At the end of the loop we get an exception when the connection with the other side terminates.
    This is kinda ugly since strictly speaking this is not an error, but oh well.
    '''
    bufsize = 1024
    buf = os.read(fd, bufsize).decode()
    over = False
    while len(buf) > 0 and not over:
        # print('got buf [{0}]'.format(buf))
        lines = buf.split('\n')
        buf = lines[-1]
        del lines[-1]
        for line in lines:
            line += '\n'
            print('got line [{0}]'.format(line))
        try:
            buf += os.read(fd, bufsize).decode()
        except OSError as e:
            over = True
    (pid, ret) = os.wait()
    if os.WIFEXITED(ret):
        print('proc exited and status was [{}]'.format(os.WEXITSTATUS(ret)))
    if os.WIFSTOPPED(ret):
        print('proc stopped and stopsig was [{}]'.format(os.WSTOPSIG(ret)))
    if os.WIFSIGNALED(ret):
        print('proc signaled and signal was [{}]'.format(os.WTERMSIG(ret)))