1use std::{fmt, sync::Arc, time::Duration};
2
3pub use bestool_canopy as canopy;
4pub use bestool_canopy::Redacted;
5
6pub mod backup;
7mod child_confinement;
8pub mod commands;
9mod context;
10mod daemon;
11pub mod doctor;
12pub mod http_server;
13mod metrics;
14pub mod tasks;
15
16#[cfg(windows)]
17pub mod windows_service;
18
19pub use backup::{BackupRegistry, BackupRunner, BackupTask, RunningBackup};
20pub use context::InternalContext;
21pub use daemon::{RestartTrigger, run, run_with_shutdown};
22pub use tasks::{BackgroundTask, TaskContext, TaskEndpoint, TaskEndpointResponse};
23
24pub const VERSION: &str = env!("CARGO_PKG_VERSION");
26
27pub fn http_builder() -> reqwest::ClientBuilder {
31 reqwest::Client::builder().user_agent(concat!("bestool-alertd/", env!("CARGO_PKG_VERSION")))
32}
33
34pub fn http_client() -> reqwest::Client {
36 http_builder()
37 .build()
38 .expect("failed to build alertd HTTP client")
39}
40#[derive(Clone)]
42pub struct DaemonConfig {
43 pub pg_pool: Option<bestool_postgres::pool::PgPool>,
51
52 pub database_url: Option<String>,
54
55 pub device_key_pem: Option<Redacted<String>>,
61
62 pub no_server: bool,
64
65 pub server_addrs: Vec<std::net::SocketAddr>,
67
68 pub watchdog_timeout: Option<Duration>,
74
75 pub background_tasks: Vec<Arc<dyn BackgroundTask>>,
80
81 pub backups: Option<Arc<BackupRegistry>>,
84
85 pub metrics: Option<crate::doctor::DoctorMetricsHandle>,
89
90 pub binary_version: String,
95}
96
97impl fmt::Debug for DaemonConfig {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 f.debug_struct("DaemonConfig")
100 .field("database_url", &self.database_url)
101 .field("device_key_pem", &self.device_key_pem)
102 .field("binary_version", &self.binary_version)
103 .field("no_server", &self.no_server)
104 .field("server_addrs", &self.server_addrs)
105 .field("watchdog_timeout", &self.watchdog_timeout)
106 .field(
107 "background_tasks",
108 &self
109 .background_tasks
110 .iter()
111 .map(|t| t.name())
112 .collect::<Vec<_>>(),
113 )
114 .finish()
115 }
116}
117
118impl DaemonConfig {
119 pub fn new(
120 pg_pool: Option<bestool_postgres::pool::PgPool>,
121 database_url: Option<String>,
122 ) -> Self {
123 Self {
124 pg_pool,
125 database_url,
126 device_key_pem: None,
127 no_server: false,
128 server_addrs: Vec::new(),
129 watchdog_timeout: Some(Duration::from_secs(10 * 60)),
130 background_tasks: Vec::new(),
131 backups: None,
132 metrics: None,
133 binary_version: VERSION.to_string(),
136 }
137 }
138
139 pub fn with_binary_version(mut self, version: String) -> Self {
141 self.binary_version = version;
142 self
143 }
144
145 pub fn with_task(mut self, task: Arc<dyn BackgroundTask>) -> Self {
146 self.background_tasks.push(task);
147 self
148 }
149
150 pub fn with_backups(mut self, registry: Arc<BackupRegistry>) -> Self {
152 self.backups = Some(registry);
153 self
154 }
155
156 pub fn with_metrics_handle(mut self, metrics: crate::doctor::DoctorMetricsHandle) -> Self {
158 self.metrics = Some(metrics);
159 self
160 }
161
162 pub fn with_device_key_pem(mut self, pem: String) -> Self {
163 self.device_key_pem = Some(Redacted(pem));
164 self
165 }
166
167 pub fn with_no_server(mut self, no_server: bool) -> Self {
168 self.no_server = no_server;
169 self
170 }
171
172 pub fn with_server_addrs(mut self, server_addrs: Vec<std::net::SocketAddr>) -> Self {
173 self.server_addrs = server_addrs;
174 self
175 }
176
177 pub fn with_watchdog_timeout(mut self, watchdog_timeout: Option<Duration>) -> Self {
178 self.watchdog_timeout = watchdog_timeout;
179 self
180 }
181}
182
183pub(crate) struct LogError<'a>(pub &'a miette::Report);
185
186impl fmt::Display for LogError<'_> {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 use miette::ReportHandler;
189
190 let handler = miette::NarratableReportHandler::new();
191
192 if let Err(e) = handler.debug(self.0.as_ref(), f) {
193 write!(f, "{}: {}", self.0, e)
194 } else {
195 Ok(())
196 }
197 }
198}