//! Creates transient unit files and optionally waits for their final state. use sausage_init::*; use std::{fs, io}; fn run() -> io::Result { let mut args = std::env::args().skip(1).peekable(); let mut unit = format!( "run-{}", std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_nanos() ); let (mut wait, mut properties, mut command) = (false, Vec::new(), Vec::new()); while let Some(arg) = args.next() { match arg.as_str() { "--unit" => { unit = args .next() .ok_or_else(|| io::Error::other("missing unit"))? } "--wait" => wait = true, "-p" => properties.push( args.next() .ok_or_else(|| io::Error::other("missing property"))?, ), "--working-directory" => properties.push(format!( "WorkingDirectory={}", args.next() .ok_or_else(|| io::Error::other("missing directory"))? )), "-E" => properties.push(format!( "Environment={}", args.next() .ok_or_else(|| io::Error::other("missing environment"))? )), "--" => { command.extend(args); break; } _ if arg.starts_with("--unit=") => unit = arg[7..].into(), _ if arg.starts_with('-') => return Ok(1), _ => { command.push(arg); command.extend(args); break; } } } if command.is_empty() { return Ok(1); } let unit = name(&unit); let mut text = format!( "[Service]\nExecStart={}\n", shlex::try_join(command.iter().map(String::as_str)).map_err(io::Error::other)? ); for property in properties { text.push_str(&property); text.push('\n'); } fs::create_dir_all(TRANSIENT)?; atomic(&format!("{TRANSIENT}/{unit}"), text.as_bytes())?; let reply = request("start", &unit)?; if reply.code != 0 { return Ok(reply.code); } if wait { let watcher = sausage_spawn::Watcher::new(&[format!("{RUN}/state/{unit}")]); let changed = watcher.subscribe(); loop { if let Ok(bytes) = fs::read(format!("{RUN}/state/{unit}")) { let status: Status = serde_json::from_slice(&bytes)?; if let Some(code) = status.code { return Ok(code); } } changed.block(); let _ = watcher.changes(); } } Ok(0) } fn main() { sausage_init::exit(run().unwrap_or(1)); }