Skip to main content

linuxutils_system/
setpgid.rs

1use linuxutils_common::man::ManContent;
2
3pub const MAN: ManContent = ManContent::empty();
4
5use clap::Parser;
6use rustix::{
7    process::{getpid, setpgid as do_setpgid},
8    termios::tcsetpgrp,
9};
10use std::{ffi::CString, process::ExitCode};
11
12#[derive(Parser)]
13#[command(
14    name = "setpgid",
15    version,
16    about = "Run a program in a new process group"
17)]
18pub struct Args {
19    /// Make the new process group the foreground process group
20    #[arg(short = 'f', long = "foreground")]
21    foreground: bool,
22
23    /// Program and its arguments
24    #[arg(
25        trailing_var_arg = true,
26        allow_hyphen_values = true,
27        required = true
28    )]
29    pub command: Vec<String>,
30}
31
32pub fn run(args: Args) -> ExitCode {
33    let pid = getpid();
34
35    if let Err(e) = do_setpgid(Some(pid), Some(pid)) {
36        eprintln!("setpgid: setpgid: {e}");
37        return ExitCode::FAILURE;
38    }
39
40    if args.foreground {
41        let stderr = std::io::stderr();
42        if let Err(e) = tcsetpgrp(&stderr, pid) {
43            eprintln!("setpgid: tcsetpgrp: {e}");
44            return ExitCode::FAILURE;
45        }
46    }
47
48    let program = &args.command[0];
49    let arguments = &args.command[1..];
50    exec_program(program, arguments)
51}
52
53fn exec_program(program: &str, arguments: &[String]) -> ExitCode {
54    let c_program = match CString::new(program.as_bytes()) {
55        Ok(s) => s,
56        Err(e) => {
57            eprintln!("setpgid: {e}");
58            return ExitCode::FAILURE;
59        }
60    };
61
62    let mut c_args: Vec<CString> = vec![c_program.clone()];
63    for arg in arguments {
64        match CString::new(arg.as_bytes()) {
65            Ok(s) => c_args.push(s),
66            Err(e) => {
67                eprintln!("setpgid: {e}");
68                return ExitCode::FAILURE;
69            }
70        }
71    }
72
73    let c_ptrs: Vec<*const libc::c_char> = c_args
74        .iter()
75        .map(|s| s.as_ptr())
76        .chain(std::iter::once(std::ptr::null()))
77        .collect();
78
79    unsafe {
80        libc::execvp(c_program.as_ptr(), c_ptrs.as_ptr());
81    }
82
83    // execvp only returns on error.
84    eprintln!(
85        "setpgid: exec {program}: {}",
86        std::io::Error::last_os_error()
87    );
88    ExitCode::from(127)
89}