diff --git a/osbuild-run b/osbuild-run index da8245c2..c3e0fe10 100755 --- a/osbuild-run +++ b/osbuild-run @@ -1,11 +1,24 @@ #!/usr/bin/python3 +import array +import json import shutil import os +import socket import subprocess import sys +# copied from remoteloop.py +def load_fds(sock, msglen): + fds = array.array("i") # Array of ints + msg, ancdata, _, addr = sock.recvmsg(msglen, socket.CMSG_LEN(253 * fds.itemsize)) + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if (cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS): + # Append data, ignoring any truncated integers at the end. + fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + return json.loads(msg), list(fds), addr + def ldconfig(): # ld.so.conf must exist, or `ldconfig` throws a warning subprocess.run(["touch", "/etc/ld.so.conf"], check=True) @@ -72,7 +85,22 @@ def nsswitch(): pass +def setup_stdio(): + with socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) as sock: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_PASSCRED, 1) + sock.connect("/run/osbuild/api/osbuild") + req = {'method': 'setup-stdio'} + sock.send(json.dumps(req).encode('utf-8')) + msg, fds, _ = load_fds(sock, 1024) + for io in ['stdin', 'stdout', 'stderr']: + target = getattr(sys, io) + source = fds[msg[io]] + os.dup2(source, target.fileno()) + os.close(source) + + if __name__ == "__main__": + setup_stdio() ldconfig() sysusers() update_ca_trust() diff --git a/osbuild/pipeline.py b/osbuild/pipeline.py index 60b182e9..4cfe8209 100644 --- a/osbuild/pipeline.py +++ b/osbuild/pipeline.py @@ -7,6 +7,7 @@ import os import subprocess import tempfile +from .api import API from . import buildroot from . import objectstore from . import remoteloop @@ -72,17 +73,16 @@ class Stage: } path = "/run/osbuild/lib" - r = build_root.run( - [f"{path}/osbuild-run", f"{path}/stages/{self.name}"], - binds=[f"{tree}:/run/osbuild/tree"], - readonly_binds=[f"{libdir}:{path}"] if libdir else [f"/usr/lib/osbuild:{path}"], - encoding="utf-8", - input=json.dumps(args), - stdout=None if interactive else subprocess.PIPE, - stderr=subprocess.STDOUT - ) - if check and r.returncode != 0: - raise StageFailed(self.name, r.returncode, r.stdout) + with build_root.bound_socket("osbuild") as osbuild_sock, \ + API(osbuild_sock, args, interactive) as api: + r = build_root.run( + [f"{path}/osbuild-run", f"{path}/stages/{self.name}"], + binds=[f"{tree}:/run/osbuild/tree"], + readonly_binds=[f"{libdir}:{path}"] if libdir else [f"/usr/lib/osbuild:{path}"], + stdin=subprocess.DEVNULL, + ) + if check and r.returncode != 0: + raise StageFailed(self.name, r.returncode, api.output) return r.returncode == 0 @@ -136,18 +136,18 @@ class Assembler: # buildroot we should remove this because it includes code from the host in the buildroot thus # violating our effort of reproducibility. ro_binds += [f"/usr/lib/osbuild:{path}", f"{osbuild_module_path}:{path}/assemblers/osbuild"] - with build_root.bound_socket("remoteloop") as sock, \ - remoteloop.LoopServer(sock): + with build_root.bound_socket("remoteloop") as loop_sock, \ + build_root.bound_socket("osbuild") as osbuild_sock, \ + remoteloop.LoopServer(loop_sock), \ + API(osbuild_sock, args, interactive) as api: r = build_root.run( [f"{path}/osbuild-run", f"{path}/assemblers/{self.name}"], binds=binds, readonly_binds=ro_binds, - encoding="utf-8", - input=json.dumps(args), - stdout=None if interactive else subprocess.PIPE, - stderr=subprocess.STDOUT) + stdin=subprocess.DEVNULL, + ) if check and r.returncode != 0: - raise AssemblerFailed(self.name, r.returncode, r.stdout) + raise AssemblerFailed(self.name, r.returncode, api.output) return r.returncode == 0