daemonize-simple 0.1.5

A simple crate to run an app as a Unix daemon
Documentation
use std::env::set_current_dir;
use std::ffi::CString;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::mem;
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,
    pub append: 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");
        }
        mem::forget(stdin_file);
        libc::close(fd.as_raw_fd());
        let stdout_file = self.stdout_file.unwrap_or_else(|| "/dev/null".into());
        let fd = OpenOptions::new()
            .create(true)
            .write(true)
            .append(self.append)
            .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");
        }
        mem::forget(stdout_file);
        libc::close(fd.as_raw_fd());
        let stderr_file = self.stderr_file.unwrap_or_else(|| "/dev/null".into());
        let fd = OpenOptions::new()
            .create(true)
            .write(true)
            .append(self.append)
            .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");
        }
        mem::forget(stderr_file);
        libc::close(fd.as_raw_fd());

        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")?;
            } else {
                set_current_dir(chdir).map_err(|_| "chdir() failed")?;
            }
        }

        Ok(())
    }

    pub fn doit(self) -> Result<(), &'static str> {
        unsafe { self._doit() }
    }
}