pg_embedded_setup_unpriv/bootstrap/
mode.rs1use camino::Utf8PathBuf;
4#[cfg(unix)]
5use nix::unistd::geteuid;
6
7use crate::error::{BootstrapError, BootstrapResult};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ExecutionPrivileges {
12 Root,
14 Unprivileged,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum ExecutionMode {
21 InProcess,
25 Subprocess,
27}
28
29#[must_use]
43pub fn detect_execution_privileges() -> ExecutionPrivileges {
44 #[cfg(unix)]
45 {
46 if geteuid().is_root() {
47 ExecutionPrivileges::Root
48 } else {
49 ExecutionPrivileges::Unprivileged
50 }
51 }
52
53 #[cfg(not(unix))]
54 {
55 ExecutionPrivileges::Unprivileged
56 }
57}
58
59pub(crate) const fn root_privilege_drop_supported() -> bool {
60 cfg!(all(unix, privileged_unix_platform))
61}
62
63pub(crate) fn unsupported_root_privilege_drop_error() -> BootstrapError {
64 BootstrapError::from(color_eyre::eyre::eyre!(
65 "privilege drop is not supported on this target; run without root privileges"
66 ))
67}
68
69pub(super) fn determine_execution_mode(
70 privileges: ExecutionPrivileges,
71 worker_binary: Option<&Utf8PathBuf>,
72) -> BootstrapResult<ExecutionMode> {
73 match privileges {
74 ExecutionPrivileges::Root => {
75 if !root_privilege_drop_supported() {
76 return Err(unsupported_root_privilege_drop_error());
77 }
78 if worker_binary.is_none() {
79 Err(BootstrapError::from(color_eyre::eyre::eyre!(
80 "PG_EMBEDDED_WORKER must be set when running with root privileges"
81 )))
82 } else {
83 Ok(ExecutionMode::Subprocess)
84 }
85 }
86 ExecutionPrivileges::Unprivileged => Ok(ExecutionMode::InProcess),
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
95
96 #[cfg(all(unix, privileged_unix_platform))]
97 #[test]
98 fn determine_execution_mode_requires_worker_when_root() {
99 let err = determine_execution_mode(ExecutionPrivileges::Root, None)
100 .expect_err("root execution without worker must error");
101 let message = err.to_string();
102 assert!(
103 message.contains("PG_EMBEDDED_WORKER must be set"),
104 "unexpected error message: {message}",
105 );
106 }
107
108 #[cfg(all(unix, privileged_unix_platform))]
109 #[test]
110 fn determine_execution_mode_allows_subprocess_with_worker() {
111 let worker = Utf8PathBuf::from("/tmp/pg_worker");
112 let mode = determine_execution_mode(ExecutionPrivileges::Root, Some(&worker))
113 .expect("root execution with worker should succeed");
114 assert_eq!(mode, ExecutionMode::Subprocess);
115 }
116
117 #[cfg(unix)]
118 #[test]
119 fn determine_execution_mode_in_process_when_unprivileged() {
120 let mode = determine_execution_mode(ExecutionPrivileges::Unprivileged, None)
121 .expect("unprivileged execution should succeed");
122 assert_eq!(mode, ExecutionMode::InProcess);
123 }
124
125 #[cfg(unix)]
126 #[test]
127 fn determine_execution_mode_ignores_worker_when_unprivileged() {
128 let worker = Utf8PathBuf::from("/tmp/pg_worker");
129 let mode = determine_execution_mode(ExecutionPrivileges::Unprivileged, Some(&worker))
130 .expect("unprivileged execution should succeed with worker configured");
131 assert_eq!(mode, ExecutionMode::InProcess);
132 }
133
134 #[cfg(not(all(unix, privileged_unix_platform)))]
135 #[test]
136 fn determine_execution_mode_rejects_root_when_privilege_drop_is_unsupported() {
137 let worker = Utf8PathBuf::from("/tmp/pg_worker");
138 let err = determine_execution_mode(ExecutionPrivileges::Root, Some(&worker))
139 .expect_err("non-unix root execution should fail before worker dispatch");
140 let message = err.to_string();
141 assert!(
142 message.contains("privilege drop is not supported"),
143 "unexpected error message: {message}",
144 );
145 }
146}