use clap::{Arg, Command};
use color_eyre::Result;
use color_eyre::eyre::eyre;
use std::env;
use std::fs::OpenOptions;
use std::io::IsTerminal;
use std::process;
pub struct NohupOptions {
pub command: Vec<String>,
}
pub fn parse_options(matches: &clap::ArgMatches) -> Result<NohupOptions> {
let command: Vec<String> = matches.get_many::<String>("command")
.map(|vals| vals.cloned().collect())
.unwrap_or_default();
if command.is_empty() {
return Err(eyre!("nohup: missing operand"));
}
Ok(NohupOptions { command })
}
pub fn command() -> Command {
Command::new("nohup")
.about("Run a command immune to hangups")
.arg(Arg::new("command")
.num_args(1..)
.required(true)
.help("Command to run"))
}
pub fn run(options: NohupOptions) -> Result<()> {
#[cfg(unix)]
{
use nix::sys::signal::{Signal, SigHandler};
unsafe {
nix::sys::signal::signal(Signal::SIGHUP, SigHandler::SigIgn)
.map_err(|e| eyre!("nohup: cannot ignore SIGHUP: {}", e))?;
}
}
let nohup_file = if std::path::Path::new("nohup.out").parent().is_some() {
"nohup.out".to_string()
} else {
env::var("HOME")
.map(|home| format!("{}/nohup.out", home))
.unwrap_or_else(|_| "nohup.out".to_string())
};
let stdout_is_tty = std::io::stdout().is_terminal();
let stderr_is_tty = std::io::stderr().is_terminal();
let cmd = &options.command[0];
let args = &options.command[1..];
let mut process_cmd = process::Command::new(cmd);
process_cmd.args(args);
if stdout_is_tty || stderr_is_tty {
let file = OpenOptions::new()
.create(true)
.write(true)
.append(true)
.open(&nohup_file)
.map_err(|e| eyre!("nohup: cannot open '{}': {}", nohup_file, e))?;
if stdout_is_tty {
process_cmd.stdout(file.try_clone().unwrap());
}
if stderr_is_tty {
process_cmd.stderr(file);
}
if stderr_is_tty {
eprintln!("nohup: appending output to '{}'", nohup_file);
}
}
let status = process_cmd.status()
.map_err(|e| eyre!("nohup: cannot execute '{}': {}", cmd, e))?;
std::process::exit(status.code().unwrap_or(1));
}