Example #1
0
    def Wait(self):
        # This is a list of async jobs
        while True:
            try:
                pid, status = posix.wait()
            except OSError as e:
                #log('wait() error: %s', e)
                if e.errno == errno.ECHILD:
                    return False  # nothing to wait for caller should stop
                else:
                    # We should never get here.  EINTR was handled by the 'posix'
                    # module.  The only other error is EINVAL, which doesn't apply to
                    # this call.
                    raise
            else:
                break  # no exception thrown, so no need to retry

        #log('WAIT got %s %s', pid, status)

        # TODO: Also handle WIFSTOPPED case?
        if posix.WIFSIGNALED(status):
            status = 128 + posix.WTERMSIG(status)

            # Print newline after Ctrl-C.
            if posix.WTERMSIG(status) == signal.SIGINT:
                print()

        elif posix.WIFEXITED(status):
            status = posix.WEXITSTATUS(status)
            #log('exit status: %s', status)

        # This could happen via coding error.  But this may legitimately happen
        # if a grandchild outlives the child (its parent).  Then it is reparented
        # under this process, so we might receive notification of its exit, even
        # though we didn't start it.  We can't have any knowledge of such
        # processes, so print a warning.
        if pid not in self.callbacks:
            ui.Stderr("osh: PID %d stopped, but osh didn't start it", pid)
            return True  # caller should keep waiting

        callback = self.callbacks.pop(pid)
        callback(pid, status)
        self.last_status = status  # for wait -n

        return True  # caller should keep waiting
Example #2
0
    def WaitForOne(self):
        # type: () -> bool
        """Wait until the next process returns (or maybe Ctrl-C).

    Returns:
      True if we got a notification, or False if there was nothing to wait for.

      In the interactive shell, we return True if we get a Ctrl-C, so the
      caller will try again.
    """
        # This is a list of async jobs
        try:
            # -1 makes it like wait(), which waits for any process.
            # NOTE: WUNTRACED is necessary to get stopped jobs.  What about
            # WCONTINUED?
            pid, status = posix.waitpid(-1, posix.WUNTRACED)
        except OSError as e:
            #log('wait() error: %s', e)
            if e.errno == errno_.ECHILD:
                return False  # nothing to wait for caller should stop
            else:
                # We should never get here.  EINTR was handled by the 'posix'
                # module.  The only other error is EINVAL, which doesn't apply to
                # this call.
                raise
        except KeyboardInterrupt:
            # NOTE: Another way to handle this is to disable SIGINT when a process is
            # running.  Not sure if there's any real difference.  bash and dash
            # handle SIGINT pretty differently.
            if self.exec_opts.interactive():
                # Caller should keep waiting.  If we run 'sleep 3' and hit Ctrl-C, both
                # processes will get SIGINT, but the shell has to wait again to get the
                # exit code.
                return True
            else:
                raise  # abort a batch script

        #log('WAIT got %s %s', pid, status)

        # All child processes are suppoed to be in this doc.  But this may
        # legitimately happen if a grandchild outlives the child (its parent).
        # Then it is reparented under this process, so we might receive
        # notification of its exit, even though we didn't start it.  We can't have
        # any knowledge of such processes, so print a warning.
        if pid not in self.job_state.child_procs:
            stderr_line("osh: PID %d stopped, but osh didn't start it", pid)
            return True  # caller should keep waiting

        proc = self.job_state.child_procs[pid]

        if posix.WIFSIGNALED(status):
            status = 128 + posix.WTERMSIG(status)

            # Print newline after Ctrl-C.
            if posix.WTERMSIG(status) == signal.SIGINT:
                print('')

            proc.WhenDone(pid, status)

        elif posix.WIFEXITED(status):
            status = posix.WEXITSTATUS(status)
            #log('exit status: %s', status)
            proc.WhenDone(pid, status)

        elif posix.WIFSTOPPED(status):
            #sig = posix.WSTOPSIG(status)

            # TODO: Do something nicer here.  Implement 'fg' command.
            # Show in jobs list.
            log('')
            log('[PID %d] Stopped', pid)
            self.job_state.NotifyStopped(
                pid)  # show in 'jobs' list, enable 'fg'
            proc.WhenStopped()

        self.last_status = status  # for wait -n

        return True  # caller should keep waiting