1use std::fmt::Debug;
2use std::fs::File;
3use std::os::unix::io::AsRawFd;
4use std::path::Path;
5
6use nix::fcntl::{OFlag, open};
7use nix::sys::stat::Mode;
8#[cfg(not(target_os = "macos"))]
9use nix::unistd::{
10 close, dup2,
11};
12#[cfg(target_os = "macos")]
13use nix::unistd::{
14 chdir, chown, close, dup2, fork, ForkResult, getpid, Gid, Pid, setgid, setsid, setuid, Uid,
15};
16
17use crate::{DaemonError, Result};
18
19#[derive(Debug)]
20enum StdioImp {
21 Devnull,
22 RedirectToFile(File),
23}
24
25#[derive(Debug)]
27pub struct Stdio {
28 inner: StdioImp,
29}
30
31impl Stdio {
32 pub(crate) fn devnull() -> Self {
33 Self {
34 inner: StdioImp::Devnull,
35 }
36 }
37}
38
39impl From<File> for Stdio {
40 fn from(file: File) -> Self {
41 Self {
42 inner: StdioImp::RedirectToFile(file),
43 }
44 }
45}
46
47pub(crate) fn redirect_stdio(stdin: &Stdio, stdout: &Stdio, stderr: &Stdio) -> Result<()> {
48 let devnull_fd = match open(
49 Path::new("/dev/null"),
50 OFlag::O_APPEND,
51 Mode::from_bits(OFlag::O_RDWR.bits() as _).unwrap(),
52 ) {
53 Ok(fd) => fd,
54 Err(_) => return Err(DaemonError::OpenDevNull),
55 };
56 let proc_stream = |fd, stdio: &Stdio| {
57 match close(fd) {
58 Ok(_) => (),
59 Err(_) => return Err(DaemonError::CloseFp),
60 };
61 return match &stdio.inner {
62 StdioImp::Devnull => match dup2(devnull_fd, fd) {
63 Ok(_) => Ok(()),
64 Err(_) => Err(DaemonError::RedirectStream),
65 },
66 StdioImp::RedirectToFile(file) => {
67 let raw_fd = file.as_raw_fd();
68 match dup2(raw_fd, fd) {
69 Ok(_) => Ok(()),
70 Err(_) => Err(DaemonError::RedirectStream),
71 }
72 }
73 };
74 };
75
76 proc_stream(libc::STDIN_FILENO, stdin)?;
77 proc_stream(libc::STDOUT_FILENO, stdout)?;
78 proc_stream(libc::STDERR_FILENO, stderr)?;
79
80 Ok(())
81}