fork-0.1.23/.cargo_vcs_info.json0000644000000001360000000000100120600ustar { "git": { "sha1": "d7e925408aeccd020420f5f9e231ff48d8a27164" }, "path_in_vcs": "" }fork-0.1.23/.github/FUNDING.yml000064400000000000000000000000161046102023000140220ustar 00000000000000github: nbari fork-0.1.23/.github/actions-rs/grcov.yml000064400000000000000000000000571046102023000161370ustar 00000000000000--- output-type: lcov output-file: ./lcov.info fork-0.1.23/.github/workflows/.codespellrc000064400000000000000000000000461046102023000165450ustar 00000000000000[codespell] ignore-words-list = crate fork-0.1.23/.github/workflows/build.yml000064400000000000000000000023251046102023000160710ustar 00000000000000--- name: Build on: push: tags: - '*' workflow_dispatch: permissions: contents: write jobs: test: uses: ./.github/workflows/test.yml build: name: Test build runs-on: ${{ matrix.os }} needs: test strategy: matrix: include: - build: linux os: ubuntu-latest target: x86_64-unknown-linux-musl - build: macos os: macos-latest target: x86_64-apple-darwin steps: - name: Checkout uses: actions/checkout@v4 - name: Get the release version from the tag run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - name: Install Rust uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.target }} - name: Build run: | cargo build --release --target ${{ matrix.target }} publish: name: Publish runs-on: ubuntu-latest needs: - build steps: - name: Checkout sources uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable - run: cargo publish --token ${CRATES_TOKEN} env: CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }} fork-0.1.23/.github/workflows/test.yml000064400000000000000000000035161046102023000157540ustar 00000000000000--- name: Test on: workflow_call: pull_request: branches: - '*' jobs: format: name: Format runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Format run: cargo fmt --all -- --check lint: name: Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Clippy run: cargo clippy -- -D clippy::all -D clippy::nursery -D warnings check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Check run: cargo check test: name: Test strategy: matrix: os: - ubuntu-latest - macOS-latest rust: - stable runs-on: ${{ matrix.os }} needs: - format - lint - check steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: test run: cargo test coverage: name: Coverage runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - name: Run tests run: cargo test --verbose env: CARGO_INCREMENTAL: '0' RUSTFLAGS: -Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests RUSTDOCFLAGS: -Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests - name: rust-grcov uses: actions-rs/grcov@v0.1 - name: Upload to codecov.io uses: codecov/codecov-action@v3 - name: Coveralls GitHub Action uses: coverallsapp/github-action@v2 fork-0.1.23/.gitignore000064400000000000000000000000371046102023000126400ustar 00000000000000/target/ Cargo.lock **/*.rs.bk fork-0.1.23/.travis.yml000064400000000000000000000004671046102023000127700ustar 00000000000000language: rust rust: - stable - beta os: - linux - osx before_script: - rustup component add clippy script: - cargo clippy --all-targets --all-features -- -D clippy::pedantic -D clippy::nursery - cargo build --all --all-targets - cargo test --all notifications: email: on_sucess: never fork-0.1.23/Cargo.toml0000644000000020300000000000100100510ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "fork" version = "0.1.23" authors = ["Nicolas Embriz "] description = "Library for creating a new process detached from the controlling terminal (daemon)" homepage = "https://docs.rs/fork/latest/fork/" documentation = "https://docs.rs/fork/latest/fork/" readme = "README.md" keywords = [ "fork", "setsid", "daemon", "process", "daemonize", ] categories = [ "command-line-utilities", "os", ] license = "BSD-3-Clause" repository = "https://github.com/immortal/fork" [dependencies.libc] version = "0.2" fork-0.1.23/Cargo.toml.orig000064400000000000000000000010331046102023000135340ustar 00000000000000[package] name = "fork" version = "0.1.23" authors = ["Nicolas Embriz "] description = "Library for creating a new process detached from the controlling terminal (daemon)" documentation = "https://docs.rs/fork/latest/fork/" homepage = "https://docs.rs/fork/latest/fork/" repository = "https://github.com/immortal/fork" readme = "README.md" keywords = ["fork", "setsid", "daemon", "process", "daemonize"] categories = ["command-line-utilities", "os"] license = "BSD-3-Clause" edition = "2021" [dependencies] libc = "0.2" fork-0.1.23/LICENSE000064400000000000000000000027541046102023000116650ustar 00000000000000BSD 3-Clause License Copyright (c) 2019, immortal All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. fork-0.1.23/README.md000064400000000000000000000042371046102023000121350ustar 00000000000000# fork [![crates.io](https://img.shields.io/crates/v/fork.svg)](https://crates.io/crates/fork) [![Build](https://github.com/immortal/fork/actions/workflows/build.yml/badge.svg)](https://github.com/immortal/fork/actions/workflows/build.yml) [![codecov](https://codecov.io/gh/immortal/fork/graph/badge.svg?token=LHZW56OC10)](https://codecov.io/gh/immortal/fork) [![docs](https://docs.rs/fork/badge.svg)](https://docs.rs/fork) Library for creating a new process detached from the controlling terminal (daemon). ## Why? - Minimal library to daemonize, fork, double-fork a process. - [daemon(3)](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/daemon.3.html) has been deprecated in MacOSX 10.5, by using `fork` and `setsid` syscalls, new methods can be created to achieve the same goal. Example: Create a new test project: $ cargo new --bin fork > To install `cargo` use: `curl https://sh.rustup.rs -sSf | sh` Edit `fork/Cargo.toml` and add to `[dependencies]`: fork = "0.1" Add the following code to `fork/main.rs` ```rs use fork::{daemon, Fork}; use std::process::Command; fn main() { if let Ok(Fork::Child) = daemon(false, false) { Command::new("sleep") .arg("300") .output() .expect("failed to execute process"); } } ``` > If using `daemon(false, false)`,it will `chdir` to `/` and close the standard input, standard output, and standard error file descriptors. Test running: $ cargo run Use `ps` to check the process, for example: $ ps -axo ppid,pid,pgid,sess,tty,tpgid,stat,uid,%mem,%cpu,command, | egrep "fork|sleep|PID" > `egrep` is used to show the `ps` headers Output should be something like: ```pre PPID PID PGID SESS TTY TPGID STAT UID %MEM %CPU COMMAND 1 48738 48737 0 ?? 0 S 501 0.0 0.0 target/debug/fork 48738 48753 48737 0 ?? 0 S 501 0.0 0.0 sleep 300 ``` * `PPID == 1` that's the parent process * `TTY = ??` no controlling terminal * new `PGID = 48737` 1 - root (init/launchd) \-- 48738 fork PGID - 48737 \--- 48753 sleep PGID - 48737 fork-0.1.23/src/lib.rs000064400000000000000000000127301046102023000125560ustar 00000000000000//! Library for creating a new process detached from the controlling terminal (daemon). //! //! Example: //! ``` //!use fork::{daemon, Fork}; //!use std::process::Command; //! //!if let Ok(Fork::Child) = daemon(false, false) { //! Command::new("sleep") //! .arg("3") //! .output() //! .expect("failed to execute process"); //!} //!``` use std::ffi::CString; use std::process::exit; /// Fork result pub enum Fork { Parent(libc::pid_t), Child, } /// Change dir to `/` [see chdir(2)](https://www.freebsd.org/cgi/man.cgi?query=chdir&sektion=2) /// /// 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. /// /// Example: /// ///``` ///use fork::chdir; ///use std::env; /// ///match chdir() { /// Ok(_) => { /// let path = env::current_dir().expect("failed current_dir"); /// assert_eq!(Some("/"), path.to_str()); /// } /// _ => panic!(), ///} ///``` /// /// # Errors /// returns `-1` if error pub fn chdir() -> Result { let dir = CString::new("/").expect("CString::new failed"); let res = unsafe { libc::chdir(dir.as_ptr()) }; match res { -1 => Err(-1), res => Ok(res), } } /// Close file descriptors stdin,stdout,stderr /// /// # Errors /// returns `-1` if error pub fn close_fd() -> Result<(), i32> { match unsafe { libc::close(0) } { -1 => Err(-1), _ => match unsafe { libc::close(1) } { -1 => Err(-1), _ => match unsafe { libc::close(2) } { -1 => Err(-1), _ => Ok(()), }, }, } } /// Create a new child process [see fork(2)](https://www.freebsd.org/cgi/man.cgi?fork) /// /// 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. /// /// Example: /// /// ``` ///use fork::{fork, Fork}; /// ///match fork() { /// Ok(Fork::Parent(child)) => { /// println!("Continuing execution in parent process, new child has pid: {}", child); /// } /// Ok(Fork::Child) => println!("I'm a new child process"), /// Err(_) => println!("Fork failed"), ///} ///``` /// This will print something like the following (order indeterministic). /// /// ```text /// Continuing execution in parent process, new child has pid: 1234 /// I'm a new child process /// ``` /// /// The thing to note is that you end up with two processes continuing execution /// immediately after the fork call but with different match arms. /// /// # [`nix::unistd::fork`](https://docs.rs/nix/0.15.0/nix/unistd/fn.fork.html) /// /// The example has been taken from the [`nix::unistd::fork`](https://docs.rs/nix/0.15.0/nix/unistd/fn.fork.html), /// please check the [Safety](https://docs.rs/nix/0.15.0/nix/unistd/fn.fork.html#safety) section /// /// # Errors /// returns `-1` if error pub fn fork() -> Result { let res = unsafe { libc::fork() }; match res { -1 => Err(-1), 0 => Ok(Fork::Child), res => Ok(Fork::Parent(res)), } } /// Create session and set process group ID [see setsid(2)](https://www.freebsd.org/cgi/man.cgi?setsid) /// /// 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 /// /// # Errors /// returns `-1` if error pub fn setsid() -> Result { let res = unsafe { libc::setsid() }; match res { -1 => Err(-1), res => Ok(res), } } /// The process group of the current process [see getgrp(2)](https://www.freebsd.org/cgi/man.cgi?query=getpgrp) /// /// # Errors /// returns `-1` if error pub fn getpgrp() -> Result { let res = unsafe { libc::getpgrp() }; match res { -1 => Err(-1), res => Ok(res), } } /// The daemon function is for programs wishing to detach themselves from the /// controlling terminal and run in the background as system daemons. /// /// * `nochdir = false`, changes the current working directory to the root (`/`). /// * `noclose = false`, will close standard input, standard output, and standard error /// /// # Errors /// If an error occurs, returns -1 /// /// 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; /// ///if let Ok(Fork::Child) = daemon(false, false) { /// Command::new("sleep") /// .arg("3") /// .output() /// .expect("failed to execute process"); ///} ///``` pub fn daemon(nochdir: bool, noclose: bool) -> Result { match fork() { Ok(Fork::Parent(_)) => exit(0), Ok(Fork::Child) => setsid().and_then(|_| { if !nochdir { chdir()?; } if !noclose { close_fd()?; } fork() }), Err(n) => Err(n), } } #[cfg(test)] mod tests { use super::{fork, Fork}; #[test] fn test_fork() { if let Ok(Fork::Parent(child)) = fork() { assert!(child > 0); } } }