cageforge_backend_api/
execution.rs1use std::{
6 error::Error,
7 io::{Read, Write},
8 process::ExitStatus,
9};
10
11use cageforge_policy::PathResolutionContext;
12
13use crate::{BackendRequest, PreparedBackendRequest, SandboxBackend};
14
15pub trait DynSandbox: SandboxBackend + Send + Sync {
26 fn launch(
31 &self,
32 request: BackendRequest<'_>,
33 context: &PathResolutionContext,
34 ) -> Result<Box<dyn SandboxChild<Error = SandboxExecutionError> + Send>, SandboxExecutionError>;
35}
36
37#[derive(Debug, thiserror::Error)]
43#[non_exhaustive]
44pub enum SandboxExecutionError {
45 #[error("sandbox preparation failed: {source}")]
47 Prepare {
48 source: Box<dyn Error + Send + Sync>,
50 },
51 #[error("sandbox launch failed: {source}")]
53 Spawn {
54 source: Box<dyn Error + Send + Sync>,
56 },
57 #[error("sandbox status check failed: {source}")]
59 TryWait {
60 source: Box<dyn Error + Send + Sync>,
62 },
63 #[error("sandbox wait failed: {source}")]
65 Wait {
66 source: Box<dyn Error + Send + Sync>,
68 },
69 #[error("sandbox termination failed: {source}")]
71 Kill {
72 source: Box<dyn Error + Send + Sync>,
74 },
75}
76
77struct ErasedChild<C> {
78 child: C,
79}
80
81pub trait Sandbox: SandboxBackend {
84 type Child: SandboxChild<Error = Self::Error>;
86
87 type Error: std::error::Error + 'static;
89
90 fn prepare<'a>(
92 &self,
93 request: BackendRequest<'a>,
94 context: &PathResolutionContext,
95 ) -> Result<PreparedBackendRequest<'a, Self>, Self::Error>
96 where
97 Self: Sized;
98
99 fn spawn<'a>(
101 &self,
102 prepared: PreparedBackendRequest<'a, Self>,
103 ) -> Result<Self::Child, Self::Error>
104 where
105 Self: Sized;
106}
107
108pub trait SandboxChild {
110 fn id(&self) -> u32;
112
113 fn stdin(&mut self) -> Option<&mut dyn Write>;
115
116 fn stdout(&mut self) -> Option<&mut dyn Read>;
118
119 fn stderr(&mut self) -> Option<&mut dyn Read>;
121
122 type Error: std::error::Error + 'static;
124
125 fn try_wait(&mut self) -> Result<Option<ExitStatus>, Self::Error>;
127
128 fn wait(&mut self) -> Result<ExitStatus, Self::Error>;
130
131 fn kill(&mut self) -> Result<(), Self::Error>;
133}
134
135impl<B> DynSandbox for B
136where
137 B: Sandbox + Send + Sync,
138 B::Child: Send + 'static,
139 B::Error: Send + Sync,
140{
141 fn launch(
142 &self,
143 request: BackendRequest<'_>,
144 context: &PathResolutionContext,
145 ) -> Result<Box<dyn SandboxChild<Error = SandboxExecutionError> + Send>, SandboxExecutionError>
146 {
147 let prepared =
148 self.prepare(request, context)
149 .map_err(|source| SandboxExecutionError::Prepare {
150 source: Box::new(source),
151 })?;
152 let child = self
153 .spawn(prepared)
154 .map_err(|source| SandboxExecutionError::Spawn {
155 source: Box::new(source),
156 })?;
157 Ok(Box::new(ErasedChild { child }))
158 }
159}
160
161impl<C> SandboxChild for ErasedChild<C>
162where
163 C: SandboxChild,
164 C::Error: Send + Sync,
165{
166 type Error = SandboxExecutionError;
167
168 fn id(&self) -> u32 {
169 self.child.id()
170 }
171
172 fn stdin(&mut self) -> Option<&mut dyn Write> {
173 self.child.stdin()
174 }
175
176 fn stdout(&mut self) -> Option<&mut dyn Read> {
177 self.child.stdout()
178 }
179
180 fn stderr(&mut self) -> Option<&mut dyn Read> {
181 self.child.stderr()
182 }
183
184 fn try_wait(&mut self) -> Result<Option<ExitStatus>, Self::Error> {
185 self.child
186 .try_wait()
187 .map_err(|source| SandboxExecutionError::TryWait {
188 source: Box::new(source),
189 })
190 }
191
192 fn wait(&mut self) -> Result<ExitStatus, Self::Error> {
193 self.child
194 .wait()
195 .map_err(|source| SandboxExecutionError::Wait {
196 source: Box::new(source),
197 })
198 }
199
200 fn kill(&mut self) -> Result<(), Self::Error> {
201 self.child
202 .kill()
203 .map_err(|source| SandboxExecutionError::Kill {
204 source: Box::new(source),
205 })
206 }
207}