1extern crate libc;
9
10use std::{io, env, ffi, path, process};
11
12#[derive(Debug)]
17pub enum Error {
18 FirstFork(Option<i32>),
19 SecondFork(Option<i32>),
20 Setsid(Option<i32>),
21 Chdir(io::Error),
22 FilenameToStr(path::PathBuf),
23 FilenameFFI(path::PathBuf, ffi::NulError),
24 OpenStd(path::PathBuf, Option<i32>),
25 Dup2(Option<i32>),
26}
27
28#[derive(Debug)]
29pub enum ChdirMode {
30 NoChdir,
31 ChdirRoot,
32}
33
34struct Redirected(libc::c_int);
35
36impl Drop for Redirected {
37 fn drop(&mut self) {
38 if self.0 >= 0 {
39 unsafe { libc::close(self.0) };
40 self.0 = -1;
41 }
42 }
43}
44
45fn to_path_buf<P>(path: &Option<P>) -> path::PathBuf where P: AsRef<path::Path> {
46 if let &Some(ref p) = path {
47 p.as_ref().to_owned()
48 } else {
49 let null: &path::Path = "/dev/null".as_ref();
50 null.to_path_buf()
51 }
52}
53
54fn redirect<P>(std: Option<P>) -> Result<Redirected, Error> where P: AsRef<path::Path> {
55 let filename = std.as_ref()
56 .map(|s| s.as_ref().to_str())
57 .unwrap_or(Some("/dev/null"))
58 .ok_or(Error::FilenameToStr(to_path_buf(&std)));
59 let path = try!(ffi::CString::new(try!(filename)).map_err(|e| Error::FilenameFFI(to_path_buf(&std), e)));
60
61 let fd = unsafe { libc::open(path.as_ptr(),
62 libc::O_CREAT | libc::O_WRONLY | libc::O_APPEND,
63 (libc::S_IRUSR | libc::S_IRGRP | libc::S_IWGRP | libc::S_IWUSR) as libc::c_uint) };
64 if fd < 0 {
65 Err(Error::OpenStd(to_path_buf(&std), io::Error::last_os_error().raw_os_error()))
66 } else {
67 Ok(Redirected(fd))
68 }
69}
70
71pub fn daemonize_redirect<PO, PE>(stdout: Option<PO>, stderr: Option<PE>, chdir: ChdirMode) -> Result<libc::pid_t, Error>
77 where PO: AsRef<path::Path>, PE: AsRef<path::Path>
78{
79 daemonize(try!(redirect(stdout)), try!(redirect(stderr)), chdir)
80}
81
82fn daemonize(mut stdout_fd: Redirected, mut stderr_fd: Redirected, chdir: ChdirMode) -> Result<libc::pid_t, Error> {
83 macro_rules! errno {
84 ($err:ident) => ({ return Err(Error::$err(io::Error::last_os_error().raw_os_error())) })
85 }
86
87 let pid = unsafe { libc::fork() };
88 if pid < 0 {
89 errno!(FirstFork)
90 } else if pid != 0 {
91 process::exit(0)
92 }
93
94 if unsafe { libc::setsid() } < 0 {
95 errno!(Setsid)
96 }
97
98 unsafe { libc::signal(libc::SIGHUP, libc::SIG_IGN); }
99 let pid = unsafe { libc::fork() };
100 if pid < 0 {
101 errno!(SecondFork)
102 } else if pid != 0 {
103 process::exit(0)
104 }
105
106 if let ChdirMode::ChdirRoot = chdir {
107 match env::set_current_dir("/") {
108 Ok(()) => (),
109 Err(e) => {
110 return Err(Error::Chdir(e))
111 },
112 }
113 }
114
115 if unsafe { libc::dup2(stdout_fd.0, libc::STDOUT_FILENO) } < 0 {
116 errno!(Dup2)
117 } else {
118 stdout_fd.0 = -1
119 }
120
121 if unsafe { libc::dup2(stderr_fd.0, libc::STDERR_FILENO) } < 0 {
122 errno!(Dup2)
123 } else {
124 stderr_fd.0 = -1
125 }
126
127 Ok(unsafe { libc::getpid() })
128}