pg_embedded_setup_unpriv/bootstrap/
mode.rs

1//! Detects execution privileges and selects the appropriate orchestration mode.
2
3use camino::Utf8PathBuf;
4
5use crate::error::{BootstrapError, BootstrapResult};
6
7#[cfg(unix)]
8use nix::unistd::geteuid;
9
10/// Represents the privileges the process is running with when bootstrapping `PostgreSQL`.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ExecutionPrivileges {
13    /// The process owns `root` privileges and must drop to `nobody` for filesystem work.
14    Root,
15    /// The process is already unprivileged, so bootstrap tasks run with the current UID/GID.
16    Unprivileged,
17}
18
19/// Selects how `PostgreSQL` lifecycle commands run when privileged execution is required.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum ExecutionMode {
22    /// Execute lifecycle commands directly within the current process.
23    ///
24    /// This mode is only appropriate when the process already runs without elevated privileges.
25    InProcess,
26    /// Delegate lifecycle commands to a helper subprocess executed with reduced privileges.
27    Subprocess,
28}
29
30/// Detects whether the process is running with root privileges.
31///
32/// # Examples
33/// ```
34/// use pg_embedded_setup_unpriv::{detect_execution_privileges, ExecutionPrivileges};
35///
36/// let privileges = detect_execution_privileges();
37/// let mode = match privileges {
38///     ExecutionPrivileges::Root => "subprocess",
39///     ExecutionPrivileges::Unprivileged => "in-process",
40/// };
41/// assert!(matches!(mode, "subprocess" | "in-process"));
42/// ```
43#[must_use]
44pub fn detect_execution_privileges() -> ExecutionPrivileges {
45    #[cfg(unix)]
46    {
47        if geteuid().is_root() {
48            ExecutionPrivileges::Root
49        } else {
50            ExecutionPrivileges::Unprivileged
51        }
52    }
53
54    #[cfg(not(unix))]
55    {
56        ExecutionPrivileges::Unprivileged
57    }
58}
59
60pub(super) fn determine_execution_mode(
61    privileges: ExecutionPrivileges,
62    worker_binary: Option<&Utf8PathBuf>,
63) -> BootstrapResult<ExecutionMode> {
64    #[cfg(unix)]
65    {
66        match privileges {
67            ExecutionPrivileges::Root => {
68                if worker_binary.is_none() {
69                    Err(BootstrapError::from(color_eyre::eyre::eyre!(
70                        "PG_EMBEDDED_WORKER must be set when running with root privileges"
71                    )))
72                } else {
73                    Ok(ExecutionMode::Subprocess)
74                }
75            }
76            ExecutionPrivileges::Unprivileged => Ok(ExecutionMode::InProcess),
77        }
78    }
79
80    #[cfg(not(unix))]
81    {
82        let _ = worker_binary;
83        let _ = privileges;
84        Ok(ExecutionMode::InProcess)
85    }
86}