1use std::{
4 ffi::CString,
5 os::fd::{
6 AsFd,
7 FromRawFd,
8 OwnedFd,
9 },
10};
11
12use nix::{
13 errno::Errno,
14 libc,
15 unistd::{
16 Gid,
17 Uid,
18 User,
19 dup2,
20 getpid,
21 initgroups,
22 setpgid,
23 setresgid,
24 setresuid,
25 setsid,
26 },
27};
28
29pub fn nullify_stdio() -> Result<(), std::io::Error> {
30 let dev_null = std::fs::File::options()
31 .read(true)
32 .write(true)
33 .open("/dev/null")?;
34 let mut stdin = unsafe { OwnedFd::from_raw_fd(0) };
35 let mut stdout = unsafe { OwnedFd::from_raw_fd(1) };
36 let mut stderr = unsafe { OwnedFd::from_raw_fd(2) };
37 dup2(dev_null.as_fd(), &mut stdin)?;
38 dup2(dev_null.as_fd(), &mut stdout)?;
39 dup2(dev_null.as_fd(), &mut stderr)?;
40 std::mem::forget(stdin);
41 std::mem::forget(stdout);
42 std::mem::forget(stderr);
43 Ok(())
44}
45
46pub fn runas(user: &User, effective: Option<(Uid, Gid)>) -> Result<(), Errno> {
47 let (euid, egid) = effective.unwrap_or((user.uid, user.gid));
48 initgroups(&CString::new(user.name.as_str()).unwrap()[..], user.gid)?;
49 setresgid(user.gid, egid, Gid::from_raw(u32::MAX))?;
50 setresuid(user.uid, euid, Uid::from_raw(u32::MAX))?;
51 Ok(())
52}
53
54pub fn lead_process_group() -> Result<(), Errno> {
55 let me = getpid();
56 setpgid(me, me)
57}
58
59pub fn lead_session_and_control_terminal() -> Result<(), Errno> {
60 setsid()?;
61 if unsafe { libc::ioctl(0, libc::TIOCSCTTY as _, 0) } == -1 {
62 Err(Errno::last())?;
63 }
64 Ok(())
65}
66
67#[cfg(test)]
68mod tests {
69 use std::io::{
70 Read,
71 Write,
72 };
73
74 use nix::unistd::getpgrp;
75 use rusty_fork::rusty_fork_test;
76
77 use super::*;
78
79 rusty_fork_test! {
80 #[test]
81 fn test_nullify_stdio() {
82 nullify_stdio().expect("nullify_stdio failed");
83
84 let mut stdout = std::io::stdout();
87 stdout.write_all(b"discarded").unwrap();
88 stdout.flush().unwrap();
89
90 let mut buf = [0u8; 16];
92 let mut stdin = std::io::stdin();
93 let n = stdin.read(&mut buf).unwrap();
94 assert_eq!(n, 0);
95 }
96 }
97
98 rusty_fork_test! {
99 #[test]
100 fn test_lead_process_group() {
101 let pid = nix::unistd::getpid();
102 let pgrp_before = getpgrp();
103
104 lead_process_group().expect("lead_process_group failed");
105
106 let pgrp_after = getpgrp();
107
108 assert_eq!(pgrp_after, pid);
110
111 let _ = pgrp_before;
113 }
114 }
115}