running_process/daemon_registration_common.rs
1//! Shared internals for the independently selectable registration writers.
2//!
3//! This is deliberately crate-private. The v1 and v2 public modules each
4//! expose their established public paths, while both retain one canonical
5//! validation, error, default-directory, and owner-private-directory
6//! implementation.
7
8#[path = "daemon_registration/validation.rs"]
9pub(crate) mod validation;
10
11#[path = "broker/secure_dir.rs"]
12pub(crate) mod secure_dir;
13
14pub(crate) mod service_definition {
15 use std::io;
16 use std::path::{Path, PathBuf};
17
18 use super::{secure_dir, validation::PipePathError};
19
20 /// Environment override for tests and development.
21 #[cfg(feature = "daemon-registration")]
22 pub const SERVICE_DEF_DIR_ENV: &str = "RUNNING_PROCESS_SERVICE_DEF_DIR";
23
24 /// Return the platform service-definition directory.
25 #[must_use]
26 pub fn service_definition_dir() -> PathBuf {
27 // An empty value is not a directory. It used to yield `PathBuf::from("")`,
28 // which resolves relative to the working directory -- so `…SERVICE_DEF_DIR=`
29 // silently moved service-definition lookup to wherever the broker happened
30 // to be started from, while `config --effective` still reported an override.
31 if let Some(path) = crate::env_vars::SERVICE_DEF_DIR.path() {
32 return path;
33 }
34
35 // Where a host keeps a product's configuration is a role `platform::fs`
36 // names; this used to spell out all three answers. Config is deliberately
37 // not the data root: Windows separates roaming settings from local data,
38 // and XDG gives configuration its own base directory.
39 crate::platform::fs::user_config_dir("running-process").join("services")
40 }
41
42 /// Ensure a service-definition directory exists with private permissions.
43 pub fn ensure_service_definition_dir(path: &Path) -> Result<(), ServiceDefinitionError> {
44 secure_dir::ensure_private_dir(path)?;
45 ensure_loadable_service_definition_dir(path)
46 }
47
48 /// Errors returned while loading or writing service-definition files.
49 #[derive(Debug, thiserror::Error)]
50 pub enum ServiceDefinitionError {
51 /// Filesystem operation failed.
52 #[error("service-definition I/O failed: {0}")]
53 Io(#[from] io::Error),
54 /// Protobuf decode failed.
55 #[error("service-definition protobuf decode failed: {0}")]
56 Decode(#[from] prost::DecodeError),
57 /// Name or version validation failed.
58 #[error(transparent)]
59 InvalidName(#[from] PipePathError),
60 /// Directory permissions are too broad.
61 #[error("service-definition directory has insecure permissions: {0}")]
62 InsecureDirectory(PathBuf),
63 /// File content did not match the requested service.
64 #[error("service-definition requested {requested:?} but file declares {actual:?}")]
65 ServiceNameMismatch {
66 /// Service name requested by the Hello path.
67 requested: String,
68 /// Service name decoded from disk.
69 actual: String,
70 },
71 /// A path field was empty or relative.
72 #[error("service-definition {field} is invalid: {path:?} ({reason})")]
73 InvalidPath {
74 /// Field name.
75 field: &'static str,
76 /// Field value.
77 path: String,
78 /// Why it failed validation.
79 reason: &'static str,
80 },
81 /// Isolation fields were inconsistent.
82 #[error("service-definition isolation is invalid: {reason}")]
83 InvalidIsolation {
84 /// Why it failed validation.
85 reason: &'static str,
86 },
87 }
88
89 fn ensure_loadable_service_definition_dir(path: &Path) -> Result<(), ServiceDefinitionError> {
90 if !secure_dir::private_dir_permissions_are_private(path)? {
91 return Err(ServiceDefinitionError::InsecureDirectory(
92 path.to_path_buf(),
93 ));
94 }
95 Ok(())
96 }
97}