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
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use libc;
use std::{os::unix::process::CommandExt, process::Command};
/// daemonize adds a pre_exec to call daemon(3) causing the spawned
/// process to be forked again and detached from the controlling
/// terminal.
///
/// The implementation does not violate any of the constraints documented on
/// `Command::pre_exec`, and this code is expected to be safe.
///
/// This code may however cause a process hang if not used appropriately. Reading on
/// the subtleties of CLOEXEC, CLOFORK and forking multi-threaded programs will
/// provide ample background reading. For the sake of safe use, callers should work
/// to ensure that uses of `daemonize` occur early in the program lifecycle, before
/// many threads have been spawned, libraries have been used or files have been
/// opened that may introduce CLOEXEC behaviors that could cause EXTBUSY outcomes in
/// a Linux environment.
pub fn daemonize(c: &mut Command) -> &mut Command {
unsafe {
c.pre_exec(|| {
// daemonize(3) is deprecated on macOS 10.15. The replacement is not
// yet clear, we may want to replace this with a manual double fork
// setsid, etc.
#[allow(deprecated)]
// First argument: chdir(/)
// Second argument: do not close stdio (we use stdio to write to the daemon log file)
match libc::daemon(0, 1) {
0 => Ok(()),
x => Err(std::io::Error::from_raw_os_error(x)),
}
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_daemonize() {
let started = std::time::Instant::now();
// TODO(raggi): this technically leaks a sleep process, which is
// not ideal, but the much better approach would be a
// significant amount of work, as we'd really want a program
// that will wait for a signal on some other channel (such as a
// unix socket) and otherwise linger as a daemon. If we had
// that, we could then check the ppid and assert that daemon(3)
// really did the work we're expecting it to. As that would
// involve specific subprograms, finding those, and so on, it is
// likely beyond ROI for this test coverage, which aims to just
// prove that the immediate spawn() succeeded was detached from
// the program in question. There is a risk that this
// implementation passes if sleep(1) is not found, which is also
// not ideal.
let mut child = daemonize(Command::new("sleep").arg("10")).spawn().expect("child spawned");
child.wait().expect("child exited successfully");
assert!(started.elapsed() < std::time::Duration::from_secs(10));
}
}