pg_embedded_setup_unpriv/
lib.rs1mod bootstrap;
9mod cluster;
10mod env;
11mod error;
12mod fs;
13mod observability;
14#[cfg(all(
15 unix,
16 any(
17 target_os = "linux",
18 target_os = "android",
19 target_os = "freebsd",
20 target_os = "openbsd",
21 target_os = "dragonfly",
22 ),
23))]
24mod privileges;
25#[doc(hidden)]
26pub mod test_support;
27#[doc(hidden)]
28pub mod worker;
29pub(crate) mod worker_process;
30
31#[doc(hidden)]
32pub mod worker_process_test_api {
33 pub use crate::cluster::WorkerOperation;
36 use crate::worker_process;
37 pub use crate::worker_process::WorkerRequestArgs;
38
39 #[cfg(all(
40 unix,
41 any(
42 target_os = "linux",
43 target_os = "android",
44 target_os = "freebsd",
45 target_os = "openbsd",
46 target_os = "dragonfly",
47 ),
48 any(test, doc, feature = "privileged-tests"),
49 ))]
50 use crate::worker_process::PrivilegeDropGuard as InnerPrivilegeDropGuard;
51
52 pub struct WorkerRequest<'a>(worker_process::WorkerRequest<'a>);
57
58 impl<'a> WorkerRequest<'a> {
59 #[must_use]
85 pub const fn new(args: WorkerRequestArgs<'a>) -> Self {
86 Self(worker_process::WorkerRequest::new(args))
87 }
88
89 pub(crate) const fn inner(&self) -> &worker_process::WorkerRequest<'a> {
91 &self.0
92 }
93 }
94
95 pub fn run(request: &WorkerRequest<'_>) -> crate::BootstrapResult<()> {
97 worker_process::run(request.inner())
98 }
99
100 #[cfg(all(
102 unix,
103 any(
104 target_os = "linux",
105 target_os = "android",
106 target_os = "freebsd",
107 target_os = "openbsd",
108 target_os = "dragonfly",
109 ),
110 any(test, doc, feature = "privileged-tests"),
111 ))]
112 pub struct PrivilegeDropGuard {
113 _inner: InnerPrivilegeDropGuard,
114 }
115
116 #[cfg(all(
119 unix,
120 any(
121 target_os = "linux",
122 target_os = "android",
123 target_os = "freebsd",
124 target_os = "openbsd",
125 target_os = "dragonfly",
126 ),
127 any(test, doc, feature = "privileged-tests"),
128 ))]
129 #[must_use]
130 pub fn disable_privilege_drop_for_tests() -> PrivilegeDropGuard {
131 PrivilegeDropGuard {
132 _inner: worker_process::disable_privilege_drop_for_tests(),
133 }
134 }
135
136 #[must_use]
138 pub fn render_failure_for_tests(
139 context: &str,
140 output: &std::process::Output,
141 ) -> crate::BootstrapError {
142 worker_process::render_failure_for_tests(context, output)
143 }
144}
145
146#[doc(hidden)]
147pub use crate::env::ScopedEnv;
148pub use bootstrap::{
149 ExecutionMode, ExecutionPrivileges, TestBootstrapEnvironment, TestBootstrapSettings,
150 bootstrap_for_tests, detect_execution_privileges, find_timezone_dir, run,
151};
152#[cfg(any(doc, test, feature = "cluster-unit-tests", feature = "dev-worker"))]
153#[doc(hidden)]
154pub use cluster::WorkerInvoker;
155#[cfg(any(test, feature = "cluster-unit-tests"))]
156#[doc(hidden)]
157pub use cluster::WorkerOperation;
158pub use cluster::{
159 ConnectionMetadata, DatabaseName, TemporaryDatabase, TestCluster, TestClusterConnection,
160};
161#[doc(hidden)]
162pub use error::BootstrapResult;
163pub use error::PgEmbeddedError as Error;
164pub use error::{
165 BootstrapError, BootstrapErrorKind, PgEmbeddedError, PrivilegeError, PrivilegeResult, Result,
166};
167#[cfg(feature = "privileged-tests")]
168#[cfg(all(
169 unix,
170 any(
171 target_os = "linux",
172 target_os = "android",
173 target_os = "freebsd",
174 target_os = "openbsd",
175 target_os = "dragonfly",
176 ),
177))]
178#[expect(
179 deprecated,
180 reason = "with_temp_euid() remains exported for backward compatibility whilst deprecated"
181)]
182pub use privileges::with_temp_euid;
183#[cfg(all(
184 unix,
185 any(
186 target_os = "linux",
187 target_os = "android",
188 target_os = "freebsd",
189 target_os = "openbsd",
190 target_os = "dragonfly",
191 ),
192))]
193pub use privileges::{default_paths_for, make_data_dir_private, make_dir_accessible, nobody_uid};
194
195use color_eyre::eyre::{Context, eyre};
196use ortho_config::OrthoConfig;
197use postgresql_embedded::{Settings, VersionReq};
198use serde::{Deserialize, Serialize};
199
200use crate::error::{ConfigError, ConfigResult};
201use camino::Utf8PathBuf;
202use std::ffi::OsString;
203
204#[derive(Debug, Clone, Serialize, Deserialize, OrthoConfig, Default)]
206#[ortho_config(prefix = "PG")]
207pub struct PgEnvCfg {
216 pub version_req: Option<String>,
218 pub port: Option<u16>,
220 pub superuser: Option<String>,
222 pub password: Option<String>,
224 pub data_dir: Option<Utf8PathBuf>,
226 pub runtime_dir: Option<Utf8PathBuf>,
228 pub locale: Option<String>,
230 pub encoding: Option<String>,
232}
233
234impl PgEnvCfg {
235 pub fn load() -> ConfigResult<Self> {
241 let args = [OsString::from("pg-embedded-setup-unpriv")];
242 Self::load_from_iter(args).map_err(|err| ConfigError::from(eyre!(err)))
243 }
244
245 pub fn to_settings(&self) -> Result<Settings> {
256 let mut s = Settings::default();
257
258 self.apply_version(&mut s)?;
259 self.apply_connection(&mut s);
260 self.apply_paths(&mut s);
261 self.apply_locale(&mut s);
262
263 Ok(s)
264 }
265
266 fn apply_version(&self, settings: &mut Settings) -> ConfigResult<()> {
267 if let Some(ref vr) = self.version_req {
268 settings.version =
269 VersionReq::parse(vr).context("PG_VERSION_REQ invalid semver spec")?;
270 }
271 Ok(())
272 }
273
274 fn apply_connection(&self, settings: &mut Settings) {
275 if let Some(p) = self.port {
276 settings.port = p;
277 }
278 if let Some(ref u) = self.superuser {
279 settings.username.clone_from(u);
280 }
281 if let Some(ref pw) = self.password {
282 settings.password.clone_from(pw);
283 }
284 }
285
286 fn apply_paths(&self, settings: &mut Settings) {
287 if let Some(ref dir) = self.data_dir {
288 settings.data_dir = dir.clone().into_std_path_buf();
289 }
290 if let Some(ref dir) = self.runtime_dir {
291 settings.installation_dir = dir.clone().into_std_path_buf();
292 }
293 }
294
295 fn apply_locale(&self, settings: &mut Settings) {
301 if let Some(ref loc) = self.locale {
302 settings.configuration.insert("locale".into(), loc.clone());
303 }
304 if let Some(ref enc) = self.encoding {
305 settings
306 .configuration
307 .insert("encoding".into(), enc.clone());
308 }
309 }
310}