running_process/broker/server/
service_def_loader.rs1use std::fs;
8use std::path::{Path, PathBuf};
9
10use prost::Message;
11
12use crate::daemon_registration::protocol::{BrokerIsolation, ServiceDefinition};
13use crate::daemon_registration::validation::{validate_service_name, validate_version};
14pub use crate::daemon_registration_common::service_definition::{
15 ensure_service_definition_dir, service_definition_dir, ServiceDefinitionError,
16 SERVICE_DEF_DIR_ENV,
17};
18
19pub const SERVICE_DEF_EXTENSION: &str = "servicedef";
21
22#[derive(Clone, Debug)]
24pub struct ServiceDefinitionLoader {
25 root: PathBuf,
26}
27
28impl ServiceDefinitionLoader {
29 pub fn new(root: impl Into<PathBuf>) -> Self {
31 Self { root: root.into() }
32 }
33
34 pub fn default_root() -> Self {
36 Self::new(service_definition_dir())
37 }
38
39 pub fn root(&self) -> &Path {
41 &self.root
42 }
43
44 pub fn load(&self, service_name: &str) -> Result<ServiceDefinition, ServiceDefinitionError> {
46 ensure_loadable_service_definition_dir(&self.root)?;
47 let path = service_definition_path(&self.root, service_name)?;
48 let bytes = fs::read(&path)?;
49 let definition = ServiceDefinition::decode(bytes.as_slice())?;
50 validate_service_definition_for_service(&definition, service_name)?;
51 Ok(definition)
52 }
53
54 pub fn reload(&self, service_name: &str) -> Result<ServiceDefinition, ServiceDefinitionError> {
56 self.load(service_name)
57 }
58
59 pub fn lookup_or_reload(
61 &self,
62 service_name: &str,
63 ) -> Result<ServiceDefinition, ServiceDefinitionError> {
64 self.load(service_name)
65 }
66}
67
68pub fn write_service_definition(
73 root: &Path,
74 definition: &ServiceDefinition,
75) -> Result<PathBuf, ServiceDefinitionError> {
76 ensure_service_definition_dir(root)?;
77 validate_service_definition_for_service(definition, &definition.service_name)?;
78 let path = service_definition_path(root, &definition.service_name)?;
79 fs::write(&path, definition.encode_to_vec())?;
80 Ok(path)
81}
82
83pub fn service_definition_path(
85 root: &Path,
86 service_name: &str,
87) -> Result<PathBuf, ServiceDefinitionError> {
88 validate_service_name(service_name)?;
89 Ok(root.join(format!("{service_name}.{SERVICE_DEF_EXTENSION}")))
90}
91
92pub fn validate_service_definition_for_service(
94 definition: &ServiceDefinition,
95 expected_service: &str,
96) -> Result<(), ServiceDefinitionError> {
97 validate_service_name(expected_service)?;
98 validate_service_name(&definition.service_name)?;
99 if definition.service_name != expected_service {
100 return Err(ServiceDefinitionError::ServiceNameMismatch {
101 requested: expected_service.into(),
102 actual: definition.service_name.clone(),
103 });
104 }
105 validate_absolute_path("binary_path", &definition.binary_path)?;
106 if !definition.per_version_binary_dir.is_empty() {
107 validate_absolute_path("per_version_binary_dir", &definition.per_version_binary_dir)?;
108 }
109 if !definition.min_version.is_empty() {
110 validate_version(&definition.min_version)?;
111 }
112 for version in &definition.version_allow_list {
113 validate_version(version)?;
114 }
115
116 match BrokerIsolation::try_from(definition.isolation) {
117 Ok(BrokerIsolation::PrivateBroker) | Ok(BrokerIsolation::SharedBroker) => {
118 if !definition.explicit_instance.is_empty() {
119 return Err(ServiceDefinitionError::InvalidIsolation {
120 reason: "explicit_instance must be empty unless isolation is EXPLICIT_INSTANCE",
121 });
122 }
123 }
124 Ok(BrokerIsolation::ExplicitInstance) => {
125 if definition.explicit_instance.is_empty() {
126 return Err(ServiceDefinitionError::InvalidIsolation {
127 reason: "EXPLICIT_INSTANCE requires explicit_instance",
128 });
129 }
130 validate_service_name(&definition.explicit_instance)?;
131 }
132 Err(_) => {
133 return Err(ServiceDefinitionError::InvalidIsolation {
134 reason: "unknown BrokerIsolation value",
135 });
136 }
137 }
138
139 Ok(())
140}
141
142fn ensure_loadable_service_definition_dir(path: &Path) -> Result<(), ServiceDefinitionError> {
143 if !crate::daemon_registration_common::secure_dir::private_dir_permissions_are_private(path)? {
144 return Err(ServiceDefinitionError::InsecureDirectory(
145 path.to_path_buf(),
146 ));
147 }
148 Ok(())
149}
150
151fn validate_absolute_path(field: &'static str, value: &str) -> Result<(), ServiceDefinitionError> {
152 if value.is_empty() {
153 return Err(ServiceDefinitionError::InvalidPath {
154 field,
155 path: value.into(),
156 reason: "must not be empty",
157 });
158 }
159 if !Path::new(value).is_absolute() {
160 return Err(ServiceDefinitionError::InvalidPath {
161 field,
162 path: value.into(),
163 reason: "must be absolute",
164 });
165 }
166 Ok(())
167}