pg_embedded_setup_unpriv/bootstrap/
mode.rs1use camino::Utf8PathBuf;
4
5use crate::error::{BootstrapError, BootstrapResult};
6
7#[cfg(unix)]
8use nix::unistd::geteuid;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ExecutionPrivileges {
13 Root,
15 Unprivileged,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum ExecutionMode {
22 InProcess,
26 Subprocess,
28}
29
30#[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}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
93
94 #[cfg(unix)]
95 #[test]
96 fn determine_execution_mode_requires_worker_when_root() {
97 let err = determine_execution_mode(ExecutionPrivileges::Root, None)
98 .expect_err("root execution without worker must error");
99 let message = err.to_string();
100 assert!(
101 message.contains("PG_EMBEDDED_WORKER must be set"),
102 "unexpected error message: {message}",
103 );
104 }
105
106 #[cfg(unix)]
107 #[test]
108 fn determine_execution_mode_allows_subprocess_with_worker() {
109 let worker = Utf8PathBuf::from("/tmp/pg_worker");
110 let mode = determine_execution_mode(ExecutionPrivileges::Root, Some(&worker))
111 .expect("root execution with worker should succeed");
112 assert_eq!(mode, ExecutionMode::Subprocess);
113 }
114
115 #[cfg(unix)]
116 #[test]
117 fn determine_execution_mode_in_process_when_unprivileged() {
118 let mode = determine_execution_mode(ExecutionPrivileges::Unprivileged, None)
119 .expect("unprivileged execution should succeed");
120 assert_eq!(mode, ExecutionMode::InProcess);
121 }
122
123 #[cfg(unix)]
124 #[test]
125 fn determine_execution_mode_ignores_worker_when_unprivileged() {
126 let worker = Utf8PathBuf::from("/tmp/pg_worker");
127 let mode = determine_execution_mode(ExecutionPrivileges::Unprivileged, Some(&worker))
128 .expect("unprivileged execution should succeed with worker configured");
129 assert_eq!(mode, ExecutionMode::InProcess);
130 }
131
132 #[cfg(not(unix))]
133 #[test]
134 fn determine_execution_mode_defaults_to_in_process() {
135 let worker = Utf8PathBuf::from("/tmp/pg_worker");
136 let mode = determine_execution_mode(ExecutionPrivileges::Root, Some(&worker))
137 .expect("non-unix execution should succeed");
138 assert_eq!(mode, ExecutionMode::InProcess);
139 }
140}