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}