daemonize_me/
stdio.rs

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/// describes what to do with a standard io stream for a child process.
26#[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}