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