def stop_doppelgänger(pid_path="/tmp/menu.pid"): if os.path.isfile(pid_path): if os.path.isfile(pid_path): with open(pid_path, "r") as f: pid = int(f.read()) try: twin = Process(pid) twin.send_signal(signal.SIGTERM) print(f"our twin {twin.pid} was stopped", flush=True) twin.wait(timeout=5) except psutil.NoSuchProcess as e: pass except psutil.TimeoutExpired as e: print(f"Timeout expired; exiting.", flush=True) sys.exit(10) except OSError as e: # errno.ESRCH unimportant: sometimes the /tmp/menu.pid is stale if e.errno == errno.ESRCH: print(f"no such process which is ok.") else: print(f"Unexpected OSError {e}") except Exception as e: print(f"Unexpected exception {e}") this_pid = psutil.Process() with open(pid_path, "w") as f: f.write(str(this_pid.pid)) return this_pid.pid
def kill(self, p: psutil.Process): if sys.platform == "win32": import ctypes kernel = ctypes.windll.kernel32 kernel.FreeConsole() kernel.AttachConsole(p.pid) kernel.SetConsoleCtrlHandler(None, 1) kernel.GenerateConsoleCtrlEvent(0, 0) sys.exit(0) import signal p.send_signal(signal.SIGINT)
def kill_process( self, process: psutil.Process, signal: int, ) -> None: try: self.logger.info(msg='kill_process request', ) process.send_signal(sig=signal, ) except Exception as exception: self.logger.error(msg=str(exception), )
def doKillChild(child: psutil.Process, sig: int): Logging.info("Killing sub-sub process {} with signal {}".format(child.pid, sig)) child.send_signal(sig) try: retCode = child.wait(20) # type: ignore if (- retCode) == signal.SIGSEGV: # type: ignore # Crashed Logging.warning("Process {} CRASHED, please check CORE file!".format(child.pid)) elif (- retCode) == sig : # type: ignore Logging.info("Sub-sub process terminated with expected return code {}".format(sig)) else: Logging.warning("Process terminated, EXPECTING ret code {}, got {}".format(sig, -retCode)) # type: ignore return True # terminated successfully except psutil.TimeoutExpired as err: Logging.warning("Failed to kill sub-sub process {} with signal {}".format(child.pid, sig)) return False # did not terminate
def _kill(pid, timeout=1.5): try: process = Process(pid) except NoSuchProcess: return try: process.send_signal(SIGINT) sleep(timeout) except OSError: pass finally: try: process.send_signal(SIGKILL) except (OSError, NoSuchProcess): pass process.wait()
def test_sigint(self): ''' Setup test server sleep 0.01 for each request Start spider in separate python shell (untill sigin or max 200 requests) Wait 1 sec (~100 requests, in reality less because of process start-up time) Send SIGINT to the process Check it returned with 13 or 139 codes 139 code means segfault (yeah...o_O) But as I see from logs it segfaults after successfully processing the SIGINT and this is all I need from this test ''' #logging.error('step-0') # pylint: disable=no-member self.server.response['sleep'] = 0.01 # pylint: enable=no-member with temp_file() as path: with open(path, 'w') as out: # pylint: disable=no-member out.write(self.script_tpl % ('', self.server.get_url())) # pylint: enable=no-member ret_codes = [] for _ in range(10): #logging.error('step-1') proc = Popen('python %s' % path, shell=True) #logging.error('step-2') parent = Process(proc.pid) #logging.error('step-3') time.sleep(1) #logging.error('killing children') for child in parent.children(): #logging.error('CHILD: %s', child.pid) # Sending multiple SIGINTs # because in very rare cases the only # sigint signals is ignored :-/ # do not send too fast for _ in range(1): try: #logging.error('sending sigint') child.send_signal(SIGNAL_INT) except NoSuchProcess: break else: time.sleep(1) if platform.system() == 'Darwin': # On OSX the Popen(shell=True) spawns only # one process, no child #logging.error('Killing parent') #logging.error('PARENT: %s', parent.pid) # Sending multiple SIGINTs # because in very rare cases the only # sigint signals is ignored :-/ # do not send too fast for _ in range(1): try: #logging.error('sending sigint') parent.send_signal(SIGNAL_INT) except NoSuchProcess: break else: time.sleep(1) #logging.error('step-4') ret = None for _ in range(20): #print('before proc-poll-%d' % step) ret = proc.poll() if ret is not None: break time.sleep(0.1) else: #logging.error('CHILD PROCESS DID NOT RETURN') #raise Exception('Child process did not return') # try to clean processes try: for child in parent.children(): child.send_signal(signal.SIGTERM) except NoSuchProcess: pass time.sleep(0.5) try: parent.send_signal(signal.SIGTERM) except NoSuchProcess: pass #logging.error('step-5') # FIXME: find out the reasonf of segfault # the 130 signal means the program was terminated by ctrl-c #print('RET CODE: %s' % ret) ret_codes.append(ret) # Could fail in 10% (1 of 10) # pylint: disable=no-member self.assertTrue(sum(1 for x in ret_codes if x in (13, 130, 139)) >= 9)
class SpyProcess: """ Spying process to detect the currently open files and their current reading advancement. Notes ----- There is three threads at play here: - The main thread, which handles the display - The ticking thread which drives refreshing - The process watching thread, which waits for the process to be done Both the ticking and the process threads communicate their ticks to the main thread through a queue. This way the main thread can easily display an update every second and close instantly when the process is done. """ def __init__(self, args: Sequence[Text], period: float, output: Output, attach=Optional[int]): self.args = args self.attach = attach self.proc: Optional[Popen] = None self.files_cache = {} self.display_ticks = Queue() self.process_thread = Thread(target=self.watch_process) self.ticks_thread = Thread(target=self.generate_ticks, args=(period, ), daemon=True) self.progress = Progress(output) self.counters = {} def start(self): """ If a PID was supplied to attach in the CLI options then use it. Otherwise use the provided arguments to start the command. """ if self.attach is None: try: self.proc = Popen(self.args) except FileNotFoundError: stderr.write(f'Could not find command "{self.args[0]}"\n') exit(1) else: try: self.proc = Process(self.attach) except (AccessDenied, NoSuchProcess): stderr.write( f"Could not attach process {self.attach}. Does it exist? " f"Do you have the rights?\n") exit(1) def open_files(self) -> List[popenfile]: """ Returns the list of open files """ return self.proc.open_files() def list_files(self) -> Sequence[FileInfo]: """ Generates the FileInfo object of all interesting files. """ files_in_use = set() out = [] try: for f in self.open_files(): if f.mode != "r": continue files_in_use.add(f.path) if f.path not in self.files_cache: self.files_cache[f.path] = path.getsize(f.path) if hasattr(f, "position"): out.append( FileInfo( path=f.path, size=self.files_cache[f.path], position=f.position, )) except (AccessDenied, NoSuchProcess): pass for k in set(self.files_cache.keys()) - files_in_use: del self.files_cache[k] return out def print_progress(self): """ UI display thread, looping around until the thing is done """ stop = False try: while not stop: files = self.list_files() self.progress.update(files) stop = self.display_ticks.get() finally: self.progress.close() def generate_ticks(self, period: float): """ Ticks into the queue every "period" second Parameters ---------- period Number of seconds between two ticks """ while True: self.display_ticks.put(False) sleep(period) def start_display(self): """ Starts the threads that will tick the display """ self.ticks_thread.start() self.process_thread.start() self.print_progress() def watch_process(self): """ Waits until the process finishes and raises the tick """ self.return_code() self.display_ticks.put(True) def return_code(self) -> int: """ Waits for the process to finish and returns its return code """ return self.proc.wait() def send_signal(self, sig): """ Sends a signal to the child process Parameters ---------- sig Unix signal """ try: self.proc.send_signal(sig) except NoSuchProcess: pass