caac091f257a | atagen | 2026-10-04 11:41 UTC | 1 | use std::{ |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 2 | env, io, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 3 | os::fd::{AsRawFd, FromRawFd, OwnedFd}, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 4 | os::unix::process::CommandExt, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 5 | process::{Child, Command}, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 6 | time::Duration, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 7 | }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 8 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 9 | use futures_lite::StreamExt; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 10 | use zbus::{ |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 11 | blocking::{Connection, Proxy}, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 12 | proxy::SignalStream, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 13 | zvariant::{OwnedObjectPath, Value}, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 14 | }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 15 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 16 | use crate::{ |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 17 | config::{Overrun, RamOverrun}, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 18 | units::Bytes, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 19 | }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 20 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 21 | const EXEC: &str = "__roundabout_exec"; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 22 | const MANAGER: &str = "org.freedesktop.systemd1.Manager"; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 23 | const BUS_NAME: &str = "org.freedesktop.systemd1"; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 24 | const BUS_PATH: &str = "/org/freedesktop/systemd1"; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 25 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 26 | pub struct Scope { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 27 | connection: Connection, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 28 | name: String, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 29 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 30 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 31 | fn properties( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 32 | pid: u32, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 33 | cores: u32, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 34 | ram: Bytes, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 35 | overrun: &Overrun, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 36 | ) -> zbus::Result<Vec<(&'static str, Value<'static>)>> { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 37 | let max = ram.0 as f64 * overrun.ram_slack.max(1.0); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 38 | if !overrun.ram_slack.is_finite() || max >= u64::MAX as f64 { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 39 | return Err(zbus::Error::Failure("RAM limit must be finite".into())); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 40 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 41 | let mut properties = vec![ |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 42 | ("Description", Value::from("roundabout build")), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 43 | ("PIDs", Value::from(vec![pid])), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 44 | ("CollectMode", Value::from("inactive-or-failed")), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 45 | ("CPUAccounting", Value::from(true)), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 46 | ("MemoryAccounting", Value::from(true)), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 47 | ( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 48 | "CPUQuotaPerSecUSec", |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 49 | Value::from(u64::from(cores.max(1)) * 1_000_000), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 50 | ), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 51 | ("MemoryMax", Value::from((max as u64).max(1))), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 52 | ("OOMPolicy", Value::from("kill")), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 53 | ("TimeoutStopUSec", Value::from(10_000_000_u64)), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 54 | ]; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 55 | if overrun.ram == RamOverrun::Throttle { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 56 | properties.push(("MemoryHigh", Value::from(ram.0.max(1)))); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 57 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 58 | Ok(properties) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 59 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 60 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 61 | fn wait_job(mut signals: SignalStream<'_>, job: &OwnedObjectPath) -> zbus::Result<()> { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 62 | async_io::block_on(futures_lite::future::race( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 63 | async { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 64 | while let Some(signal) = signals.next().await { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 65 | let (_, path, _, result): (u32, OwnedObjectPath, String, String) = |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 66 | signal.body().deserialize()?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 67 | if path == *job { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 68 | return if result == "done" { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 69 | Ok(()) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 70 | } else { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 71 | Err(zbus::Error::Failure(format!("systemd job {result}"))) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 72 | }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 73 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 74 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 75 | Err(zbus::Error::Failure( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 76 | "systemd disconnected before the job finished".into(), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 77 | )) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 78 | }, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 79 | async { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 80 | async_io::Timer::after(Duration::from_secs(15)).await; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 81 | Err(zbus::Error::Failure("systemd job timed out".into())) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 82 | }, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 83 | )) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 84 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 85 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 86 | impl Scope { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 87 | fn manager(&self) -> zbus::Result<Proxy<'_>> { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 88 | Proxy::new(&self.connection, BUS_NAME, BUS_PATH, MANAGER) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 89 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 90 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 91 | fn start( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 92 | connection: Connection, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 93 | pid: u32, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 94 | lease: u64, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 95 | cores: u32, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 96 | ram: Bytes, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 97 | overrun: &Overrun, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 98 | ) -> zbus::Result<Self> { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 99 | let scope = Self { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 100 | connection, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 101 | name: format!("roundabout-{}-{lease}-{pid}.scope", std::process::id()), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 102 | }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 103 | let manager = scope.manager()?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 104 | let signals = async_io::block_on( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 105 | manager |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 106 | .inner() |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 107 | .receive_signal_with_args("JobRemoved", &[(2, &scope.name)]), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 108 | )?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 109 | let _: () = manager.call("Subscribe", &())?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 110 | let auxiliary: Vec<(&str, Vec<(&str, Value<'_>)>)> = Vec::new(); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 111 | let job: OwnedObjectPath = manager.call( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 112 | "StartTransientUnit", |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 113 | &( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 114 | &scope.name, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 115 | "fail", |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 116 | properties(pid, cores, ram, overrun)?, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 117 | auxiliary, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 118 | ), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 119 | )?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 120 | wait_job(signals, &job)?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 121 | drop(manager); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 122 | Ok(scope) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 123 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 124 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 125 | pub fn signal(&self, signal: i32) -> zbus::Result<()> { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 126 | self.manager()? |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 127 | .call("KillUnit", &(&self.name, "all", signal)) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 128 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 129 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 130 | fn stop(&self) -> zbus::Result<()> { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 131 | let manager = self.manager()?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 132 | let signals = async_io::block_on( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 133 | manager |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 134 | .inner() |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 135 | .receive_signal_with_args("JobRemoved", &[(2, &self.name)]), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 136 | )?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 137 | let job: OwnedObjectPath = manager.call("StopUnit", &(&self.name, "replace"))?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 138 | wait_job(signals, &job) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 139 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 140 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 141 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 142 | impl Drop for Scope { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 143 | fn drop(&mut self) { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 144 | let _ = self.stop(); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 145 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 146 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 147 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 148 | fn gate() -> io::Result<(OwnedFd, OwnedFd)> { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 149 | let mut descriptors = [-1; 2]; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 150 | if unsafe { libc::pipe2(descriptors.as_mut_ptr(), libc::O_CLOEXEC) } != 0 { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 151 | return Err(io::Error::last_os_error()); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 152 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 153 | Ok(unsafe { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 154 | ( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 155 | OwnedFd::from_raw_fd(descriptors[0]), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 156 | OwnedFd::from_raw_fd(descriptors[1]), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 157 | ) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 158 | }) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 159 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 160 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 161 | pub fn spawn( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 162 | command: &Command, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 163 | lease: u64, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 164 | cores: u32, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 165 | ram: Bytes, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 166 | overrun: &Overrun, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 167 | ) -> Result<(Child, Scope), String> { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 168 | let connection = |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 169 | Connection::session().map_err(|error| format!("can't connect to the user bus, {error}"))?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 170 | let (read, write) = gate().map_err(|error| error.to_string())?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 171 | let descriptor = read.as_raw_fd(); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 172 | let mut runner = Command::new(env::current_exe().map_err(|error| error.to_string())?); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 173 | runner |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 174 | .arg(EXEC) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 175 | .arg(descriptor.to_string()) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 176 | .arg(command.get_program()) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 177 | .args(command.get_args()) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 178 | .process_group(0); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 179 | for (name, value) in command.get_envs() { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 180 | if let Some(value) = value { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 181 | runner.env(name, value); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 182 | } else { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 183 | runner.env_remove(name); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 184 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 185 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 186 | let parent = unsafe { libc::getpid() }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 187 | unsafe { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 188 | runner.pre_exec(move || { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 189 | if libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) != 0 |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 190 | || libc::fcntl(descriptor, libc::F_SETFD, 0) != 0 |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 191 | { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 192 | return Err(io::Error::last_os_error()); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 193 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 194 | if libc::getppid() != parent { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 195 | return Err(io::Error::other("parent exited before launch")); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 196 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 197 | Ok(()) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 198 | }); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 199 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 200 | let mut child = runner.spawn().map_err(|error| error.to_string())?; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 201 | drop(read); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 202 | let result = Scope::start(connection, child.id(), lease, cores, ram, overrun) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 203 | .map_err(|error| format!("can't start the transient scope, {error}")) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 204 | .and_then(|scope| { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 205 | if unsafe { libc::write(write.as_raw_fd(), b"1".as_ptr().cast(), 1) } != 1 { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 206 | return Err(io::Error::last_os_error().to_string()); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 207 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 208 | Ok(scope) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 209 | }); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 210 | drop(write); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 211 | match result { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 212 | Ok(scope) => Ok((child, scope)), |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 213 | Err(error) => { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 214 | let _ = child.kill(); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 215 | let _ = child.wait(); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 216 | Err(error) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 217 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 218 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 219 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 220 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 221 | pub fn exec() -> Option<i32> { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 222 | let mut arguments = env::args_os().skip(1); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 223 | if arguments.next()?.as_encoded_bytes() != EXEC.as_bytes() { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 224 | return None; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 225 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 226 | let Some(descriptor) = arguments |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 227 | .next() |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 228 | .and_then(|arg| arg.to_str()?.parse::<i32>().ok()) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 229 | .filter(|fd| *fd >= 3) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 230 | else { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 231 | return Some(1); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 232 | }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 233 | let descriptor = unsafe { OwnedFd::from_raw_fd(descriptor) }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 234 | let mut byte = [0_u8]; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 235 | loop { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 236 | let count = unsafe { libc::read(descriptor.as_raw_fd(), byte.as_mut_ptr().cast(), 1) }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 237 | if count == 1 && byte[0] == b'1' { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 238 | break; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 239 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 240 | if count != -1 || io::Error::last_os_error().kind() != io::ErrorKind::Interrupted { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 241 | return Some(1); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 242 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 243 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 244 | drop(descriptor); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 245 | let Some(program) = arguments.next() else { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 246 | return Some(1); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 247 | }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 248 | let error = Command::new(&program).args(arguments).exec(); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 249 | eprintln!( |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 250 | "rabt: couldn't start {}: {error}", |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 251 | program.to_string_lossy() |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 252 | ); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 253 | Some(127) |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 254 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 255 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 256 | #[cfg(test)] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 257 | mod tests { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 258 | use super::*; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 259 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 260 | #[test] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 261 | fn limits_follow_the_grant_with_memory_headroom() { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 262 | let overrun = Overrun::default(); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 263 | let properties = properties(123, 8, Bytes::gib(4), &overrun).unwrap(); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 264 | assert!(properties.contains(&("PIDs", Value::from(vec![123_u32])))); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 265 | assert!(properties.contains(&("CPUQuotaPerSecUSec", Value::from(8_000_000_u64)))); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 266 | assert!(properties.contains(&("MemoryMax", Value::from(Bytes::gib(5).0)))); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 267 | assert!(!properties.iter().any(|(name, _)| *name == "MemoryHigh")); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 268 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 269 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 270 | #[test] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 271 | fn throttle_starts_at_the_granted_memory() { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 272 | let overrun = Overrun { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 273 | ram: RamOverrun::Throttle, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 274 | ..Overrun::default() |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 275 | }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 276 | let properties = properties(123, 2, Bytes::gib(2), &overrun).unwrap(); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 277 | assert!(properties.contains(&("MemoryHigh", Value::from(Bytes::gib(2).0)))); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 278 | assert!(properties.contains(&("MemoryMax", Value::from(Bytes::mib(2560).0)))); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 279 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 280 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 281 | #[test] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 282 | fn small_grants_never_become_unlimited() { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 283 | let overrun = Overrun { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 284 | ram_slack: 0.5, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 285 | ..Overrun::default() |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 286 | }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 287 | let properties = properties(123, 0, Bytes::ZERO, &overrun).unwrap(); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 288 | assert!(properties.contains(&("CPUQuotaPerSecUSec", Value::from(1_000_000_u64)))); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 289 | assert!(properties.contains(&("MemoryMax", Value::from(1_u64)))); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 290 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 291 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 292 | #[test] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 293 | fn nonfinite_limits_are_rejected() { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 294 | for ram_slack in [f64::INFINITY, f64::NEG_INFINITY, f64::NAN, f64::MAX] { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 295 | let overrun = Overrun { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 296 | ram_slack, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 297 | ..Overrun::default() |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 298 | }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 299 | assert!(properties(123, 1, Bytes::gib(2), &overrun).is_err()); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 300 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 301 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 302 | } |