atagen

roundabout

public
Star0
Watch0
9117440444eb609ecea8476b5a62b03917077591
1
ssh://brewery@pls.lobotomise.me:33/atagen/roundabout.git

Blame: src/run.rs

6f5b84fad3efatagen2026-09-27 03:59 UTC1
use std::{
6f5b84fad3efatagen2026-09-27 03:59 UTC2
    env,
6f5b84fad3efatagen2026-09-27 03:59 UTC3
    fmt::Write as _,
6f5b84fad3efatagen2026-09-27 03:59 UTC4
    os::unix::{net::UnixStream, process::CommandExt},
6f5b84fad3efatagen2026-09-27 03:59 UTC5
    process::{Command, ExitStatus},
6f5b84fad3efatagen2026-09-27 03:59 UTC6
    sync::{
6f5b84fad3efatagen2026-09-27 03:59 UTC7
        Arc,
6f5b84fad3efatagen2026-09-27 03:59 UTC8
        atomic::{AtomicBool, AtomicI32, Ordering},
6f5b84fad3efatagen2026-09-27 03:59 UTC9
    },
6f5b84fad3efatagen2026-09-27 03:59 UTC10
    thread,
6f5b84fad3efatagen2026-09-27 03:59 UTC11
    time::{Duration, Instant},
6f5b84fad3efatagen2026-09-27 03:59 UTC12
};
6f5b84fad3efatagen2026-09-27 03:59 UTC13
6f5b84fad3efatagen2026-09-27 03:59 UTC14
use crate::{
6f5b84fad3efatagen2026-09-27 03:59 UTC15
    artifacts, client,
caac091f257aatagen2026-10-04 11:41 UTC16
    config::{Config, OverCap},
6f5b84fad3efatagen2026-09-27 03:59 UTC17
    history, paths,
6f5b84fad3efatagen2026-09-27 03:59 UTC18
    proto::{self, Acquire, Event, Reader, Request},
6f5b84fad3efatagen2026-09-27 03:59 UTC19
    sched::Need,
6f5b84fad3efatagen2026-09-27 03:59 UTC20
    sys,
6f5b84fad3efatagen2026-09-27 03:59 UTC21
    units::{Amount, Bytes, Dur},
6f5b84fad3efatagen2026-09-27 03:59 UTC22
};
6f5b84fad3efatagen2026-09-27 03:59 UTC23
6f5b84fad3efatagen2026-09-27 03:59 UTC24
pub const EXIT_TIMEOUT: i32 = 124;
6f5b84fad3efatagen2026-09-27 03:59 UTC25
pub const EXIT_GAVE_UP: i32 = 75;
6f5b84fad3efatagen2026-09-27 03:59 UTC26
ecc1cf8df0f5atagen2026-09-27 13:36 UTC27
#[derive(Default)]
6f5b84fad3efatagen2026-09-27 03:59 UTC28
pub struct Args {
6f5b84fad3efatagen2026-09-27 03:59 UTC29
    pub size: Option<String>,
6f5b84fad3efatagen2026-09-27 03:59 UTC30
    pub ram: Option<Bytes>,
6f5b84fad3efatagen2026-09-27 03:59 UTC31
    pub disk: Option<Bytes>,
6f5b84fad3efatagen2026-09-27 03:59 UTC32
    pub cores: Option<Amount<u32>>,
6f5b84fad3efatagen2026-09-27 03:59 UTC33
    pub time: Option<Dur>,
6f5b84fad3efatagen2026-09-27 03:59 UTC34
    pub deadline: Option<Dur>,
6f5b84fad3efatagen2026-09-27 03:59 UTC35
    pub max_wait: Option<Dur>,
6f5b84fad3efatagen2026-09-27 03:59 UTC36
    pub session: Option<String>,
ecc1cf8df0f5atagen2026-09-27 13:36 UTC37
    pub exact: bool,
6f5b84fad3efatagen2026-09-27 03:59 UTC38
    pub cmd: Vec<String>,
6f5b84fad3efatagen2026-09-27 03:59 UTC39
}
6f5b84fad3efatagen2026-09-27 03:59 UTC40
6f5b84fad3efatagen2026-09-27 03:59 UTC41
static PGID: AtomicI32 = AtomicI32::new(0);
caac091f257aatagen2026-10-04 11:41 UTC42
#[cfg(target_os = "linux")]
caac091f257aatagen2026-10-04 11:41 UTC43
static FORWARDED: AtomicI32 = AtomicI32::new(0);
6f5b84fad3efatagen2026-09-27 03:59 UTC44
6f5b84fad3efatagen2026-09-27 03:59 UTC45
extern "C" fn forward(sig: libc::c_int) {
caac091f257aatagen2026-10-04 11:41 UTC46
    #[cfg(target_os = "linux")]
caac091f257aatagen2026-10-04 11:41 UTC47
    FORWARDED.store(sig, Ordering::Relaxed);
6f5b84fad3efatagen2026-09-27 03:59 UTC48
    let pgid = PGID.load(Ordering::Relaxed);
6f5b84fad3efatagen2026-09-27 03:59 UTC49
    if pgid > 1 {
6f5b84fad3efatagen2026-09-27 03:59 UTC50
        unsafe { libc::kill(-pgid, sig) };
6f5b84fad3efatagen2026-09-27 03:59 UTC51
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC52
}
6f5b84fad3efatagen2026-09-27 03:59 UTC53
6f5b84fad3efatagen2026-09-27 03:59 UTC54
fn say(msg: &str) {
fdad58f8133aatagen2026-10-25 23:14 UTC55
    eprintln!("roundabout: {msg}");
6f5b84fad3efatagen2026-09-27 03:59 UTC56
}
6f5b84fad3efatagen2026-09-27 03:59 UTC57
ecc1cf8df0f5atagen2026-09-27 13:36 UTC58
pub fn need(cfg: &Config, a: &Args) -> Result<Need, String> {
6f5b84fad3efatagen2026-09-27 03:59 UTC59
    let base = cfg.preset(a.size.as_deref().unwrap_or(&cfg.enforce.default_size))?;
6f5b84fad3efatagen2026-09-27 03:59 UTC60
    Ok(Need {
6f5b84fad3efatagen2026-09-27 03:59 UTC61
        ram: a.ram.unwrap_or(base.ram),
6f5b84fad3efatagen2026-09-27 03:59 UTC62
        disk: a.disk.unwrap_or(base.disk),
6f5b84fad3efatagen2026-09-27 03:59 UTC63
        cores: a.cores.unwrap_or(base.cores).resolve(sys::cores()),
6f5b84fad3efatagen2026-09-27 03:59 UTC64
        time: a.time.unwrap_or(base.time).0,
6f5b84fad3efatagen2026-09-27 03:59 UTC65
    })
6f5b84fad3efatagen2026-09-27 03:59 UTC66
}
6f5b84fad3efatagen2026-09-27 03:59 UTC67
6f5b84fad3efatagen2026-09-27 03:59 UTC68
pub fn base(cfg: &Config) -> Result<Need, String> {
6f5b84fad3efatagen2026-09-27 03:59 UTC69
    let p = cfg.preset(&cfg.enforce.default_size)?;
6f5b84fad3efatagen2026-09-27 03:59 UTC70
    Ok(Need {
6f5b84fad3efatagen2026-09-27 03:59 UTC71
        ram: p.ram,
6f5b84fad3efatagen2026-09-27 03:59 UTC72
        disk: p.disk,
6f5b84fad3efatagen2026-09-27 03:59 UTC73
        cores: p.cores.resolve(sys::cores()),
6f5b84fad3efatagen2026-09-27 03:59 UTC74
        time: p.time.0,
6f5b84fad3efatagen2026-09-27 03:59 UTC75
    })
6f5b84fad3efatagen2026-09-27 03:59 UTC76
}
6f5b84fad3efatagen2026-09-27 03:59 UTC77
6f5b84fad3efatagen2026-09-27 03:59 UTC78
struct Grant {
6f5b84fad3efatagen2026-09-27 03:59 UTC79
    stream: UnixStream,
6f5b84fad3efatagen2026-09-27 03:59 UTC80
    lease: u64,
6f5b84fad3efatagen2026-09-27 03:59 UTC81
    cores: u32,
ecc1cf8df0f5atagen2026-09-27 13:36 UTC82
    ram: Bytes,
6f5b84fad3efatagen2026-09-27 03:59 UTC83
}
6f5b84fad3efatagen2026-09-27 03:59 UTC84
6f5b84fad3efatagen2026-09-27 03:59 UTC85
fn acquire(req: &Acquire, max_wait: Option<Dur>) -> Result<Grant, i32> {
6f5b84fad3efatagen2026-09-27 03:59 UTC86
    let started = Instant::now();
6f5b84fad3efatagen2026-09-27 03:59 UTC87
    // a daemon on its way out can accept us then hang up, so go round again
6f5b84fad3efatagen2026-09-27 03:59 UTC88
    for _ in 0..3 {
6f5b84fad3efatagen2026-09-27 03:59 UTC89
        let mut stream = client::connect_or_spawn().map_err(|e| {
6f5b84fad3efatagen2026-09-27 03:59 UTC90
            say(&format!("can't reach the daemon: {e}"));
6f5b84fad3efatagen2026-09-27 03:59 UTC91
            1
6f5b84fad3efatagen2026-09-27 03:59 UTC92
        })?;
6f5b84fad3efatagen2026-09-27 03:59 UTC93
        if proto::send(&mut stream, &Request::Acquire(req.clone())).is_err() {
6f5b84fad3efatagen2026-09-27 03:59 UTC94
            continue;
6f5b84fad3efatagen2026-09-27 03:59 UTC95
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC96
        let _ = stream.set_read_timeout(Some(Duration::from_secs(1)));
6f5b84fad3efatagen2026-09-27 03:59 UTC97
        let mut reader = Reader::new(&stream).map_err(|_| 1)?;
6f5b84fad3efatagen2026-09-27 03:59 UTC98
        loop {
6f5b84fad3efatagen2026-09-27 03:59 UTC99
            match reader.recv::<Event>() {
6f5b84fad3efatagen2026-09-27 03:59 UTC100
                Ok(Some(Event::Granted {
6f5b84fad3efatagen2026-09-27 03:59 UTC101
                    lease,
6f5b84fad3efatagen2026-09-27 03:59 UTC102
                    cores,
ecc1cf8df0f5atagen2026-09-27 13:36 UTC103
                    ram,
6f5b84fad3efatagen2026-09-27 03:59 UTC104
                    heavy,
6f5b84fad3efatagen2026-09-27 03:59 UTC105
                })) => {
ecc1cf8df0f5atagen2026-09-27 13:36 UTC106
                    let bonus = cores.saturating_sub(req.need.cores);
6f5b84fad3efatagen2026-09-27 03:59 UTC107
                    say(&format!(
ecc1cf8df0f5atagen2026-09-27 13:36 UTC108
                        "lease {lease} go, cores {cores}{}{}",
ecc1cf8df0f5atagen2026-09-27 13:36 UTC109
                        if bonus > 0 {
ecc1cf8df0f5atagen2026-09-27 13:36 UTC110
                            format!(" ({bonus} spare)")
ecc1cf8df0f5atagen2026-09-27 13:36 UTC111
                        } else {
ecc1cf8df0f5atagen2026-09-27 13:36 UTC112
                            String::new()
ecc1cf8df0f5atagen2026-09-27 13:36 UTC113
                        },
6f5b84fad3efatagen2026-09-27 03:59 UTC114
                        if heavy { ", heavy" } else { "" }
6f5b84fad3efatagen2026-09-27 03:59 UTC115
                    ));
6f5b84fad3efatagen2026-09-27 03:59 UTC116
                    let _ = stream.set_read_timeout(None);
6f5b84fad3efatagen2026-09-27 03:59 UTC117
                    return Ok(Grant {
6f5b84fad3efatagen2026-09-27 03:59 UTC118
                        stream,
6f5b84fad3efatagen2026-09-27 03:59 UTC119
                        lease,
6f5b84fad3efatagen2026-09-27 03:59 UTC120
                        cores,
ecc1cf8df0f5atagen2026-09-27 13:36 UTC121
                        ram,
6f5b84fad3efatagen2026-09-27 03:59 UTC122
                    });
6f5b84fad3efatagen2026-09-27 03:59 UTC123
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC124
                Ok(Some(Event::Queued {
6f5b84fad3efatagen2026-09-27 03:59 UTC125
                    lease,
6f5b84fad3efatagen2026-09-27 03:59 UTC126
                    position,
6f5b84fad3efatagen2026-09-27 03:59 UTC127
                    reason,
6f5b84fad3efatagen2026-09-27 03:59 UTC128
                })) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC129
                    let why = reason.map(|r| format!(", {r}")).unwrap_or_default();
6f5b84fad3efatagen2026-09-27 03:59 UTC130
                    say(&format!("lease {lease} queued at #{position}{why}"));
6f5b84fad3efatagen2026-09-27 03:59 UTC131
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC132
                Ok(Some(Event::Kill)) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC133
                    say("killed while queued");
6f5b84fad3efatagen2026-09-27 03:59 UTC134
                    return Err(143);
6f5b84fad3efatagen2026-09-27 03:59 UTC135
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC136
                Ok(Some(_)) => {}
6f5b84fad3efatagen2026-09-27 03:59 UTC137
                Err(e)
6f5b84fad3efatagen2026-09-27 03:59 UTC138
                    if matches!(
6f5b84fad3efatagen2026-09-27 03:59 UTC139
                        e.kind(),
6f5b84fad3efatagen2026-09-27 03:59 UTC140
                        std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut
6f5b84fad3efatagen2026-09-27 03:59 UTC141
                    ) =>
6f5b84fad3efatagen2026-09-27 03:59 UTC142
                {
6f5b84fad3efatagen2026-09-27 03:59 UTC143
                    if max_wait.is_some_and(|m| started.elapsed() > m.0) {
6f5b84fad3efatagen2026-09-27 03:59 UTC144
                        say("gave up waiting");
6f5b84fad3efatagen2026-09-27 03:59 UTC145
                        return Err(EXIT_GAVE_UP);
6f5b84fad3efatagen2026-09-27 03:59 UTC146
                    }
6f5b84fad3efatagen2026-09-27 03:59 UTC147
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC148
                Ok(None) | Err(_) => break,
6f5b84fad3efatagen2026-09-27 03:59 UTC149
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC150
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC151
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC152
    say("daemon keeps hanging up");
6f5b84fad3efatagen2026-09-27 03:59 UTC153
    Err(1)
6f5b84fad3efatagen2026-09-27 03:59 UTC154
}
6f5b84fad3efatagen2026-09-27 03:59 UTC155
caac091f257aatagen2026-10-04 11:41 UTC156
fn command(cfg: &Config, a: &Args, grant: &Grant) -> Command {
6f5b84fad3efatagen2026-09-27 03:59 UTC157
    let n = grant.cores.to_string();
caac091f257aatagen2026-10-04 11:41 UTC158
    let mut cmd = Command::new(&a.cmd[0]);
caac091f257aatagen2026-10-04 11:41 UTC159
    cmd.args(&a.cmd[1..])
6f5b84fad3efatagen2026-09-27 03:59 UTC160
        .process_group(0)
6f5b84fad3efatagen2026-09-27 03:59 UTC161
        .env("ROUNDABOUT_LEASE", grant.lease.to_string())
6f5b84fad3efatagen2026-09-27 03:59 UTC162
        .env("CARGO_BUILD_JOBS", &n)
6f5b84fad3efatagen2026-09-27 03:59 UTC163
        .env("CMAKE_BUILD_PARALLEL_LEVEL", &n);
6f5b84fad3efatagen2026-09-27 03:59 UTC164
6f5b84fad3efatagen2026-09-27 03:59 UTC165
    let make = env::var("MAKEFLAGS").unwrap_or_default();
6f5b84fad3efatagen2026-09-27 03:59 UTC166
    if !make.contains("-j") {
6f5b84fad3efatagen2026-09-27 03:59 UTC167
        cmd.env("MAKEFLAGS", format!("-j{n} {make}").trim_end());
6f5b84fad3efatagen2026-09-27 03:59 UTC168
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC169
6f5b84fad3efatagen2026-09-27 03:59 UTC170
    if cfg.nix.inject {
6f5b84fad3efatagen2026-09-27 03:59 UTC171
        let per = cfg.nix.cores_per_job.max(1);
6f5b84fad3efatagen2026-09-27 03:59 UTC172
        let jobs = (grant.cores / per).max(1);
6f5b84fad3efatagen2026-09-27 03:59 UTC173
        let cores = grant.cores.min(per);
6f5b84fad3efatagen2026-09-27 03:59 UTC174
        let mut nix = env::var("NIX_CONFIG").unwrap_or_default();
6f5b84fad3efatagen2026-09-27 03:59 UTC175
        let _ = write!(nix, "\ncores = {cores}\nmax-jobs = {jobs}\n");
6f5b84fad3efatagen2026-09-27 03:59 UTC176
        cmd.env("NIX_CONFIG", nix);
6f5b84fad3efatagen2026-09-27 03:59 UTC177
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC178
6f5b84fad3efatagen2026-09-27 03:59 UTC179
    cmd
6f5b84fad3efatagen2026-09-27 03:59 UTC180
}
6f5b84fad3efatagen2026-09-27 03:59 UTC181
6f5b84fad3efatagen2026-09-27 03:59 UTC182
fn cpu_time() -> Duration {
6f5b84fad3efatagen2026-09-27 03:59 UTC183
    let mut ru = std::mem::MaybeUninit::<libc::rusage>::uninit();
6f5b84fad3efatagen2026-09-27 03:59 UTC184
    unsafe {
6f5b84fad3efatagen2026-09-27 03:59 UTC185
        if libc::getrusage(libc::RUSAGE_CHILDREN, ru.as_mut_ptr()) != 0 {
6f5b84fad3efatagen2026-09-27 03:59 UTC186
            return Duration::ZERO;
6f5b84fad3efatagen2026-09-27 03:59 UTC187
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC188
        let ru = ru.assume_init();
6f5b84fad3efatagen2026-09-27 03:59 UTC189
        let tv = |t: libc::timeval| {
6f5b84fad3efatagen2026-09-27 03:59 UTC190
            Duration::from_secs(t.tv_sec as u64) + Duration::from_micros(t.tv_usec as u64)
6f5b84fad3efatagen2026-09-27 03:59 UTC191
        };
6f5b84fad3efatagen2026-09-27 03:59 UTC192
        tv(ru.ru_utime) + tv(ru.ru_stime)
6f5b84fad3efatagen2026-09-27 03:59 UTC193
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC194
}
6f5b84fad3efatagen2026-09-27 03:59 UTC195
6f5b84fad3efatagen2026-09-27 03:59 UTC196
fn code(status: ExitStatus) -> i32 {
6f5b84fad3efatagen2026-09-27 03:59 UTC197
    use std::os::unix::process::ExitStatusExt;
6f5b84fad3efatagen2026-09-27 03:59 UTC198
    status
6f5b84fad3efatagen2026-09-27 03:59 UTC199
        .code()
6f5b84fad3efatagen2026-09-27 03:59 UTC200
        .or_else(|| status.signal().map(|s| 128 + s))
6f5b84fad3efatagen2026-09-27 03:59 UTC201
        .unwrap_or(1)
6f5b84fad3efatagen2026-09-27 03:59 UTC202
}
6f5b84fad3efatagen2026-09-27 03:59 UTC203
6f5b84fad3efatagen2026-09-27 03:59 UTC204
pub fn run(cfg: &Config, a: &Args) -> i32 {
6f5b84fad3efatagen2026-09-27 03:59 UTC205
    if a.cmd.is_empty() {
6f5b84fad3efatagen2026-09-27 03:59 UTC206
        say("nothing to run, put the command after --");
6f5b84fad3efatagen2026-09-27 03:59 UTC207
        return 2;
6f5b84fad3efatagen2026-09-27 03:59 UTC208
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC209
    if a.size.is_none() && a.ram.is_none() {
6f5b84fad3efatagen2026-09-27 03:59 UTC210
        let sizes: Vec<_> = cfg.sizes.keys().map(String::as_str).collect();
6f5b84fad3efatagen2026-09-27 03:59 UTC211
        say(&format!(
fdad58f8133aatagen2026-10-25 23:14 UTC212
            "how big is it? pass --size {} or --ram and friends. `roundabout suggest -- <cmd>` has a guess",
6f5b84fad3efatagen2026-09-27 03:59 UTC213
            sizes.join("|")
6f5b84fad3efatagen2026-09-27 03:59 UTC214
        ));
6f5b84fad3efatagen2026-09-27 03:59 UTC215
        return 2;
6f5b84fad3efatagen2026-09-27 03:59 UTC216
    }
ecc1cf8df0f5atagen2026-09-27 13:36 UTC217
    let mut need = match need(cfg, a) {
6f5b84fad3efatagen2026-09-27 03:59 UTC218
        Ok(n) => n,
6f5b84fad3efatagen2026-09-27 03:59 UTC219
        Err(e) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC220
            say(&e);
6f5b84fad3efatagen2026-09-27 03:59 UTC221
            return 2;
6f5b84fad3efatagen2026-09-27 03:59 UTC222
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC223
    };
6f5b84fad3efatagen2026-09-27 03:59 UTC224
    let cwd = env::current_dir().unwrap_or_else(|_| "/".into());
6f5b84fad3efatagen2026-09-27 03:59 UTC225
    let root = paths::project_root(&cwd);
6f5b84fad3efatagen2026-09-27 03:59 UTC226
6f5b84fad3efatagen2026-09-27 03:59 UTC227
    let report = artifacts::check(cfg, Some(&root), false);
6f5b84fad3efatagen2026-09-27 03:59 UTC228
    if report.over() {
6f5b84fad3efatagen2026-09-27 03:59 UTC229
        let msg = report.describe(cfg);
6f5b84fad3efatagen2026-09-27 03:59 UTC230
        if cfg.artifacts.over_cap == OverCap::Block {
e9a5f5a41749atagen2026-10-25 11:09 UTC231
            say(&format!("{msg}. clean up first, `{}`", report.remedy()));
6f5b84fad3efatagen2026-09-27 03:59 UTC232
            return EXIT_GAVE_UP;
6f5b84fad3efatagen2026-09-27 03:59 UTC233
        }
e9a5f5a41749atagen2026-10-25 11:09 UTC234
        say(&format!("{msg}. `{}` when you get a sec", report.remedy()));
6f5b84fad3efatagen2026-09-27 03:59 UTC235
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC236
6f5b84fad3efatagen2026-09-27 03:59 UTC237
    let req = Acquire {
6f5b84fad3efatagen2026-09-27 03:59 UTC238
        need,
6f5b84fad3efatagen2026-09-27 03:59 UTC239
        cwd,
6f5b84fad3efatagen2026-09-27 03:59 UTC240
        cmd: a.cmd.clone(),
6f5b84fad3efatagen2026-09-27 03:59 UTC241
        session: a.session.clone(),
6f5b84fad3efatagen2026-09-27 03:59 UTC242
        deadline: a.deadline,
ecc1cf8df0f5atagen2026-09-27 13:36 UTC243
        exact: a.exact,
6f5b84fad3efatagen2026-09-27 03:59 UTC244
    };
6f5b84fad3efatagen2026-09-27 03:59 UTC245
6f5b84fad3efatagen2026-09-27 03:59 UTC246
    let grant = match acquire(&req, a.max_wait.or(cfg.queue.max_wait)) {
6f5b84fad3efatagen2026-09-27 03:59 UTC247
        Ok(g) => g,
6f5b84fad3efatagen2026-09-27 03:59 UTC248
        Err(code) => return code,
6f5b84fad3efatagen2026-09-27 03:59 UTC249
    };
ecc1cf8df0f5atagen2026-09-27 13:36 UTC250
    need.ram = need.ram.max(grant.ram);
6f5b84fad3efatagen2026-09-27 03:59 UTC251
caac091f257aatagen2026-10-04 11:41 UTC252
    let command = command(cfg, a, &grant);
caac091f257aatagen2026-10-04 11:41 UTC253
    #[cfg(target_os = "linux")]
caac091f257aatagen2026-10-04 11:41 UTC254
    let (mut child, scope) =
caac091f257aatagen2026-10-04 11:41 UTC255
        match crate::systemd::spawn(&command, grant.lease, grant.cores, grant.ram, &cfg.overrun) {
caac091f257aatagen2026-10-04 11:41 UTC256
            Ok(spawned) => spawned,
caac091f257aatagen2026-10-04 11:41 UTC257
            Err(error) => {
caac091f257aatagen2026-10-04 11:41 UTC258
                say(&format!("couldn't start {}: {error}", a.cmd[0]));
caac091f257aatagen2026-10-04 11:41 UTC259
                return 127;
caac091f257aatagen2026-10-04 11:41 UTC260
            }
caac091f257aatagen2026-10-04 11:41 UTC261
        };
caac091f257aatagen2026-10-04 11:41 UTC262
    #[cfg(not(target_os = "linux"))]
caac091f257aatagen2026-10-04 11:41 UTC263
    let mut command = command;
caac091f257aatagen2026-10-04 11:41 UTC264
    #[cfg(not(target_os = "linux"))]
caac091f257aatagen2026-10-04 11:41 UTC265
    let mut child = match command.spawn() {
6f5b84fad3efatagen2026-09-27 03:59 UTC266
        Ok(c) => c,
6f5b84fad3efatagen2026-09-27 03:59 UTC267
        Err(e) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC268
            say(&format!("couldn't start {}: {e}", a.cmd[0]));
6f5b84fad3efatagen2026-09-27 03:59 UTC269
            return 127;
6f5b84fad3efatagen2026-09-27 03:59 UTC270
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC271
    };
6f5b84fad3efatagen2026-09-27 03:59 UTC272
    let pgid = i32::try_from(child.id()).expect("pid fits in pid_t");
6f5b84fad3efatagen2026-09-27 03:59 UTC273
    PGID.store(pgid, Ordering::Relaxed);
6f5b84fad3efatagen2026-09-27 03:59 UTC274
    for sig in [libc::SIGINT, libc::SIGTERM, libc::SIGHUP] {
6f5b84fad3efatagen2026-09-27 03:59 UTC275
        unsafe { libc::signal(sig, forward as *const () as libc::sighandler_t) };
6f5b84fad3efatagen2026-09-27 03:59 UTC276
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC277
6f5b84fad3efatagen2026-09-27 03:59 UTC278
    let mut out = grant.stream.try_clone().ok();
6f5b84fad3efatagen2026-09-27 03:59 UTC279
    if let Some(s) = &mut out {
6f5b84fad3efatagen2026-09-27 03:59 UTC280
        let _ = proto::send(s, &Request::Started { pid: pgid });
6f5b84fad3efatagen2026-09-27 03:59 UTC281
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC282
6f5b84fad3efatagen2026-09-27 03:59 UTC283
    let killed = Arc::new(AtomicBool::new(false));
6f5b84fad3efatagen2026-09-27 03:59 UTC284
    if let Ok(mut reader) = Reader::new(&grant.stream) {
6f5b84fad3efatagen2026-09-27 03:59 UTC285
        let killed = killed.clone();
6f5b84fad3efatagen2026-09-27 03:59 UTC286
        thread::spawn(move || {
6f5b84fad3efatagen2026-09-27 03:59 UTC287
            while let Ok(Some(ev)) = reader.recv::<Event>() {
6f5b84fad3efatagen2026-09-27 03:59 UTC288
                if matches!(ev, Event::Kill) {
6f5b84fad3efatagen2026-09-27 03:59 UTC289
                    killed.store(true, Ordering::Relaxed);
6f5b84fad3efatagen2026-09-27 03:59 UTC290
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC291
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC292
        });
6f5b84fad3efatagen2026-09-27 03:59 UTC293
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC294
6f5b84fad3efatagen2026-09-27 03:59 UTC295
    let started = Instant::now();
6f5b84fad3efatagen2026-09-27 03:59 UTC296
    let grace = need.time.mul_f64(cfg.overrun.time_grace);
6f5b84fad3efatagen2026-09-27 03:59 UTC297
    let loud_ram = Bytes((need.ram.0 as f64 * cfg.overrun.ram_slack) as u64);
6f5b84fad3efatagen2026-09-27 03:59 UTC298
    let mut peak = Bytes::ZERO;
6f5b84fad3efatagen2026-09-27 03:59 UTC299
    let mut last_sample: Option<Instant> = None;
6f5b84fad3efatagen2026-09-27 03:59 UTC300
    let mut term_sent: Option<Instant> = None;
6f5b84fad3efatagen2026-09-27 03:59 UTC301
    let mut timed_out = false;
6f5b84fad3efatagen2026-09-27 03:59 UTC302
    let mut warned_ram = false;
6f5b84fad3efatagen2026-09-27 03:59 UTC303
    let mut warned_time = false;
6f5b84fad3efatagen2026-09-27 03:59 UTC304
6f5b84fad3efatagen2026-09-27 03:59 UTC305
    let status = loop {
caac091f257aatagen2026-10-04 11:41 UTC306
        #[cfg(target_os = "linux")]
caac091f257aatagen2026-10-04 11:41 UTC307
        if let signal @ 1.. = FORWARDED.swap(0, Ordering::Relaxed) {
caac091f257aatagen2026-10-04 11:41 UTC308
            let _ = scope.signal(signal);
caac091f257aatagen2026-10-04 11:41 UTC309
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC310
        match child.try_wait() {
6f5b84fad3efatagen2026-09-27 03:59 UTC311
            Ok(Some(status)) => break status,
6f5b84fad3efatagen2026-09-27 03:59 UTC312
            Ok(None) => {}
6f5b84fad3efatagen2026-09-27 03:59 UTC313
            Err(e) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC314
                say(&format!("lost track of the child: {e}"));
6f5b84fad3efatagen2026-09-27 03:59 UTC315
                return 1;
6f5b84fad3efatagen2026-09-27 03:59 UTC316
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC317
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC318
        let elapsed = started.elapsed();
6f5b84fad3efatagen2026-09-27 03:59 UTC319
6f5b84fad3efatagen2026-09-27 03:59 UTC320
        if term_sent.is_none() {
6f5b84fad3efatagen2026-09-27 03:59 UTC321
            if killed.load(Ordering::Relaxed) {
6f5b84fad3efatagen2026-09-27 03:59 UTC322
                say("killed on request");
6f5b84fad3efatagen2026-09-27 03:59 UTC323
                term_sent = Some(Instant::now());
6f5b84fad3efatagen2026-09-27 03:59 UTC324
            } else if a.deadline.is_some_and(|d| elapsed > d.0) {
6f5b84fad3efatagen2026-09-27 03:59 UTC325
                say("deadline hit, killing it");
6f5b84fad3efatagen2026-09-27 03:59 UTC326
                timed_out = true;
6f5b84fad3efatagen2026-09-27 03:59 UTC327
                term_sent = Some(Instant::now());
6f5b84fad3efatagen2026-09-27 03:59 UTC328
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC329
            if term_sent.is_some() {
caac091f257aatagen2026-10-04 11:41 UTC330
                #[cfg(target_os = "linux")]
caac091f257aatagen2026-10-04 11:41 UTC331
                let _ = scope.signal(libc::SIGTERM);
6f5b84fad3efatagen2026-09-27 03:59 UTC332
                unsafe { libc::kill(-pgid, libc::SIGTERM) };
6f5b84fad3efatagen2026-09-27 03:59 UTC333
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC334
        } else if term_sent.is_some_and(|t| t.elapsed() > Duration::from_secs(10)) {
caac091f257aatagen2026-10-04 11:41 UTC335
            #[cfg(target_os = "linux")]
caac091f257aatagen2026-10-04 11:41 UTC336
            let _ = scope.signal(libc::SIGKILL);
6f5b84fad3efatagen2026-09-27 03:59 UTC337
            unsafe { libc::kill(-pgid, libc::SIGKILL) };
6f5b84fad3efatagen2026-09-27 03:59 UTC338
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC339
6f5b84fad3efatagen2026-09-27 03:59 UTC340
        if last_sample.is_none_or(|t| t.elapsed() >= Duration::from_secs(2)) {
6f5b84fad3efatagen2026-09-27 03:59 UTC341
            last_sample = Some(Instant::now());
6f5b84fad3efatagen2026-09-27 03:59 UTC342
            let rss = sys::group_rss(pgid);
6f5b84fad3efatagen2026-09-27 03:59 UTC343
            peak = peak.max(rss);
6f5b84fad3efatagen2026-09-27 03:59 UTC344
            if let Some(s) = &mut out {
6f5b84fad3efatagen2026-09-27 03:59 UTC345
                let _ = proto::send(s, &Request::Usage { rss });
6f5b84fad3efatagen2026-09-27 03:59 UTC346
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC347
            if !warned_ram && rss > loud_ram {
6f5b84fad3efatagen2026-09-27 03:59 UTC348
                warned_ram = true;
6f5b84fad3efatagen2026-09-27 03:59 UTC349
                say(&format!("using {rss} against a {} estimate", need.ram));
6f5b84fad3efatagen2026-09-27 03:59 UTC350
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC351
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC352
        if !warned_time && elapsed > grace {
6f5b84fad3efatagen2026-09-27 03:59 UTC353
            warned_time = true;
6f5b84fad3efatagen2026-09-27 03:59 UTC354
            say(&format!(
6f5b84fad3efatagen2026-09-27 03:59 UTC355
                "{} past its {} estimate and still going",
6f5b84fad3efatagen2026-09-27 03:59 UTC356
                Dur(elapsed),
6f5b84fad3efatagen2026-09-27 03:59 UTC357
                Dur(need.time)
6f5b84fad3efatagen2026-09-27 03:59 UTC358
            ));
6f5b84fad3efatagen2026-09-27 03:59 UTC359
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC360
        thread::sleep(Duration::from_millis(250));
6f5b84fad3efatagen2026-09-27 03:59 UTC361
    };
6f5b84fad3efatagen2026-09-27 03:59 UTC362
caac091f257aatagen2026-10-04 11:41 UTC363
    PGID.store(0, Ordering::Relaxed);
caac091f257aatagen2026-10-04 11:41 UTC364
    #[cfg(target_os = "linux")]
caac091f257aatagen2026-10-04 11:41 UTC365
    drop(scope);
6f5b84fad3efatagen2026-09-27 03:59 UTC366
    let wall = started.elapsed();
6f5b84fad3efatagen2026-09-27 03:59 UTC367
    if let Some(s) = &mut out {
6f5b84fad3efatagen2026-09-27 03:59 UTC368
        let _ = proto::send(s, &Request::Done);
6f5b84fad3efatagen2026-09-27 03:59 UTC369
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC370
    history::record(
6f5b84fad3efatagen2026-09-27 03:59 UTC371
        history::key(&root, &a.cmd),
6f5b84fad3efatagen2026-09-27 03:59 UTC372
        peak,
6f5b84fad3efatagen2026-09-27 03:59 UTC373
        wall,
6f5b84fad3efatagen2026-09-27 03:59 UTC374
        cpu_time(),
6f5b84fad3efatagen2026-09-27 03:59 UTC375
        grant.cores,
6f5b84fad3efatagen2026-09-27 03:59 UTC376
    );
6f5b84fad3efatagen2026-09-27 03:59 UTC377
    artifacts::register(cfg, &root, true);
6f5b84fad3efatagen2026-09-27 03:59 UTC378
    if peak > loud_ram || wall > grace {
6f5b84fad3efatagen2026-09-27 03:59 UTC379
        say(&format!(
6f5b84fad3efatagen2026-09-27 03:59 UTC380
            "took {} and peaked at {}, asked for {} and {}. ask for more next time",
6f5b84fad3efatagen2026-09-27 03:59 UTC381
            Dur(wall),
6f5b84fad3efatagen2026-09-27 03:59 UTC382
            peak,
6f5b84fad3efatagen2026-09-27 03:59 UTC383
            Dur(need.time),
6f5b84fad3efatagen2026-09-27 03:59 UTC384
            need.ram
6f5b84fad3efatagen2026-09-27 03:59 UTC385
        ));
6f5b84fad3efatagen2026-09-27 03:59 UTC386
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC387
    if timed_out {
6f5b84fad3efatagen2026-09-27 03:59 UTC388
        EXIT_TIMEOUT
6f5b84fad3efatagen2026-09-27 03:59 UTC389
    } else {
6f5b84fad3efatagen2026-09-27 03:59 UTC390
        code(status)
6f5b84fad3efatagen2026-09-27 03:59 UTC391
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC392
}