use linuxutils_common::man::ManContent;
pub const MAN: ManContent = ManContent::empty();
use clap::Parser;
use rustix::{
process::{getpid, setpgid as do_setpgid},
termios::tcsetpgrp,
};
use std::{ffi::CString, process::ExitCode};
#[derive(Parser)]
#[command(
name = "setpgid",
version,
about = "Run a program in a new process group"
)]
pub struct Args {
#[arg(short = 'f', long = "foreground")]
foreground: bool,
#[arg(
trailing_var_arg = true,
allow_hyphen_values = true,
required = true
)]
pub command: Vec<String>,
}
pub fn run(args: Args) -> ExitCode {
let pid = getpid();
if let Err(e) = do_setpgid(Some(pid), Some(pid)) {
eprintln!("setpgid: setpgid: {e}");
return ExitCode::FAILURE;
}
if args.foreground {
let stderr = std::io::stderr();
if let Err(e) = tcsetpgrp(&stderr, pid) {
eprintln!("setpgid: tcsetpgrp: {e}");
return ExitCode::FAILURE;
}
}
let program = &args.command[0];
let arguments = &args.command[1..];
exec_program(program, arguments)
}
fn exec_program(program: &str, arguments: &[String]) -> ExitCode {
let c_program = match CString::new(program.as_bytes()) {
Ok(s) => s,
Err(e) => {
eprintln!("setpgid: {e}");
return ExitCode::FAILURE;
}
};
let mut c_args: Vec<CString> = vec![c_program.clone()];
for arg in arguments {
match CString::new(arg.as_bytes()) {
Ok(s) => c_args.push(s),
Err(e) => {
eprintln!("setpgid: {e}");
return ExitCode::FAILURE;
}
}
}
let c_ptrs: Vec<*const libc::c_char> = c_args
.iter()
.map(|s| s.as_ptr())
.chain(std::iter::once(std::ptr::null()))
.collect();
unsafe {
libc::execvp(c_program.as_ptr(), c_ptrs.as_ptr());
}
eprintln!(
"setpgid: exec {program}: {}",
std::io::Error::last_os_error()
);
ExitCode::from(127)
}