linuxutils_system/
setsid.rs1use linuxutils_common::man::ManContent;
2
3pub const MAN: ManContent = ManContent::empty();
4
5use clap::Parser;
6use rustix::process::{
7 Pid, WaitOptions, getpgrp, getpid, setsid as do_setsid, waitpid,
8};
9use std::{ffi::CString, os::unix::io::AsRawFd, process::ExitCode};
10
11#[derive(Parser)]
12#[command(name = "setsid", version, about = "Run a program in a new session")]
13pub struct Args {
14 #[arg(short = 'c', long = "ctty")]
16 ctty: bool,
17
18 #[arg(short = 'f', long = "fork")]
20 fork: bool,
21
22 #[arg(short = 'w', long = "wait")]
24 wait: bool,
25
26 #[arg(
28 trailing_var_arg = true,
29 allow_hyphen_values = true,
30 required = true
31 )]
32 pub command: Vec<String>,
33}
34
35pub fn run(args: Args) -> ExitCode {
36 let need_fork = args.fork || is_process_group_leader();
37
38 if need_fork {
39 let pid = unsafe { libc::fork() };
41 match pid {
42 -1 => {
43 eprintln!("setsid: fork: {}", std::io::Error::last_os_error());
44 return ExitCode::FAILURE;
45 }
46 0 => {
47 }
49 child_pid => {
50 if !args.wait {
52 return ExitCode::SUCCESS;
53 }
54 return wait_for_child(child_pid);
55 }
56 }
57 }
58
59 if let Err(e) = do_setsid() {
60 eprintln!("setsid: setsid: {e}");
61 return ExitCode::FAILURE;
62 }
63
64 if args.ctty {
65 let stdin = std::io::stdin();
66 let fd = stdin.as_raw_fd();
67 let ret = unsafe { libc::ioctl(fd, libc::TIOCSCTTY, 0) };
69 if ret < 0 {
70 eprintln!(
71 "setsid: ioctl TIOCSCTTY: {}",
72 std::io::Error::last_os_error()
73 );
74 return ExitCode::FAILURE;
75 }
76 }
77
78 let program = &args.command[0];
79 let arguments = &args.command[1..];
80 exec_program(program, arguments)
81}
82
83fn is_process_group_leader() -> bool {
84 let pid = getpid();
85 let pgrp = getpgrp();
86 pid.as_raw_nonzero() == pgrp.as_raw_nonzero()
87}
88
89fn wait_for_child(pid: i32) -> ExitCode {
90 let pid = Pid::from_raw(pid);
91 let Some(pid) = pid else {
92 return ExitCode::FAILURE;
93 };
94 loop {
95 match waitpid(Some(pid), WaitOptions::empty()) {
96 Ok(Some((_pid, status))) => {
97 if let Some(code) = status.exit_status() {
98 return ExitCode::from(code as u8);
99 }
100 if status.terminating_signal().is_some() {
101 return ExitCode::FAILURE;
102 }
103 }
105 Ok(None) => continue,
106 Err(e) => {
107 eprintln!("setsid: waitpid: {e}");
108 return ExitCode::FAILURE;
109 }
110 }
111 }
112}
113
114fn exec_program(program: &str, arguments: &[String]) -> ExitCode {
115 let c_program = match CString::new(program.as_bytes()) {
116 Ok(s) => s,
117 Err(e) => {
118 eprintln!("setsid: {e}");
119 return ExitCode::FAILURE;
120 }
121 };
122
123 let mut c_args: Vec<CString> = vec![c_program.clone()];
124 for arg in arguments {
125 match CString::new(arg.as_bytes()) {
126 Ok(s) => c_args.push(s),
127 Err(e) => {
128 eprintln!("setsid: {e}");
129 return ExitCode::FAILURE;
130 }
131 }
132 }
133
134 let c_ptrs: Vec<*const libc::c_char> = c_args
135 .iter()
136 .map(|s| s.as_ptr())
137 .chain(std::iter::once(std::ptr::null()))
138 .collect();
139
140 unsafe {
141 libc::execvp(c_program.as_ptr(), c_ptrs.as_ptr());
142 }
143
144 eprintln!(
146 "setsid: exec {program}: {}",
147 std::io::Error::last_os_error()
148 );
149 ExitCode::from(127)
150}