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