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}