// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan // PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. #![deny(missing_docs)] //! This file implements a high level wrapper for daemonize //! //! # Daemonize Introduction //! //! [daemonize](https://linux.die.net/man/1/daemonize) to runs a command as a //! Unix daemonize. A daemon is a process that executes in the background either //! waiting for some event to occur, or waiting to perform some specified task //! on a periodic basis. A typical daemon program will: //! 1. Close all open file descriptors(especially standard input, standard //! output and standard error). //! 2. Change its working directory to the root filesystem, to ensure that it //! doesn't tie up another filesystem and prevent it from being unmounted. //! 3. Reset its umask value. //! 4. Run in the background(i.e., fork). //! 5. Ignore all terminal I/O signals. //! 6. Disassociate from the control terminal. //! 7. Disassociate from its process group, to insulate itself from signals //! sent to the process group. //! 8. Handle any `SIGCLD` signals. use std::cmp::Ordering; use std::fs::{File, OpenOptions}; use std::io::prelude::*; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::RawFd; use std::path::Path; use std::process::exit; use crate::errors::{ErrorKind, Result}; /// Write process id to pid file. fn create_pid_file(path: &str) -> Result<()> { let pid: u32 = std::process::id(); let mut pid_file: File = OpenOptions::new() .write(true) .create(true) .mode(0o600) .open(path)?; write!(pid_file, "{}", pid)?; Ok(()) } /// [fork(2)](https://man7.org/linux/man-pages/man2/fork.2.html) /// fork() creates a new process by duplicating the calling process. The new /// process is referred to as the child process. The calling process is referred /// to as the parent process. /// **libc::fork()** may have three kinds ret: /// if ret > 0 : current process is parent process, it's not expected, so exit /// if ret < 0 : error occurred in fork() /// if ret = 0 : current process is child process, it's expected /// /// # Errors /// /// `DaemonFork` Error, the ret of `libc::fork()` is less than zero. fn fork() -> Result<()> { let ret = unsafe { libc::fork() }; match ret.cmp(&0) { Ordering::Less => Err(ErrorKind::DaemonFork.into()), Ordering::Greater => exit(0), Ordering::Equal => Ok(()), } } /// [setsid(2)](https://man7.org/linux/man-pages/man2/setsid.2.html) /// setsid() creates a new session if the calling process is not a process group /// leader. The calling process is the leader of the new session. The calling /// process also becomes the process group leader or a new process group in the /// session. /// The calling process will be the only process in the new process group and in /// the new session. New session has no controlling termimal. /// /// # Errors /// /// `DaemonSetsid` Error, the ret of `libc::setsid()` is -1 fn set_sid() -> Result<()> { let ret = unsafe { libc::setsid() }; if ret == -1 { Err(ErrorKind::DaemonSetsid.into()) } else { Ok(()) } } /// Redirect stdio to `/dev/null`. /// /// Use [dup(2)](https://man7.org/linux/man-pages/man2/dup.2.html) /// dup2(oldfd, newfd) creates a copy of the file descriptor `oldfd`, uses the /// file descriptor number specified in `newfd`. If the file descriptor `newfd` /// was previously open, it is silently closed before being reused. This /// function use `dup2` to redirect file descriptor use to `/dev/null`. /// /// # Errors /// /// `DaemonRedirectStdio` Error, the ret of `libc::open()`, `libc::dup2()`, /// `libc::close()`is -1 fn redirect_stdio(fd: RawFd) -> Result<()> { unsafe { let devnull_fd = libc::open(b"/dev/null\0" as *const [u8; 10] as _, libc::O_RDWR); if devnull_fd == -1 { return Err(ErrorKind::DaemonRedirectStdio.into()); } if libc::dup2(devnull_fd, fd) == -1 { return Err(ErrorKind::DaemonRedirectStdio.into()); } if libc::close(devnull_fd) == -1 { return Err(ErrorKind::DaemonRedirectStdio.into()); } } Ok(()) } /// Daemonize a process. /// /// # Arguments /// /// * `pid_file` - Path where will create pid file. /// /// # Notes /// This function do five things to daemonize a process: /// 1. Reset its umask value. /// 2. Run in the background use fork. /// 3. Ignore all terminal I/O signals. /// 4. Disassociate from the control terminal. /// 5. Write pid to pidfile. pub fn daemonize(pid_file: Option<String>) -> Result<()> { if let Some(path) = pid_file.as_ref() { if Path::new(path).exists() { return Err(ErrorKind::PidFileExist.into()); } } // The first fork make parent process quit, child process inherit parent's // session ID and have a new process ID. It can guarantee child // process will not be the first process in a session. fork()?; // Create a new session for process. Now parent process quit will not // influence stratovirt process. But stratovirt becomes the first process in // new section. set_sid()?; // The second fork make stratovirt run as daemonize process. It won't be the // first process in this session and never get terminal control. fork()?; // Redirect stdio to `/dev/null`. redirect_stdio(libc::STDIN_FILENO)?; redirect_stdio(libc::STDOUT_FILENO)?; redirect_stdio(libc::STDERR_FILENO)?; // Now can record PID to file. It won't be changed again in stratovirt's // lifetime. if let Some(path) = pid_file { create_pid_file(&path)?; } Ok(()) }