diff --git a/profiles/ch-runner.nix b/profiles/ch-runner.nix index b82d700..cd481fa 100644 --- a/profiles/ch-runner.nix +++ b/profiles/ch-runner.nix @@ -355,6 +355,16 @@ in return args_mut + def alive_after(proc, timeout): + if proc.returncode is not None: + return False + try: + proc.wait(timeout) + except subprocess.TimeoutExpired: + return True + return False + + class Processes: def __init__(self, prefix, vm, check=True, **defaults): self.prefix = prefix @@ -392,6 +402,7 @@ in }, ) + @contextmanager def popen(self, *args, **kwargs): kwargs["pass_fds"] = kwargs.get("pass_fds", ()) kwargs["env"] = kwargs.get("env", self.make_env()) @@ -399,11 +410,20 @@ in kwargs["stdin"] = kwargs.get("stdin", subprocess.DEVNULL) kwargs["stdout"] = kwargs.get("stdout", subprocess.DEVNULL) kwargs["stderr"] = kwargs.get("stderr", subprocess.DEVNULL) - return subprocess.Popen( - args, - **kwargs, - ) + try: + proc = subprocess.Popen( + args, + **kwargs, + ) + yield proc + finally: + try: + proc.wait(0.125) + except subprocess.TimeoutExpired: + proc.terminate() + proc.wait() + @contextmanager def bwrap( self, *bwrap_args, @@ -447,7 +467,10 @@ in bwrap_args_sock, remote = socket.socketpair() remote.set_inheritable(True) bwrap_args_f = bwrap_args_sock.makefile("w") - with closing(bwrap_args_sock), closing(bwrap_args_f): + with ExitStack() as cleanup: + # cleanup.enter_context(closing(bwrap_args_sock)) + # cleanup.enter_context(closing(bwrap_args_f)) + def print_arg(*args): print(*args, file=bwrap_args_f, sep="\0", end="\0") @@ -491,15 +514,17 @@ in bwrap_args_f.flush() - with closing(remote): - proc = self.popen( + with ExitStack() as es: + es.enter_context(closing(remote)) + es.enter_context(closing(bwrap_args_sock)) + es.enter_context(closing(bwrap_args_f)) + proc = cleanup.enter_context(self.popen( "bwrap", "--args", str(remote.fileno()), *bwrap_args, **popen_kwargs, executable=BWRAP_PATH, pass_fds=(*pass_fds, remote.fileno()), - ) - - return proc + )) + yield proc @contextmanager def run_ch(self): @@ -520,37 +545,27 @@ in "fd=0", # f"fd={s.fileno()}" ] - p = self.bwrap( - *args, - bind=[self.prefix], - # Probably just need the path to vmlinux - ro_bind=("/nix/store",), # A give up - unshare_net=False, - shell=False, - stderr=None, - # pass_fds=(s.fileno(),) - ) - # s.close() - try: - p.wait(0.125) - needs_cleanup = False - except subprocess.TimeoutExpired: + needs_cleanup = False + with self.bwrap( + *args, + bind=[self.prefix], + # Probably just need the path to vmlinux + ro_bind=("/nix/store",), # A give up + unshare_net=False, + shell=False, + stderr=None, + # pass_fds=(s.fileno(),) + ) as proc: + # s.close() + assert alive_after(proc, 0.125) + if not os.path.exists(self.prefix + "/vmm.sock"): + raise RuntimeError( + f"{self.prefix}/vmm.sock should exist by now") needs_cleanup = True - if not os.path.exists(self.prefix + "/vmm.sock"): - raise RuntimeError( - f"{self.prefix}/vmm.sock should exist by now") - if p.returncode is not None: - raise RuntimeError("CH exited early") - yield p + if proc.returncode is not None: + raise RuntimeError("CH exited early") + yield proc finally: - try: - p.poll() - except: # noqa: E722 - pass - if p.returncode is None: - print("Terminating CH") - p.terminate() # CH handles SIG{INT,TERM}? - p.wait() unlink_paths = [ self.prefix + "/vmm.sock", self.prefix + "/vmm.sock.lock", @@ -621,13 +636,9 @@ in # "pass_fds": (2, s.fileno()), } - proc_ctx = self.popen(*args, **kwargs) - with proc_ctx as p: + with self.popen(*args, **kwargs) as p: try: - try: - p.wait(0.125) - except subprocess.TimeoutExpired: - pass + assert alive_after(p, 0.125) if p.returncode is not None: raise RuntimeError("virtiofsd exited too early") yield p, sock_path