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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! Library for creating a new process detached from the controling terminal (daemon)
//!
//! Example:
//! ```
//!use fork::{daemon, Fork};
//!use std::process::Command;
//!
//!fn main() {
//!    if let Ok(Fork::Child) = daemon(true, true) {
//!        Command::new("sleep")
//!            .arg("300")
//!            .output()
//!            .expect("failed to execute process");
//!    }
//!}
//!```

use libc;
use std::ffi::CString;
use std::process::exit;

/// Fork result
pub enum Fork {
    Parent(libc::pid_t),
    Child,
}

/// Upon successful completion, fork() returns a value of 0 to the child process
/// and returns the process ID of the child process to the parent process.
/// Otherwise, a value of -1 is returned to the parent process, no child process
/// is created.
pub fn fork() -> Result<Fork, ()> {
    let res = unsafe { libc::fork() };
    match res {
        -1 => Err(()),
        0 => Ok(Fork::Child),
        res => Ok(Fork::Parent(res)),
    }
}

/// Upon successful completion, the setsid() system call returns the value of the
/// process group ID of the new process group, which is the same as the process ID
/// of the calling process. If an error occurs, setsid() returns -1
pub fn setsid() -> Result<libc::pid_t, ()> {
    let res = unsafe { libc::setsid() };
    match res {
        -1 => Err(()),
        res => Ok(res),
    }
}

/// Upon successful completion, 0 shall be returned. Otherwise, -1 shall be
/// returned, the current working directory shall remain unchanged, and errno
/// shall be set to indicate the error.
pub fn chdir() -> Result<libc::c_int, ()> {
    let dir = CString::new("/").expect("CString::new failed");
    let res = unsafe { libc::chdir(dir.as_ptr()) };
    match res {
        -1 => Err(()),
        res => Ok(res),
    }
}

/// close file descriptors stdin,stdout,stderr
pub fn close_fd() -> Result<(), ()> {
    match unsafe { libc::close(0) } {
        -1 => Err(()),
        _ => match unsafe { libc::close(1) } {
            -1 => Err(()),
            _ => match unsafe { libc::close(2) } {
                -1 => Err(()),
                _ => Ok(()),
            },
        },
    }
}

/// The daemon function is for programs wishing to detach themselves from the
/// controlling terminal and run in the background as system daemons.
///
/// * `cd = true`, changes the current working directory to the root (`/`).
/// * `close = true`, will close standard input, standard output, and standard error
///
/// Example:
///
///```
///// The parent forks the child
///// The parent exits
///// The child calls setsid() to start a new session with no controlling terminals
///// The child forks a grandchild
///// The child exits
///// The grandchild is now the daemon
///use fork::{daemon, Fork};
///use std::process::Command;
///
///fn main() {
///    if let Ok(Fork::Child) = daemon(true, true) {
///        Command::new("sleep")
///            .arg("300")
///            .output()
///            .expect("failed to execute process");
///    }
///}
///```
pub fn daemon(cd: bool, close: bool) -> Result<Fork, ()> {
    match fork() {
        Ok(Fork::Parent(_)) => exit(0),
        Ok(Fork::Child) => setsid().and_then(|_| {
            if cd {
                chdir()?;
            }
            if close {
                close_fd()?;
            }
            fork()
        }),
        Err(n) => Err(n),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_fork() {
        if let Ok(Fork::Parent(child)) = fork() {
            assert!(child > 0);
        }
    }
}