3"""Transactional three-source launcher for image-supervisor runtime proofs."""
5from __future__
import annotations
9from collections.abc
import Callable
10from dataclasses
import dataclass
11from pathlib
import Path
13DescriptorCloser = Callable[[int],
None]
14PYTHON_INTERPRETER =
"/usr/bin/python3"
17@dataclass(frozen=True)
19 """Optional launch authorities kept together at the Popen boundary."""
21 extra_descriptors: tuple[int, ...] = ()
22 environment: dict[str, str] |
None =
None
23 source_opener: Callable[[Path, int], int] = os.open
24 descriptor_closer: DescriptorCloser = os.close
27@dataclass(frozen=True)
29 """Retain a started runner and both errors for caller-owned resolution."""
31 process: subprocess.Popen[bytes] |
None
32 start_error: OSError | ValueError | subprocess.SubprocessError |
None
33 close_error: OSError |
None
36def close_owned_descriptors(descriptors: set[int], closer: DescriptorCloser = os.close) ->
None:
37 """Attempt each close once after irrevocably releasing numeric authority."""
38 first_error: OSError |
None =
None
39 for descriptor
in sorted(descriptors):
40 descriptors.remove(descriptor)
43 except OSError
as error:
44 if first_error
is None:
46 if first_error
is not None:
50def open_supervisor_sources(
54 opener: Callable[[Path, int], int] = os.open,
55 closer: DescriptorCloser = os.close,
56) -> tuple[int, int, int, set[int]]:
57 """Acquire all source descriptors or exhaustively release predecessors."""
58 owned: set[int] = set()
60 main_descriptor = opener(main_path, os.O_RDONLY | os.O_NOFOLLOW)
61 owned.add(main_descriptor)
62 process_descriptor = opener(process_path, os.O_RDONLY | os.O_NOFOLLOW)
63 owned.add(process_descriptor)
64 cases_descriptor = opener(cases_path, os.O_RDONLY | os.O_NOFOLLOW)
65 owned.add(cases_descriptor)
66 except OSError
as open_error:
68 close_owned_descriptors(owned, closer)
69 except OSError
as cleanup_error:
70 raise open_error
from cleanup_error
72 return main_descriptor, process_descriptor, cases_descriptor, owned
76 descriptors: tuple[int, ...],
77 arguments: tuple[str, ...],
78 environment: dict[str, str] |
None,
79) -> subprocess.Popen[bytes]:
80 """Spawn the fixed interpreter with three bound source descriptors."""
81 main_descriptor, process_descriptor, cases_descriptor = descriptors[:3]
82 return subprocess.Popen(
88 f
"/proc/self/fd/{main_descriptor}",
90 str(process_descriptor),
92 str(cases_descriptor),
96 start_new_session=
True,
97 stdout=subprocess.PIPE,
98 stderr=subprocess.PIPE,
104 paths: tuple[Path, Path, Path],
105 arguments: tuple[str, ...],
106 config: SupervisorStart,
108 """Open, spawn, and release every authenticated source exactly once."""
109 main_path, process_path, cases_path = paths
110 main, process_source, cases, owned = open_supervisor_sources(
114 config.source_opener,
115 config.descriptor_closer,
117 descriptors = main, process_source, cases, *config.extra_descriptors
118 process: subprocess.Popen[bytes] |
None =
None
119 start_error: OSError | ValueError | subprocess.SubprocessError |
None =
None
120 close_error: OSError |
None =
None
122 process = _spawn(descriptors, arguments, config.environment)
123 except (OSError, ValueError, subprocess.SubprocessError)
as error:
127 close_owned_descriptors(owned, config.descriptor_closer)
128 except OSError
as error:
130 return LaunchResult(process, start_error, close_error)