// Copyright 2022 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34//! Provides [fork_process] to fork a process.56#![deny(missing_docs)]78use std::ffi::CString;9use std::mem::ManuallyDrop;10use std::os::unix::process::ExitStatusExt;11use std::process;1213use base::error;14use base::linux::wait_for_pid;15use base::Pid;16use base::RawDescriptor;17#[cfg(feature = "seccomp_trace")]18use log::debug;19use log::warn;20use minijail::Minijail;2122/// Child represents the forked process.23pub struct Child {24/// The pid of the child process.25pub pid: Pid,26}2728impl Child {29/// Wait for the child process exit using `waitpid(2)`.30pub fn wait(self) -> base::Result<u8> {31// Suppress warning from the drop().32let pid = self.into_pid();33let (_, status) = wait_for_pid(pid, 0)?;34if let Some(exit_code) = status.code() {35Ok(exit_code as u8)36} else if let Some(signal) = status.signal() {37let exit_code = if signal >= 128 {38warn!("wait for child: unexpected signal({:?})", signal);3925540} else {41128 + signal as u842};43Ok(exit_code)44} else {45unreachable!("waitpid with option 0 only waits for exited and signaled status");46}47}4849/// Convert [Child] into [Pid].50///51/// If [Child] is dropped without `Child::wait()`, it logs warning message. Users who wait52/// processes in other ways should suppress the warning by unwrapping [Child] into [Pid].53///54/// The caller of this method now owns the process and is responsible for managing the55/// termination of the process.56pub fn into_pid(self) -> Pid {57let pid = self.pid;58// Suppress warning from the drop().59let _ = ManuallyDrop::new(self);60pid61}62}6364impl Drop for Child {65fn drop(&mut self) {66warn!("the child process have not been waited.: {}", self.pid);67}68}6970/// Forks this process using [Minijail] and calls a closure in the new process.71///72/// After `post_fork_cb` returns, the new process exits with `0` code. If `post_fork_cb` panics, the73/// new process exits with `101` code.74///75/// This function never returns in the forked process.76///77/// # Arguments78///79/// * `jail` - [Minijail] instance to fork.80/// * `keep_rds` - [RawDescriptor]s to be kept in the forked process. other file descriptors will be81/// closed by [Minijail] in the forked process.82/// * `debug_label` - (optional) thread name. this will be trimmed to 15 charactors.83/// * `post_fork_cb` - Callback to run in the new process.84pub fn fork_process<F>(85jail: Minijail,86mut keep_rds: Vec<RawDescriptor>,87debug_label: Option<String>,88post_fork_cb: F,89) -> minijail::Result<Child>90where91F: FnOnce(),92{93// Deduplicate the FDs since minijail expects this.94keep_rds.sort_unstable();95keep_rds.dedup();9697// SAFETY:98// Safe because the program is still single threaded.99// We own the jail object and nobody else will try to reuse it.100let pid = match unsafe { jail.fork(Some(&keep_rds)) }? {1010 => {102struct ExitGuard;103impl Drop for ExitGuard {104fn drop(&mut self) {105// Rust exits with 101 when panics.106process::exit(101);107}108}109// Prevents a panic in post_fork_cb from bypassing the process::exit.110let _exit_guard = ExitGuard {};111112if let Some(debug_label) = debug_label {113// pthread_setname_np() limit on Linux114const MAX_THREAD_LABEL_LEN: usize = 15;115let debug_label_trimmed = &debug_label.as_bytes()116[..std::cmp::min(MAX_THREAD_LABEL_LEN, debug_label.len())];117match CString::new(debug_label_trimmed) {118Ok(thread_name) => {119// SAFETY:120// Safe because thread_name is a valid pointer and setting name of this121// thread should be safe.122let _ = unsafe {123libc::pthread_setname_np(libc::pthread_self(), thread_name.as_ptr())124};125}126Err(e) => {127error!("failed to compile thread name: {:?}", e);128}129}130}131132post_fork_cb();133// ! Never returns134process::exit(0);135}136pid => pid,137};138#[cfg(feature = "seccomp_trace")]139debug!(140// Proxy and swap devices fork here141"seccomp_trace {{\"event\": \"minijail_fork\", \"pid\": \"{}\", \"name\": \"{}\", \"jail_addr\": \"0x{:x}\"}}",142pid,143match debug_label {144Some(debug_label) => debug_label,145None => "process.rs: no debug label".to_owned(),146},147// Can't use safe wrapper because jail crate depends on base148// SAFETY:149// Safe because it's only doing a read within bound checked by static assert150unsafe {*(&jail as *const Minijail as *const usize)}151);152Ok(Child { pid })153}154155156