use camino::Utf8PathBuf;
#[cfg(unix)]
use nix::unistd::geteuid;
use crate::error::{BootstrapError, BootstrapResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExecutionPrivileges {
Root,
Unprivileged,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExecutionMode {
InProcess,
Subprocess,
}
#[must_use]
pub fn detect_execution_privileges() -> ExecutionPrivileges {
#[cfg(unix)]
{
if geteuid().is_root() {
ExecutionPrivileges::Root
} else {
ExecutionPrivileges::Unprivileged
}
}
#[cfg(not(unix))]
{
ExecutionPrivileges::Unprivileged
}
}
pub(crate) const fn root_privilege_drop_supported() -> bool {
cfg!(all(unix, privileged_unix_platform))
}
pub(crate) fn unsupported_root_privilege_drop_error() -> BootstrapError {
BootstrapError::from(color_eyre::eyre::eyre!(
"privilege drop is not supported on this target; run without root privileges"
))
}
pub(super) fn determine_execution_mode(
privileges: ExecutionPrivileges,
worker_binary: Option<&Utf8PathBuf>,
) -> BootstrapResult<ExecutionMode> {
match privileges {
ExecutionPrivileges::Root => {
if !root_privilege_drop_supported() {
return Err(unsupported_root_privilege_drop_error());
}
if worker_binary.is_none() {
Err(BootstrapError::from(color_eyre::eyre::eyre!(
"PG_EMBEDDED_WORKER must be set when running with root privileges"
)))
} else {
Ok(ExecutionMode::Subprocess)
}
}
ExecutionPrivileges::Unprivileged => Ok(ExecutionMode::InProcess),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(all(unix, privileged_unix_platform))]
#[test]
fn determine_execution_mode_requires_worker_when_root() {
let err = determine_execution_mode(ExecutionPrivileges::Root, None)
.expect_err("root execution without worker must error");
let message = err.to_string();
assert!(
message.contains("PG_EMBEDDED_WORKER must be set"),
"unexpected error message: {message}",
);
}
#[cfg(all(unix, privileged_unix_platform))]
#[test]
fn determine_execution_mode_allows_subprocess_with_worker() {
let worker = Utf8PathBuf::from("/tmp/pg_worker");
let mode = determine_execution_mode(ExecutionPrivileges::Root, Some(&worker))
.expect("root execution with worker should succeed");
assert_eq!(mode, ExecutionMode::Subprocess);
}
#[cfg(unix)]
#[test]
fn determine_execution_mode_in_process_when_unprivileged() {
let mode = determine_execution_mode(ExecutionPrivileges::Unprivileged, None)
.expect("unprivileged execution should succeed");
assert_eq!(mode, ExecutionMode::InProcess);
}
#[cfg(unix)]
#[test]
fn determine_execution_mode_ignores_worker_when_unprivileged() {
let worker = Utf8PathBuf::from("/tmp/pg_worker");
let mode = determine_execution_mode(ExecutionPrivileges::Unprivileged, Some(&worker))
.expect("unprivileged execution should succeed with worker configured");
assert_eq!(mode, ExecutionMode::InProcess);
}
#[cfg(not(all(unix, privileged_unix_platform)))]
#[test]
fn determine_execution_mode_rejects_root_when_privilege_drop_is_unsupported() {
let worker = Utf8PathBuf::from("/tmp/pg_worker");
let err = determine_execution_mode(ExecutionPrivileges::Root, Some(&worker))
.expect_err("non-unix root execution should fail before worker dispatch");
let message = err.to_string();
assert!(
message.contains("privilege drop is not supported"),
"unexpected error message: {message}",
);
}
}