//! What the sandbox tiers share, on every system a runner runs on: where a //! job's disks are kept, how a helper says a job never started, reading a //! job's output by lines, and this binary's hash for receipts. use std::io::{BufRead, BufReader, Read}; use std::path::{Path, PathBuf}; use std::sync::{mpsc, OnceLock}; /// Where the `n`th `ephemeral://disk` mount of a job keeps its files. pub fn disk_dir(scratch: &Path, n: usize) -> PathBuf { scratch.join(format!("mount-{n}")) } /// How the helper says the job never started, on its stderr. pub(crate) const FAILED: &str = "\u{1e}F "; pub(crate) fn lines(r: impl Read, stdout: bool, tx: mpsc::Sender<(bool, Vec)>) { let mut r = BufReader::new(r); let mut buf = Vec::new(); loop { buf.clear(); // A long line is cut at 16 KiB, as flow-init cuts it. match (&mut r).take(16 * 1024).read_until(b'\n', &mut buf) { Ok(0) | Err(_) => break, Ok(_) => { if buf.last() == Some(&b'\n') { buf.pop(); } if tx.send((stdout, buf.clone())).is_err() { break; } } } } } /// This binary, to start helpers from and hash: `/proc/self/exe` on Linux /// and Android (the running file, even after an update replaced its name), /// the path it was started from elsewhere (oxygenflow#83). pub fn self_exe() -> PathBuf { if cfg!(target_os = "linux") || cfg!(target_os = "android") { PathBuf::from("/proc/self/exe") } else { std::env::current_exe().unwrap_or_default() } } /// This binary's hash, which receipts name as the sandbox. pub(crate) fn self_hash() -> &'static str { static HASH: OnceLock = OnceLock::new(); HASH.get_or_init(|| crate::home::hash_file(&self_exe()).unwrap_or_else(|_| "unknown".into())) }