def become_tty_fg(self,child=True): os.setpgrp() hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN) tty = os.open('/dev/tty', os.O_RDWR) os.tcsetpgrp(tty, os.getpgrp()) if child: signal.signal(signal.SIGTTOU, hdlr)
def _give_terminal_to(pgid): st = _shell_tty() if st is not None and os.isatty(st): oldmask = signal.pthread_sigmask(signal.SIG_BLOCK, _block_when_giving) os.tcsetpgrp(st, pgid) signal.pthread_sigmask(signal.SIG_SETMASK, oldmask)
def run_and_kill_children_on_exit(fn: "typing.Callable[[], typing.Any]"): error = False try: opgrp = os.getpgrp() if opgrp != os.getpid(): # Create new process group and become its leader os.setpgrp() # Preserve whether our process group is the terminal leader with suppress_sigttou(): tty = None try: tty = os.open('/dev/tty', os.O_RDWR) except OSError: pass if tty is not None and os.tcgetpgrp(tty) == opgrp: os.tcsetpgrp(tty, os.getpgrp()) os.close(tty) fn() except KeyboardInterrupt: error = True sys.exit("Exiting due to Ctrl+C") except subprocess.CalledProcessError as err: error = True extra_msg = (". Working directory was ", err.cwd) if hasattr(err, "cwd") else () if err.stderr is not None: extra_msg += ("\nStandard error was:\n", err.stderr.decode("utf-8")) fatal_error("Command ", "`" + commandline_to_str(err.cmd) + "` failed with non-zero exit code ", err.returncode, *extra_msg, fatal_when_pretending=True, sep="", exit_code=err.returncode) finally: if error: signal.signal(signal.SIGTERM, signal.SIG_IGN) os.killpg(0, signal.SIGTERM) # Tell all child processes to exit
async def _run(): self.fg_proc = proc = await astart_command(cmd, stdin=None, stdout=None, stderr=None, **kw) await proc.communicate() self.fg_proc = None # One of the main use cases for this function is to run interactive # bash in a subshell. Interactive bash of course creates a process # group for itself and sets it as the foreground process group for # the controlling terminal. Usually on exit, our process group # becomes the foreground process group again but when the subshell # is killed for some reason it does not. This causes the tcsetattr # that screen.start() does to either cause SIGTTOU to be sent, or # if that is ignored (either because there is no shell around to do # job control things or we are ignoring it) fail with EIO. So we # force our process group back into the foreground with this # call. That's not quite enough though because tcsetpgrp *also* # causes SIGTTOU to be sent to a background process that calls it, # but fortunately if we ignore that (done in start_urwid below), # the call still succeeds. # # I would now like a drink. os.tcsetpgrp(0, os.getpgrp()) screen.start() if after_hook is not None: after_hook()
def set_foreground(pgid): """ Use to be called by a the a shell to give the foreground to any pgid, including himself. """ with open("/dev/tty") as control_term: os.tcsetpgrp(control_term.fileno(), pgid)
def give_terminal_to(pgid): if pgid is None: return False oldmask = _pthread_sigmask(signal.SIG_BLOCK, _block_when_giving) try: os.tcsetpgrp(FD_STDERR, pgid) return True except ProcessLookupError: # when the process finished before giving terminal to it, # see issue #2288 return False except OSError as e: if e.errno == 22: # [Errno 22] Invalid argument # there are cases that all the processes of pgid have # finished, then we don't need to do anything here, see # issue #2220 return False elif e.errno == 25: # [Errno 25] Inappropriate ioctl for device # There are also cases where we are not connected to a # real TTY, even though we may be run in interactive # mode. See issue #2267 for an example with emacs return False else: raise finally: _pthread_sigmask(signal.SIG_SETMASK, oldmask)
def become_tty_fg(): # make child process use the fg # http://stackoverflow.com/questions/15200700/how-do-i-set-the-terminal-foreground-process-group-for-a-process-im-running-und os.setpgrp() hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN) tty = os.open('/dev/tty', os.O_RDWR) os.tcsetpgrp(tty, os.getpgrp()) signal.signal(signal.SIGTTOU, hdlr)
def _give_terminal_to(pgid): # over-simplified version of: # give_terminal_to from bash 4.3 source, jobs.c, line 4030 # this will give the terminal to the process group pgid if _shell_tty is not None and os.isatty(_shell_tty): oldmask = signal.pthread_sigmask(signal.SIG_BLOCK, _block_when_giving) os.tcsetpgrp(_shell_tty, pgid) signal.pthread_sigmask(signal.SIG_SETMASK, oldmask)
def run_command_in_interactive_shell(command, shell): try: subprocess.call([shell, '-i', '-c', command]) finally: # The terminal was attached to the interactive shell we just # started, and left in limbo when that shell terminated. Retrieve # it for this process group, so that we can still print and recieve # keypresses. os.tcsetpgrp(0, os.getpgrp())
def restore_pgrp() -> None: old_handler = signal.getsignal(signal.SIGTTOU) signal.signal(signal.SIGTTOU, signal.SIG_IGN) try: # This will raise SIGTTOU if we're not in the # foreground process group, which we have to ignore os.tcsetpgrp(their_fd, os.getpgrp()) finally: signal.signal(signal.SIGTTOU, old_handler)
def _leave_foreground(self): """ This resets the foreground process group. :return: """ if self.is_foreground: os.tcsetpgrp(self.controlling_terminal, self.orig_fore_pg) os.close(self.controlling_terminal) self.is_foreground = False
def tcsetpgrp(space, fd, pgid): """ tcsetpgrp(fd, pgid) Set the process group associated with the terminal given by a fd. """ try: os.tcsetpgrp(fd, pgid) except OSError as e: raise wrap_oserror(space, e)
def _controlTerminal(self): try: # the child should control stdin -- if stdin is a tty # that can be controlled if sys.stdin.isatty(): os.tcsetpgrp(0, os.getpgrp()) except AttributeError: # stdin might not even have an isatty method pass
def run_command_full(command): if os.name == 'nt': p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) elif os.name == 'posix': # Run the command supporting .bashrc subprocess.call([os.getenv('SHELL'), '-i', '-c', command], stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Retrieve the terminal os.tcsetpgrp(0,os.getpgrp()) out, err = p.communicate() return out, err
def set_as_tty_foreground(): # Create a new process group just for us os.setpgrp() # Becoming terminal foreground sends SIGTTOU which normally kills the # process. Ignore SIGTTOU. signal.signal(signal.SIGTTOU, signal.SIG_IGN) # Prepare a terminal device tty = os.open('/dev/tty', os.O_RDWR) # Become terminal foreground os.tcsetpgrp(tty, os.getpgrp())
def become_fg_process(): """ Executed in the child process to assume control over the terminal """ os.setpgrp() ttou_handler = signal.signal(signal.SIGTTOU, signal.SIG_IGN) tty = os.open('/dev/tty', os.O_RDWR) os.tcsetpgrp(tty, os.getpgrp()) os.close(tty) signal.signal(signal.SIGTTOU, ttou_handler)
def become_fg_process_group(): # we are going to get SIGTTOU because terminal settings change, which would put us to sleep hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN) os.setpgrp( ) # create a new process group so the newly started process can get ctrl-c,z tty = os.open('/dev/tty', os.O_RDWR) os.tcsetpgrp(tty, os.getpgrp()) # make the process group foreground for the tty os.close(tty) signal.signal(signal.SIGTTOU, hdlr) # restore normal signal handling
def __init__(self, pid=None, jobs=None, leaderCurrent=None): if pid is None: pid = os.getpid() os.setpgid(os.getpid(), os.getpid()) os.tcsetpgrp(1, os.getpid()) self.pere = pid if jobs is None: jobs = Jobs() self.jobs = jobs self.leaderCurrent = leaderCurrent
def execWithRedirect(command, argv, stdin = 0, stdout = 1, stderr = 2, searchPath = 0, root = '/', newPgrp = 0, ignoreTermSigs = 0): stdin = getfd(stdin) if stdout == stderr: stdout = getfd(stdout) stderr = stdout else: stdout = getfd(stdout) stderr = getfd(stderr) childpid = os.fork() if (not childpid): if (root and root != '/'): os.chroot(root) os.chdir("/") if ignoreTermSigs: signal.signal(signal.SIGTSTP, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN) if type(stdin) == type("a"): stdin = os.open(stdin, os.O_RDONLY) if type(stdout) == type("a"): stdout = os.open(stdout, os.O_RDWR) if type(stderr) == type("a"): stderr = os.open(stderr, os.O_RDWR) if stdin != 0: os.dup2(stdin, 0) os.close(stdin) if stdout != 1: os.dup2(stdout, 1) if stdout != stderr: os.close(stdout) if stderr != 2: os.dup2(stderr, 2) os.close(stderr) if (searchPath): os.execvp(command, argv) else: os.execv(command, argv) sys.exit(1) if newPgrp: os.setpgid(childpid, childpid) oldPgrp = os.tcgetpgrp(0) os.tcsetpgrp(0, childpid) status = -1 try: (pid, status) = os.waitpid(childpid, 0) except OSError, (errno, msg): print __name__, "waitpid:", msg
def fg(num=0): global jobS, chld pid = os.fork() if pid == 0: os.tcsetpgrp(1, os.getpgid(os.getpid())) else: chld = pid jobS[num] = [chld, "running"] os.setpgid(pid, 0) os.kill(jobS[num][0], signal.SIGCONT) signal.pause()
def become_tty_fg(): """Become foreground tty. This is used before spawning the subprocess so key sequences are correctly passed down. We also use it to grab it back when sleeping. """ os.setpgrp() hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN) tty = os.open('/dev/tty', os.O_RDWR) os.tcsetpgrp(tty, os.getpgrp()) signal.signal(signal.SIGTTOU, hdlr) os.close(tty)
def preexec_fn(): # Block SIGTTOU generated by the tcsetpgrp call orig_mask = signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGTTOU]) # Put us in a new process group. os.setpgid(0, 0) # And put it in the foreground. fd = os.open("/dev/tty", os.O_RDONLY) os.tcsetpgrp(fd, os.getpgid(0)) os.close(fd) signal.pthread_sigmask(signal.SIG_SETMASK, orig_mask)
def close(self): """ Reassert control of tty. Closing stdin, stderr, and and stdout will get rid of the last pointer to the slave fd of the pseudo tty, which should cause the logging process to stop. We wait for it to die before continuing """ if not self.logging: return self.closed = True # restore old terminal settings before quitting if self.oldStdin != 0: os.dup2(self.oldStdin, 0) os.dup2(self.oldStdout, 1) os.dup2(self.oldStderr, 2) if self.oldTermios is not None: termios.tcsetattr(0, termios.TCSADRAIN, self.oldTermios) if self.oldStdin != 0: os.close(self.oldStdin) os.close(self.oldStdout) os.close(self.oldStderr) try: # control stdin -- if stdin is a tty # that can be controlled if sys.stdin.isatty() and self.restoreTerminalControl: os.tcsetpgrp(0, os.getpgrp()) except AttributeError: # stdin might not even have an isatty method pass # Wait for child logging process to die. Send successively ruder # signals if it does not do so within a reasonable time. The primary # reason that it would not die immediately is that a process has forked # while holding the TTY file descriptor, and thus the logger is still # polling it for output. signals = [signal.SIGTERM, signal.SIGKILL] while signals: start = time.time() while time.time() - start < 10: pid, status = os.waitpid(self.loggerPid, os.WNOHANG) if pid: break time.sleep(0.1) else: # Child process did not die. signum = signals.pop(0) os.kill(self.loggerPid, signum) continue break else: # Last signal was a KILL, so wait indefinitely. os.waitpid(self.loggerPid, 0)
def fg(self, numero_job): if numero_job == []: fils = [job.pid for job in self.jobs.jobs] pidfils = fils[-1] pgid = os.getpgid(pidfils) os.tcsetpgrp(1, pgid) fgpid = pgid os.kill(-pgid, signal.SIGCONT) pid, status = os.waitpid(pidfils, os.WUNTRACED) if os.WIFSTOPPED(status): print("Fg stopped") elif os.WTERMSIG(status): self.jobs.removebyPId(pid) print("Fg Process killed") else: self.jobs.removebyPId(pidfils) os.tcsetpgrp(1, self.pere) else: fils = [job.pid for job in self.jobs.jobs] pidfils = fils[numero_job[0] - 1] pgid = os.getpgid(pidfils) os.tcsetpgrp(1, pgid) fgpid = pgid os.kill(-pgid, signal.SIGCONT) pid, status = os.waitpid(pidfils, os.WUNTRACED) if os.WIFSTOPPED(status): print("Fg stopped") elif os.WTERMSIG(status): self.jobs.removebyPId(pid) print("Fg Process killed") else: self.jobs.removebyPId(pidfils) os.tcsetpgrp(1, self.pere)
def _safe_tcsetpgrp(fd, pgrp): """Set |pgrp| as the controller of the tty |fd|.""" try: curr_pgrp = os.tcgetpgrp(fd) except OSError as e: # This can come up when the fd is not connected to a terminal. if e.errno == errno.ENOTTY: return raise # We can change the owner only if currently own it. Otherwise we'll get # stopped by the kernel with SIGTTOU and that'll hit the whole group. if curr_pgrp == os.getpgrp(): os.tcsetpgrp(fd, pgrp)
def _new_tty_foreground_process_group(): try: os.setpgrp() except Exception as e: warning_message("Failed to call os.setpgrp()", e) raise e with suppress_sigttou(): try: with scoped_open('/dev/tty', os.O_RDWR, ignore_open_error=True) as tty: if tty is not None: os.tcsetpgrp(tty, os.getpgrp()) except Exception as e: warning_message("Failed to call os.tcsetpgrp()", e) raise e
def run_test_in_separate_process(file_descriptor_stdin, result_queue): """Change process group, run test and return result via a queue.""" orig_pgid = os.getpgrp() os.setpgrp() result_queue.put("pgrp") if not quiet: sys.stdin = os.fdopen(file_descriptor_stdin) result = test.run() result_queue.put(result) if not quiet: try: os.tcsetpgrp(file_descriptor_stdin, orig_pgid) except OSError as error: if error.args[0] == 25: pass
def _maybe_become_foreground(self): """ Become the foreground process if the user requested it and it is possible. Allows the "Parent" process (aka this one) to get SIGINT (Ctrl+C). Useful if running in your terminal or from python repl. :return: """ if self._should_become_foreground(): hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN) # ignore since would cause this process to stop self.controlling_terminal = os.open(os.ctermid(), os.O_RDWR) self.orig_fore_pg = os.tcgetpgrp(self.controlling_terminal) # sends SIGTTOU to this process os.tcsetpgrp(self.controlling_terminal, self.childpid) signal.signal(signal.SIGTTOU, hdlr) return True return False
def give_terminal_to(pgid): oldmask = signal.pthread_sigmask(signal.SIG_BLOCK, _block_when_giving) try: os.tcsetpgrp(FD_STDERR, pgid) return True except OSError as e: if e.errno == 22: # [Errno 22] Invalid argument # there are cases that all the processes of pgid have # finished, then we don't need to do anything here, see # issue #2220 return False else: raise finally: signal.pthread_sigmask(signal.SIG_SETMASK, oldmask)
def _preExec(self): # Set process group logging.debug("CHILD: setpgid(0, 0)") os.setpgid(0, 0) if self.jobCtrl.foreground: logging.debug("CHILD: tcsetpgrp(0, %d)", os.getpgrp()) os.tcsetpgrp(0, os.getpgrp()) # Restore signal to default values signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGTERM, signal.SIG_DFL) signal.signal(signal.SIGTTOU, signal.SIG_DFL) signal.signal(signal.SIGTTIN, signal.SIG_DFL) signal.signal(signal.SIGTSTP, signal.SIG_DFL) signal.signal(signal.SIGCHLD, signal.SIG_DFL) signal.signal(signal.SIGCONT, signal.SIG_DFL)
def become_tty_fg(): """Force a subprocess call to become the foreground process. A helper function for subprocess.call(preexec_fn=) that makes the called command to become the foreground process in the terminal, allowing the user to interact with that process. Control is returned to this script after the called process has terminated. """ #From: http://stackoverflow.com/questions/15200700/how-do-i-set-the-terminal-foreground-process-group-for-a-process-im-running-und os.setpgrp() hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN) tty = os.open('/dev/tty', os.O_RDWR) os.tcsetpgrp(tty, os.getpgrp()) signal.signal(signal.SIGTTOU, hdlr)
def give_terminal_to(pgid): signals = { signal.SIGTTOU, signal.SIGTTIN, signal.SIGTSTP, signal.SIGCHLD, } old_mask = signal.pthread_sigmask(signal.SIG_BLOCK, signals) try: os.tcsetpgrp(2, pgid) return True except ProcessLookupError: return False except OSError: return False finally: signal.pthread_sigmask(signal.SIG_SETMASK, old_mask)
def exec1(self, arguments): pid = os.fork() if pid == 0: os.setpgid(0, 0) os.tcsetpgrp(1, os.getpid()) signal.signal(signal.SIGTSTP, signal.SIG_DFL) self.redirect(arguments[0]) os.setpgid(pid, pid) os.tcsetpgrp(0, pid) self.jobs.addJobs( processus(pid=pid, state='Foreground', name=" ".join(arguments[0][1]))) self.jobs.setJobID() pid, status = os.waitpid(pid, os.WUNTRACED) if os.WIFSTOPPED(status): temp = [job for job in self.jobs.jobs if job.state == "Foreground"] if temp != []: temp[0].changeState("Suspended") print("Stoped by ctrl z") elif os.WTERMSIG(status): self.jobs.removebyPId(pid) print("Process killed") else: self.jobs.removebyPId(pid) print("Exited normally") os.tcsetpgrp(1, self.pere)
def signalHandler(self, signo, _frame): logging.debug("signalHandler: signo=%d", signo) if signo == signal.SIGINT or signo == signal.SIGTERM: self.job.kill() elif signo == signal.SIGCHLD: # Get status of child try: (pid, status) = os.waitpid( self.job.pid, os.WNOHANG + os.WCONTINUED + os.WUNTRACED) except OSError as ex: # Simulate success in case of error (very rare case...) logging.debug("waitpid: %s", str(ex)) pid = self.job.pid status = 512 logging.debug("waitpid: pid=%d status=%s", pid, status) self.job.updateStatus(status) if self.job.stopped: # If sub-process was associated with console, change it to us if os.tcgetpgrp(0) == self.job.pgid: logging.debug("tcsetpgrp(0, %d)", os.getpgrp()) os.tcsetpgrp(0, os.getpgrp()) # Propagate the stop to our process group logging.debug("killpg(0, SIGSTOP)") os.killpg(0, signal.SIGSTOP) elif signo == signal.SIGCONT: # If we are associated with console, change it to sub-process self.tcpgrp = os.tcgetpgrp(0) self.foreground = (self.tcpgrp == os.getpgrp()) logging.debug("tcpgrp=%d foreground=%d", self.tcpgrp, self.foreground) if self.foreground: logging.debug("tcsetpgrp(0, %d)", self.job.pgid) os.tcsetpgrp(0, self.job.pgid) # Propagate continue to sub-process logging.debug("killpg(%d, SIGCONT)", self.job.pgid) os.killpg(self.job.pgid, signal.SIGCONT)
def forkExecCapture(instr): childpid=0; sys.stdout.flush() sys.stdin.flush() sys.stderr.flush() prev=signal.signal(signal.SIGTTOU, signal.SIG_IGN) os.tcsetpgrp(sys.stdin.fileno(),os.getpid()) try: ( child_pid, fd ) = pty.fork() # OK #~ child_pid, fd = os.forkpty() # OK except OSError as e: print str(e) if child_pid == 0: #~ os.tcsetpgrp(fd, tcgrp) # note: fd=-1 for child! # note: in child process, every print is redirected to parent! # #childpid=os.getpid() if FDBG: print "In Child Process: PID# %s" % os.getpid() # no need for REP=gdb.execute("n",True,to_string=True) # here we capture everything anyways; just run gdb.execute(instr) time.sleep(0.5) print RET if FDBG: return "child returns" #else: return 0 else: #~ os.tcsetpgrp(fd, tcgrp) # Inappropriate ioctl for device os.tcsetpgrp(sys.stdin.fileno(),os.getpid()) if FDBG: print "In Parent Process: PID# %s" % os.getpid() # now must 'drain' the printout from child process #~ os.waitpid(os.getpid(), 0) # No child processes time.sleep(0.5) CHILDREPLY=os.read(fd, 10000) time.sleep(0.5) # wait for child to exit if FDBG: print CHILDREPLY if FDBG: return "parent returns" else: return CHILDREPLY
def signalHandler(self, signo, _frame): logging.debug("signalHandler: signo=%d", signo) if signo == signal.SIGINT or signo == signal.SIGTERM: self.job.kill() elif signo == signal.SIGCHLD: # Get status of child try: (pid, status) = os.waitpid(self.job.pid, os.WNOHANG + os.WCONTINUED + os.WUNTRACED) except OSError as ex: # Simulate success in case of error (very rare case...) logging.debug("waitpid: %s", str(ex)) pid = self.job.pid status = 512 logging.debug("waitpid: pid=%d status=%s", pid, status) self.job.updateStatus(status) if self.job.stopped: # If sub-process was associated with console, change it to us if os.tcgetpgrp(0) == self.job.pgid: logging.debug("tcsetpgrp(0, %d)", os.getpgrp()) os.tcsetpgrp(0, os.getpgrp()) # Propagate the stop to our process group logging.debug("killpg(0, SIGSTOP)") os.killpg(0, signal.SIGSTOP) elif signo == signal.SIGCONT: # If we are associated with console, change it to sub-process self.tcpgrp = os.tcgetpgrp(0) self.foreground = (self.tcpgrp == os.getpgrp()) logging.debug("tcpgrp=%d foreground=%d", self.tcpgrp, self.foreground) if self.foreground: logging.debug("tcsetpgrp(0, %d)", self.job.pgid) os.tcsetpgrp(0, self.job.pgid) # Propagate continue to sub-process logging.debug("killpg(%d, SIGCONT)", self.job.pgid) os.killpg(self.job.pgid, signal.SIGCONT)
sys.exit(1) if newPgrp: os.setpgid(childpid, childpid) oldPgrp = os.tcgetpgrp(0) os.tcsetpgrp(0, childpid) status = -1 try: (pid, status) = os.waitpid(childpid, 0) except OSError, (errno, msg): print __name__, "waitpid:", msg if newPgrp: os.tcsetpgrp(0, oldPgrp) return status ## Run an external program and capture standard out. # @param command The command to run. # @param argv A list of arguments. # @param stdin The file descriptor to read stdin from. # @param stderr The file descriptor to redirect stderr to. # @param root The directory to chroot to before running command. # @return The output of command from stdout. def execWithCapture(command, argv, stdin = 0, stderr = 2, root='/'): def chroot(): os.chroot(root) argv = list(argv)
def main(): # Setup logging setupLog() makeProg = os.environ.get("MAKE", "make") # Put in an environment variable the command line so we can find it os.environ["ALCHEMAKE_CMDLINE"] = " ".join([makeProg] + sys.argv[1:]) # If not on a terminal, do NOT use job control, simply execute make... if not os.isatty(0): logging.warning("Not using job control") process = subprocess.Popen([makeProg] + sys.argv[1:], shell=False) process.wait() sys.exit(process.returncode) return # Create our job control object jobCtrl = JobCtrl() # Try to exit silently in case of interrupts... signal.signal(signal.SIGINT, jobCtrl.signalHandler) signal.signal(signal.SIGTERM, jobCtrl.signalHandler) # Job control signal.signal(signal.SIGTTOU, signal.SIG_IGN) signal.signal(signal.SIGTTIN, signal.SIG_IGN) signal.signal(signal.SIGTSTP, signal.SIG_IGN) signal.signal(signal.SIGCHLD, jobCtrl.signalHandler) signal.signal(signal.SIGCONT, jobCtrl.signalHandler) # Only redirect stderr (redirecting stdout causes issues if a child process # wants to use the terminal, like ncurses) # Force locale to have english messages that we will try to detect env = os.environ env["LANG"] = "C" cmdline = makeProg for arg in sys.argv[1:]: cmdline += " " + arg jobCtrl.job.launch(cmdline, stderr=subprocess.PIPE, env=env) # Read from stderr redirected in a pipe # only catch top level makefile errors (sub-make files error will eventually # generate a top level error) reError1 = re.compile(r"make: \*\*\* No rule to make target .*") reError2 = re.compile(r"make: \*\*\* \[[^\[\]]*\] Error [0-9]+") errorDetected = False while True: try: # Empty line means EOF detected, so exit loop line = jobCtrl.job.process.stderr.readline() if len(line) == 0: logging.debug("EOF detected") break if not errorDetected: sys.stderr.write(line) # Check for make error if reError1.match(line) or reError2.match(line): logging.debug("error detected") errorDetected = True jobCtrl.job.kill() except IOError: # Will occur when interrupted during read, an EOF will be read next pass # Only print message once at the end if errorDetected: sys.stderr.write("\n\033[31mMAKE ERROR DETECTED\n\033[00m") # Wait for sub-process to terminate logging.debug("wait sub-process") jobCtrl.job.process.wait() # Restore stuff if jobCtrl.tcpgrp != os.tcgetpgrp(0): try: logging.debug("tcsetpgrp(0, %d)", jobCtrl.tcpgrp) os.tcsetpgrp(0, jobCtrl.tcpgrp) except OSError: # Seems to occurs when launched in background and initial foreground # process is not there anymore, just ignore pass # Exit with same result as sub-process logging.debug("exit(%d)", jobCtrl.job.process.returncode) sys.exit(jobCtrl.job.process.returncode)
def main(): global tmp_file, current_process, child_processes # https://stackoverflow.com/questions/25099895/ signal.signal(signal.SIGTTOU, signal.SIG_IGN) # Ensure that child processes are cleaned up signal.signal(signal.SIGINT, kill_signal_handler) signal.signal(signal.SIGTERM, kill_signal_handler) atexit.register(cleanup_resources) tag_to_paths = load_tags() parser = argparse.ArgumentParser( prog='run', description=cmd_description, usage='run [options] [targets] command', formatter_class=HelpFormatter ) parser.add_argument( '-p', '--parallel', help='run the command in parallel', action='store_true' ) parser.add_argument( '-e', '--exit-codes', help='display the exit codes', action='store_true' ) parser.add_argument( 'arguments', type=str, nargs=argparse.REMAINDER, metavar='[targets]', help='tags or paths to run the command for' ).completer = ChoicesCompleter(tag_to_paths.keys()) autocomplete(parser) parsed = parser.parse_args() # Separate the targets from the command index = 0 targets = OrderedDict() while index < len(parsed.arguments): target = parsed.arguments[index] if target in tag_to_paths: for path in sorted(tag_to_paths[target].keys()): targets[path] = target index += 1 elif target.startswith('@'): parser.error('unknown tag {}'.format(target)) elif os.path.isdir(target): path = expand_path(target) if path not in targets: targets[path] = None index += 1 else: break # beginning of the command # Join the command arguments into a string command_arguments = parsed.arguments[index:] if len(command_arguments) == 0: command = None elif len(command_arguments) == 1: command = ' '.join(command_arguments) else: command = ' '.join( "'{}'".format(arg) if ' ' in arg else arg for arg in command_arguments ) if not (targets and command): parser.error('too few arguments') if parsed.parallel: # Run the command in parallel for path, tag in targets.items(): tmp_file = tempfile.TemporaryFile(mode='w+t') current_process = subprocess.Popen( '{} -i -c "{}"'.format(shell, command), cwd=path, shell=True, stdout=tmp_file, stderr=tmp_file, preexec_fn=os.setsid, ) child_processes.append((path, tag, current_process, tmp_file)) for path, tag, current_process, tmp_file in child_processes: exit_code = current_process.wait() tail = ' ({}{}{})'.format(PINK, tag, CLEAR) if tag else ':' print('{}>>> {}{}{}{}'.format( YELLOW, CYAN, path, tail, CLEAR )) tmp_file.seek(0) lines = tmp_file.readlines() offset = 0 if len(lines) > 0 and contains_ctrl_error_msg(lines[0]): offset += 1 if len(lines) > 1 and contains_ctrl_error_msg(lines[1]): offset += 1 sys.stdout.write(''.join(lines[offset:])) tmp_file.close() if parsed.exit_codes: print('{}>>> {}exit code: {}{}'.format( YELLOW, RED, exit_code, CLEAR )) sys.stdout.write('\n') else: # Run the command sequentially full_command = [] for path, tag in targets.items(): tag_info = ' ({}{}{})'.format(PINK, tag, CLEAR) if tag else ':' if parsed.exit_codes: tail = 'printf "{}>>> {}exit code: $?{}\n\n"'.format( YELLOW, RED, CLEAR ) else: tail = 'printf "\n"' full_command.append( '(printf "{header}"; cd {path} && {cmd};{tail})'.format( header='{}>>> {}{}{}{}\n'.format( YELLOW, CYAN, path, CLEAR, tag_info ), path=path, cmd=command, tail=tail ) ) subprocess.call( [shell, '-i', '-c', '{}'.format(';'.join(full_command))], stderr=sys.stdout.fileno() ) # https://stackoverflow.com/questions/25099895/ os.tcsetpgrp(0, os.getpgrp()) sys.exit(0)
def execWithRedirect(command, argv, stdin = 0, stdout = 1, stderr = 2, searchPath = 0, root = '/', newPgrp = 0, ignoreTermSigs = 0, closeFds = False): stdin = getfd(stdin) if stdout == stderr: stdout = getfd(stdout) stderr = stdout else: stdout = getfd(stdout) stderr = getfd(stderr) # # XXX What is this good for? If we can't run a command we'll error # out below won't we? # # if not os.access (root + command, os.X_OK): # raise RuntimeError, command + " can not be run" childpid = os.fork() if (not childpid): if (root and root != '/'): os.chroot (root) os.chdir("/") if ignoreTermSigs: signal.signal(signal.SIGTSTP, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN) if closeFds: try: os.closerange(3, os.sysconf("SC_OPEN_MAX")) except: pass if type(stdin) == type("a"): stdin = os.open(stdin, os.O_RDONLY) if type(stdout) == type("a"): stdout = os.open(stdout, os.O_RDWR) if type(stderr) == type("a"): stderr = os.open(stderr, os.O_RDWR) if stdin != 0: os.dup2(stdin, 0) os.close(stdin) if stdout != 1: os.dup2(stdout, 1) if stdout != stderr: os.close(stdout) if stderr != 2: os.dup2(stderr, 2) os.close(stderr) if (searchPath): os.execvp(command, argv) else: os.execv(command, argv) sys.exit(1) if newPgrp: os.setpgid(childpid, childpid) oldPgrp = os.tcgetpgrp(0) os.tcsetpgrp(0, childpid) status = -1 try: (pid, status) = os.waitpid(childpid, 0) except OSError as e: print "%s waitpid: %s" % (__name__, e.strerror) if newPgrp: os.tcsetpgrp(0, oldPgrp) return status
def run(self, quiet=False): """Run all tests in the defined suite of modules.""" tests = self.tests() failedfast = False result_list = [] for test in tests: if quiet: hijacked_stdout = sys.stdout hijacked_stderr = sys.stderr sys.stdout = open(path.join(self.settings['engine_folder'], ".hitch", "test.out"), "ab", 0) sys.stderr = open(path.join(self.settings['engine_folder'], ".hitch", "test.err"), "ab", 0) def run_test_in_separate_process(file_descriptor_stdin, result_queue): """Change process group, run test and return result via a queue.""" orig_pgid = os.getpgrp() os.setpgrp() result_queue.put("pgrp") if not quiet: sys.stdin = os.fdopen(file_descriptor_stdin) result = test.run() result_queue.put(result) if not quiet: try: os.tcsetpgrp(file_descriptor_stdin, orig_pgid) except OSError as error: if error.args[0] == 25: pass if not quiet: try: orig_stdin_termios = termios.tcgetattr(sys.stdin.fileno()) except termios.error: orig_stdin_termios = None orig_stdin_fileno = sys.stdin.fileno() orig_pgid = os.getpgrp() file_descriptor_stdin = sys.stdin.fileno() result_queue = multiprocessing.Queue() # Start new process to run test in, to isolate it from future test runs test_process = multiprocessing.Process( target=run_test_in_separate_process, args=(file_descriptor_stdin, result_queue) ) test_timed_out = False test_process.start() # Ignore all exit signals but pass them on signal_pass_on_to_separate_process_group(test_process.pid) # Wait until PGRP is changed result_queue.get() # Make stdin go to the test process so that you can use ipython, etc. if not quiet: try: os.tcsetpgrp(file_descriptor_stdin, os.getpgid(test_process.pid)) except OSError as error: if error.args[0] == 25: pass # Wait until process has finished proc = psutil.Process(test_process.pid) test_timeout = self.settings.get("test_timeout", None) test_shutdown_timeout = self.settings.get("test_shutdown_timeout", 10) try: proc.wait(timeout=test_timeout) except psutil.TimeoutExpired: test_timed_out = True proc.send_signal(signal.SIGTERM) try: proc.wait(timeout=test_shutdown_timeout) except psutil.TimeoutExpired: for child in proc.get_children(recursive=True): child.send_signal(signal.SIGKILL) proc.send_signal(signal.SIGKILL) # Take back signal handling from test running code signals_trigger_exit() try: result = result_queue.get_nowait() except multiprocessing.queues.Empty: result = Result(test, True, 0.0) if test_timed_out: result.aborted = False result_list.append(result) if not quiet and orig_stdin_termios is not None: try: termios.tcsetattr(orig_stdin_fileno, termios.TCSANOW, orig_stdin_termios) except termios.error as err: # I/O error caused by another test stopping this one if err[0] == 5: pass if quiet: sys.stdout = hijacked_stdout sys.stderr = hijacked_stderr if quiet and result is not None: if result.failure: warn("X") else: warn(".") if result.aborted: warn("Aborted\n") sys.exit(1) if self.settings.get('failfast', False) and result.failure: failedfast = True break return Results(result_list, failedfast, self.settings.get('colorless', False))
def grab_control(self, pgid): os.tcsetpgrp(self.__fd, pgid)
def to_foreground(): os.tcsetpgrp(self._tty_fd.fileno(), pgid)
def return_tty(fd, pgrp_id): try: os.tcsetpgrp(fd, pgrp_id) except OSError as oserr: #fails on s390 and s390x where we don't need it pass
os.fstatvfs(fd) # 返回包含文件描述符fd的文件的文件系统的信息,想stavfs() os.fsync(fd) # 强制将文件描述符为fd的文件写入硬盘 os.ftruncate(fd,length) # 裁剪文件描述符fd对应的文件,所以它最大不能超过文件大小 os.isatty(fd) # 如果文件描述符fd是打开的,同时与tty(-like)设备相连,则返回True,否则False os.tcgetpgrp(fd) # 返回与终端fd(一个由os.open()返回的打开的文件描述符)关联的进程组 os.tcsetpgrp(fd, pg) # 设置与终端fd(一个由os.open()返回的打开的文件描述符)关联的进程组为pg。 os.ttyname(fd) # 返回一个字符串,它表示与文件描述符fd 关联的终端设备。如果fd 没有与终端设备关联,则引发一个异常 apue
def f(fd, pgrp): try: os.tcsetpgrp(fd, pgrp) except OSError: return 1 return 0
def shell_to_foreground(self): # The shell should never accidentally stop itself. signal.signal(signal.SIGTTIN, signal.SIG_IGN) signal.signal(signal.SIGTTOU, signal.SIG_IGN) if self._tty_fd is not None: os.tcsetpgrp(self._tty_fd.fileno(), os.getpgrp())