daemonize_simple/
unix.rs

1use std::env::set_current_dir;
2use std::ffi::CString;
3use std::fs::File;
4use std::fs::OpenOptions;
5use std::io::prelude::*;
6use std::mem;
7use std::os::unix::io::AsRawFd;
8use std::path::PathBuf;
9
10#[derive(Default, Debug, Clone)]
11pub struct Daemonize {
12    pub chdir: Option<PathBuf>,
13    pub pid_file: Option<PathBuf>,
14    pub stdin_file: Option<PathBuf>,
15    pub stdout_file: Option<PathBuf>,
16    pub stderr_file: Option<PathBuf>,
17    pub umask: Option<libc::mode_t>,
18    pub chroot: bool,
19    pub append: bool,
20}
21
22impl Daemonize {
23    unsafe fn _doit(self) -> Result<(), &'static str> {
24        match libc::fork() {
25            -1 => return Err("fork() failed"),
26            0 => {}
27            _ => {
28                libc::_exit(0);
29            }
30        }
31        libc::setsid();
32        match libc::fork() {
33            -1 => return Err("fork() failed"),
34            0 => {}
35            _ => {
36                libc::_exit(0);
37            }
38        };
39
40        if let Some(umask) = self.umask {
41            libc::umask(umask);
42        }
43        if let Some(chdir) = &self.chdir {
44            set_current_dir(chdir).map_err(|_| "chdir() failed")?;
45        }
46
47        let stdin_file = self.stdin_file.unwrap_or_else(|| "/dev/null".into());
48        let fd = OpenOptions::new()
49            .read(true)
50            .open(&stdin_file)
51            .map_err(|_| "Unable to open the stdin file")?;
52        if libc::dup2(fd.as_raw_fd(), 0) == -1 {
53            return Err("dup2(stdin) failed");
54        }
55        mem::forget(stdin_file);
56        libc::close(fd.as_raw_fd());
57        let stdout_file = self.stdout_file.unwrap_or_else(|| "/dev/null".into());
58        let fd = OpenOptions::new()
59            .create(true)
60            .write(true)
61            .append(self.append)
62            .open(&stdout_file)
63            .map_err(|_| "Unable to open the stdout file")?;
64        if libc::dup2(fd.as_raw_fd(), 1) == -1 {
65            return Err("dup2(stdout) failed");
66        }
67        mem::forget(stdout_file);
68        libc::close(fd.as_raw_fd());
69        let stderr_file = self.stderr_file.unwrap_or_else(|| "/dev/null".into());
70        let fd = OpenOptions::new()
71            .create(true)
72            .write(true)
73            .append(self.append)
74            .open(&stderr_file)
75            .map_err(|_| "Unable to open the stderr file")?;
76        if libc::dup2(fd.as_raw_fd(), 2) == -1 {
77            return Err("dup2(stderr) failed");
78        }
79        mem::forget(stderr_file);
80        libc::close(fd.as_raw_fd());
81
82        if let Some(pid_file) = self.pid_file {
83            let pid = match libc::getpid() {
84                -1 => return Err("getpid() failed"),
85                pid => pid,
86            };
87            let pid_str = format!("{}", pid);
88            File::create(pid_file)
89                .map_err(|_| "Creating the PID file failed")?
90                .write_all(pid_str.as_bytes())
91                .map_err(|_| "Writing to the PID file failed")?;
92        }
93
94        if let Some(chdir) = &self.chdir {
95            if self.chroot {
96                let chdir = CString::new(
97                    chdir
98                        .as_os_str()
99                        .to_str()
100                        .ok_or("Unexpected characters in chdir path")?,
101                )
102                .map_err(|_| "Unexpected chdir path")?;
103                if libc::chroot(chdir.as_ptr()) != 0 {
104                    return Err("chroot failed");
105                }
106                set_current_dir("/").map_err(|_| "chdir(\"/\") failed")?;
107            } else {
108                set_current_dir(chdir).map_err(|_| "chdir() failed")?;
109            }
110        }
111
112        Ok(())
113    }
114
115    pub fn doit(self) -> Result<(), &'static str> {
116        unsafe { self._doit() }
117    }
118}