6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 1 | use std::{ |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 2 | env, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 3 | fmt::Write as _, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 4 | os::unix::{net::UnixStream, process::CommandExt}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 5 | process::{Command, ExitStatus}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 6 | sync::{ |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 7 | Arc, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 8 | atomic::{AtomicBool, AtomicI32, Ordering}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 9 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 10 | thread, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 11 | time::{Duration, Instant}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 12 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 13 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 14 | use crate::{ |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 15 | artifacts, client, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 16 | config::{Config, OverCap}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 17 | history, paths, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 18 | proto::{self, Acquire, Event, Reader, Request}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 19 | sched::Need, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 20 | sys, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 21 | units::{Amount, Bytes, Dur}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 22 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 23 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 24 | pub const EXIT_TIMEOUT: i32 = 124; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 25 | pub const EXIT_GAVE_UP: i32 = 75; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 26 | |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 27 | #[derive(Default)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 28 | pub struct Args { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 29 | pub size: Option<String>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 30 | pub ram: Option<Bytes>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 31 | pub disk: Option<Bytes>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 32 | pub cores: Option<Amount<u32>>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 33 | pub time: Option<Dur>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 34 | pub deadline: Option<Dur>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 35 | pub max_wait: Option<Dur>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 36 | pub session: Option<String>, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 37 | pub exact: bool, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 38 | pub cmd: Vec<String>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 39 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 40 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 41 | static PGID: AtomicI32 = AtomicI32::new(0); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 42 | #[cfg(target_os = "linux")] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 43 | static FORWARDED: AtomicI32 = AtomicI32::new(0); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 44 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 45 | extern "C" fn forward(sig: libc::c_int) { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 46 | #[cfg(target_os = "linux")] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 47 | FORWARDED.store(sig, Ordering::Relaxed); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 48 | let pgid = PGID.load(Ordering::Relaxed); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 49 | if pgid > 1 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 50 | unsafe { libc::kill(-pgid, sig) }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 51 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 52 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 53 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 54 | fn say(msg: &str) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 55 | eprintln!("rabt: {msg}"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 56 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 57 | |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 58 | pub fn need(cfg: &Config, a: &Args) -> Result<Need, String> { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 59 | let base = cfg.preset(a.size.as_deref().unwrap_or(&cfg.enforce.default_size))?; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 60 | Ok(Need { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 61 | ram: a.ram.unwrap_or(base.ram), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 62 | disk: a.disk.unwrap_or(base.disk), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 63 | cores: a.cores.unwrap_or(base.cores).resolve(sys::cores()), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 64 | time: a.time.unwrap_or(base.time).0, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 65 | }) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 66 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 67 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 68 | pub fn base(cfg: &Config) -> Result<Need, String> { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 69 | let p = cfg.preset(&cfg.enforce.default_size)?; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 70 | Ok(Need { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 71 | ram: p.ram, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 72 | disk: p.disk, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 73 | cores: p.cores.resolve(sys::cores()), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 74 | time: p.time.0, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 75 | }) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 76 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 77 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 78 | struct Grant { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 79 | stream: UnixStream, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 80 | lease: u64, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 81 | cores: u32, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 82 | ram: Bytes, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 83 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 84 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 85 | fn acquire(req: &Acquire, max_wait: Option<Dur>) -> Result<Grant, i32> { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 86 | let started = Instant::now(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 87 | // a daemon on its way out can accept us then hang up, so go round again |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 88 | for _ in 0..3 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 89 | let mut stream = client::connect_or_spawn().map_err(|e| { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 90 | say(&format!("can't reach the daemon: {e}")); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 91 | 1 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 92 | })?; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 93 | if proto::send(&mut stream, &Request::Acquire(req.clone())).is_err() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 94 | continue; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 95 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 96 | let _ = stream.set_read_timeout(Some(Duration::from_secs(1))); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 97 | let mut reader = Reader::new(&stream).map_err(|_| 1)?; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 98 | loop { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 99 | match reader.recv::<Event>() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 100 | Ok(Some(Event::Granted { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 101 | lease, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 102 | cores, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 103 | ram, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 104 | heavy, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 105 | })) => { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 106 | let bonus = cores.saturating_sub(req.need.cores); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 107 | say(&format!( |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 108 | "lease {lease} go, cores {cores}{}{}", |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 109 | if bonus > 0 { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 110 | format!(" ({bonus} spare)") |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 111 | } else { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 112 | String::new() |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 113 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 114 | if heavy { ", heavy" } else { "" } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 115 | )); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 116 | let _ = stream.set_read_timeout(None); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 117 | return Ok(Grant { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 118 | stream, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 119 | lease, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 120 | cores, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 121 | ram, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 122 | }); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 123 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 124 | Ok(Some(Event::Queued { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 125 | lease, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 126 | position, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 127 | reason, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 128 | })) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 129 | let why = reason.map(|r| format!(", {r}")).unwrap_or_default(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 130 | say(&format!("lease {lease} queued at #{position}{why}")); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 131 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 132 | Ok(Some(Event::Kill)) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 133 | say("killed while queued"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 134 | return Err(143); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 135 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 136 | Ok(Some(_)) => {} |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 137 | Err(e) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 138 | if matches!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 139 | e.kind(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 140 | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 141 | ) => |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 142 | { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 143 | if max_wait.is_some_and(|m| started.elapsed() > m.0) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 144 | say("gave up waiting"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 145 | return Err(EXIT_GAVE_UP); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 146 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 147 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 148 | Ok(None) | Err(_) => break, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 149 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 150 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 151 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 152 | say("daemon keeps hanging up"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 153 | Err(1) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 154 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 155 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 156 | fn command(cfg: &Config, a: &Args, grant: &Grant) -> Command { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 157 | let n = grant.cores.to_string(); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 158 | let mut cmd = Command::new(&a.cmd[0]); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 159 | cmd.args(&a.cmd[1..]) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 160 | .process_group(0) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 161 | .env("ROUNDABOUT_LEASE", grant.lease.to_string()) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 162 | .env("CARGO_BUILD_JOBS", &n) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 163 | .env("CMAKE_BUILD_PARALLEL_LEVEL", &n); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 164 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 165 | let make = env::var("MAKEFLAGS").unwrap_or_default(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 166 | if !make.contains("-j") { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 167 | cmd.env("MAKEFLAGS", format!("-j{n} {make}").trim_end()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 168 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 169 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 170 | if cfg.nix.inject { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 171 | let per = cfg.nix.cores_per_job.max(1); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 172 | let jobs = (grant.cores / per).max(1); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 173 | let cores = grant.cores.min(per); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 174 | let mut nix = env::var("NIX_CONFIG").unwrap_or_default(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 175 | let _ = write!(nix, "\ncores = {cores}\nmax-jobs = {jobs}\n"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 176 | cmd.env("NIX_CONFIG", nix); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 177 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 178 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 179 | cmd |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 180 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 181 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 182 | fn cpu_time() -> Duration { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 183 | let mut ru = std::mem::MaybeUninit::<libc::rusage>::uninit(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 184 | unsafe { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 185 | if libc::getrusage(libc::RUSAGE_CHILDREN, ru.as_mut_ptr()) != 0 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 186 | return Duration::ZERO; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 187 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 188 | let ru = ru.assume_init(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 189 | let tv = |t: libc::timeval| { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 190 | Duration::from_secs(t.tv_sec as u64) + Duration::from_micros(t.tv_usec as u64) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 191 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 192 | tv(ru.ru_utime) + tv(ru.ru_stime) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 193 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 194 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 195 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 196 | fn code(status: ExitStatus) -> i32 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 197 | use std::os::unix::process::ExitStatusExt; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 198 | status |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 199 | .code() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 200 | .or_else(|| status.signal().map(|s| 128 + s)) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 201 | .unwrap_or(1) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 202 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 203 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 204 | pub fn run(cfg: &Config, a: &Args) -> i32 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 205 | if a.cmd.is_empty() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 206 | say("nothing to run, put the command after --"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 207 | return 2; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 208 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 209 | if a.size.is_none() && a.ram.is_none() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 210 | let sizes: Vec<_> = cfg.sizes.keys().map(String::as_str).collect(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 211 | say(&format!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 212 | "how big is it? pass --size {} or --ram and friends. `rabt suggest -- <cmd>` has a guess", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 213 | sizes.join("|") |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 214 | )); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 215 | return 2; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 216 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 217 | let mut need = match need(cfg, a) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 218 | Ok(n) => n, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 219 | Err(e) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 220 | say(&e); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 221 | return 2; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 222 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 223 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 224 | let cwd = env::current_dir().unwrap_or_else(|_| "/".into()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 225 | let root = paths::project_root(&cwd); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 226 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 227 | let report = artifacts::check(cfg, Some(&root), false); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 228 | if report.over() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 229 | let msg = report.describe(cfg); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 230 | if cfg.artifacts.over_cap == OverCap::Block { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 231 | say(&format!("{msg}. clean up first, `rabt artifacts gc --cap`")); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 232 | return EXIT_GAVE_UP; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 233 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 234 | say(&format!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 235 | "{msg}. `rabt artifacts gc --cap` when you get a sec" |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 236 | )); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 237 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 238 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 239 | let req = Acquire { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 240 | need, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 241 | cwd, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 242 | cmd: a.cmd.clone(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 243 | session: a.session.clone(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 244 | deadline: a.deadline, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 245 | exact: a.exact, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 246 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 247 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 248 | let grant = match acquire(&req, a.max_wait.or(cfg.queue.max_wait)) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 249 | Ok(g) => g, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 250 | Err(code) => return code, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 251 | }; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 252 | need.ram = need.ram.max(grant.ram); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 253 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 254 | let command = command(cfg, a, &grant); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 255 | #[cfg(target_os = "linux")] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 256 | let (mut child, scope) = |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 257 | match crate::systemd::spawn(&command, grant.lease, grant.cores, grant.ram, &cfg.overrun) { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 258 | Ok(spawned) => spawned, |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 259 | Err(error) => { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 260 | say(&format!("couldn't start {}: {error}", a.cmd[0])); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 261 | return 127; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 262 | } |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 263 | }; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 264 | #[cfg(not(target_os = "linux"))] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 265 | let mut command = command; |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 266 | #[cfg(not(target_os = "linux"))] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 267 | let mut child = match command.spawn() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 268 | Ok(c) => c, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 269 | Err(e) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 270 | say(&format!("couldn't start {}: {e}", a.cmd[0])); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 271 | return 127; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 272 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 273 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 274 | let pgid = i32::try_from(child.id()).expect("pid fits in pid_t"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 275 | PGID.store(pgid, Ordering::Relaxed); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 276 | for sig in [libc::SIGINT, libc::SIGTERM, libc::SIGHUP] { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 277 | unsafe { libc::signal(sig, forward as *const () as libc::sighandler_t) }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 278 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 279 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 280 | let mut out = grant.stream.try_clone().ok(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 281 | if let Some(s) = &mut out { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 282 | let _ = proto::send(s, &Request::Started { pid: pgid }); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 283 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 284 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 285 | let killed = Arc::new(AtomicBool::new(false)); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 286 | if let Ok(mut reader) = Reader::new(&grant.stream) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 287 | let killed = killed.clone(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 288 | thread::spawn(move || { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 289 | while let Ok(Some(ev)) = reader.recv::<Event>() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 290 | if matches!(ev, Event::Kill) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 291 | killed.store(true, Ordering::Relaxed); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 292 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 293 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 294 | }); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 295 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 296 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 297 | let started = Instant::now(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 298 | let grace = need.time.mul_f64(cfg.overrun.time_grace); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 299 | let loud_ram = Bytes((need.ram.0 as f64 * cfg.overrun.ram_slack) as u64); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 300 | let mut peak = Bytes::ZERO; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 301 | let mut last_sample: Option<Instant> = None; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 302 | let mut term_sent: Option<Instant> = None; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 303 | let mut timed_out = false; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 304 | let mut warned_ram = false; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 305 | let mut warned_time = false; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 306 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 307 | let status = loop { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 308 | #[cfg(target_os = "linux")] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 309 | if let signal @ 1.. = FORWARDED.swap(0, Ordering::Relaxed) { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 310 | let _ = scope.signal(signal); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 311 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 312 | match child.try_wait() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 313 | Ok(Some(status)) => break status, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 314 | Ok(None) => {} |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 315 | Err(e) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 316 | say(&format!("lost track of the child: {e}")); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 317 | return 1; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 318 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 319 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 320 | let elapsed = started.elapsed(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 321 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 322 | if term_sent.is_none() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 323 | if killed.load(Ordering::Relaxed) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 324 | say("killed on request"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 325 | term_sent = Some(Instant::now()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 326 | } else if a.deadline.is_some_and(|d| elapsed > d.0) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 327 | say("deadline hit, killing it"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 328 | timed_out = true; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 329 | term_sent = Some(Instant::now()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 330 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 331 | if term_sent.is_some() { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 332 | #[cfg(target_os = "linux")] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 333 | let _ = scope.signal(libc::SIGTERM); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 334 | unsafe { libc::kill(-pgid, libc::SIGTERM) }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 335 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 336 | } else if term_sent.is_some_and(|t| t.elapsed() > Duration::from_secs(10)) { |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 337 | #[cfg(target_os = "linux")] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 338 | let _ = scope.signal(libc::SIGKILL); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 339 | unsafe { libc::kill(-pgid, libc::SIGKILL) }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 340 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 341 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 342 | if last_sample.is_none_or(|t| t.elapsed() >= Duration::from_secs(2)) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 343 | last_sample = Some(Instant::now()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 344 | let rss = sys::group_rss(pgid); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 345 | peak = peak.max(rss); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 346 | if let Some(s) = &mut out { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 347 | let _ = proto::send(s, &Request::Usage { rss }); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 348 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 349 | if !warned_ram && rss > loud_ram { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 350 | warned_ram = true; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 351 | say(&format!("using {rss} against a {} estimate", need.ram)); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 352 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 353 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 354 | if !warned_time && elapsed > grace { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 355 | warned_time = true; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 356 | say(&format!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 357 | "{} past its {} estimate and still going", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 358 | Dur(elapsed), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 359 | Dur(need.time) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 360 | )); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 361 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 362 | thread::sleep(Duration::from_millis(250)); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 363 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 364 | |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 365 | PGID.store(0, Ordering::Relaxed); |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 366 | #[cfg(target_os = "linux")] |
caac091f257a | atagen | 2026-10-04 11:41 UTC | 367 | drop(scope); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 368 | let wall = started.elapsed(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 369 | if let Some(s) = &mut out { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 370 | let _ = proto::send(s, &Request::Done); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 371 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 372 | history::record( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 373 | history::key(&root, &a.cmd), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 374 | peak, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 375 | wall, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 376 | cpu_time(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 377 | grant.cores, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 378 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 379 | artifacts::register(cfg, &root, true); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 380 | if peak > loud_ram || wall > grace { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 381 | say(&format!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 382 | "took {} and peaked at {}, asked for {} and {}. ask for more next time", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 383 | Dur(wall), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 384 | peak, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 385 | Dur(need.time), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 386 | need.ram |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 387 | )); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 388 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 389 | if timed_out { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 390 | EXIT_TIMEOUT |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 391 | } else { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 392 | code(status) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 393 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 394 | } |