()
| 165 | |
| 166 | #[tokio::main] |
| 167 | async fn main() -> Result<()> { |
| 168 | let args = Args::parse(); |
| 169 | if args.internal_run_vm { |
| 170 | // We intentionally defer procguard arming until `run_vm()` so |
| 171 | // that the only arm is the one that knows how to clean up |
| 172 | // gvproxy. Racing two watchers against the same parent-death |
| 173 | // event causes the bare arm's `exit(1)` to win, skipping the |
| 174 | // gvproxy cleanup and leaking the helper. The risk window |
| 175 | // before `run_vm` arms procguard is ~a few syscalls long |
| 176 | // (`build_vm_launch_config`, `configured_runtime_dir`), which |
| 177 | // is negligible next to the parent gRPC server's uptime. |
| 178 | maybe_reexec_internal_vm_with_runtime_env()?; |
| 179 | let config = build_vm_launch_config(&args).map_err(|err| miette::miette!("{err}"))?; |
| 180 | run_vm(&config).map_err(|err| miette::miette!("{err}"))?; |
| 181 | return Ok(()); |
| 182 | } |
| 183 | |
| 184 | tracing_subscriber::fmt() |
| 185 | .with_env_filter( |
| 186 | EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&args.log_level)), |
| 187 | ) |
| 188 | .init(); |
| 189 | |
| 190 | let listen_mode = compute_driver_listen_mode(&args).map_err(|err| miette::miette!("{err}"))?; |
| 191 | |
| 192 | // Arm procguard so that if the gateway is killed (SIGKILL or crash) |
| 193 | // we also die. Without this the driver is reparented to init and |
| 194 | // keeps its per-sandbox VM launchers alive forever. Launchers have |
| 195 | // their own procguards (armed in `run_vm`) which cascade cleanup of |
| 196 | // gvproxy and the libkrun worker the moment this driver exits. |
| 197 | if let Err(err) = procguard::die_with_parent() { |
| 198 | tracing::warn!( |
| 199 | error = %err, |
| 200 | "procguard arm failed; gateway crashes may orphan this driver" |
| 201 | ); |
| 202 | } |
| 203 | |
| 204 | let driver = VmDriver::new(VmDriverConfig { |
| 205 | openshell_endpoint: args |
| 206 | .openshell_endpoint |
| 207 | .ok_or_else(|| miette::miette!("OPENSHELL_GRPC_ENDPOINT is required"))?, |
| 208 | state_dir: args.state_dir.clone(), |
| 209 | launcher_bin: None, |
| 210 | default_image: args.default_image.clone(), |
| 211 | bootstrap_image: args.bootstrap_image.clone(), |
| 212 | log_level: args.log_level.clone(), |
| 213 | krun_log_level: args.krun_log_level, |
| 214 | vcpus: args.vcpus, |
| 215 | mem_mib: args.mem_mib, |
| 216 | overlay_disk_mib: args.overlay_disk_mib, |
| 217 | guest_tls_ca: args.guest_tls_ca.clone(), |
| 218 | guest_tls_cert: args.guest_tls_cert.clone(), |
| 219 | guest_tls_key: args.guest_tls_key.clone(), |
| 220 | gpu_enabled: args.gpu, |
| 221 | gpu_mem_mib: args.gpu_mem_mib, |
| 222 | gpu_vcpus: args.gpu_vcpus, |
| 223 | sandbox_uid: args.sandbox_uid, |
| 224 | sandbox_gid: args.sandbox_gid, |
nothing calls this directly
no test coverage detected