1mod constants;
8mod fetcher;
9mod file_handler;
10mod inspect_server;
11mod persist_server;
12mod scheduler;
13
14use anyhow::{bail, Error};
15use argh::FromArgs;
16use fetcher::Fetcher;
17use fuchsia_async as fasync;
18use fuchsia_component::server::{ServiceFs, ServiceObj};
19use fuchsia_inspect::component;
20use fuchsia_inspect::health::Reporter;
21use futures::StreamExt;
22use log::*;
23use persist_server::PersistServer;
24use persistence_config::Config;
25use scheduler::Scheduler;
26use zx::BootInstant;
27
28pub const PROGRAM_NAME: &str = "persistence";
30pub const PERSIST_NODE_NAME: &str = "persist";
31pub const PUBLISHED_TIME_KEY: &str = "published";
33
34#[derive(FromArgs, Debug, PartialEq)]
36#[argh(subcommand, name = "persistence")]
37pub struct CommandLine {}
38
39macro_rules! on_error {
42 ($value:expr, $error_message:expr) => {
43 $value.or_else(|e| {
44 let message = format!($error_message, e);
45 warn!("{}", message);
46 bail!("{}", message)
47 })
48 };
49}
50
51pub async fn main(_args: CommandLine) -> Result<(), Error> {
52 info!("Starting Diagnostics Persistence Service service");
53 let mut health = component::health();
54 let config =
55 on_error!(persistence_config::load_configuration_files(), "Error loading configs: {}")?;
56 let inspector = component::inspector();
57 let _inspect_server_task =
58 inspect_runtime::publish(inspector, inspect_runtime::PublishOptions::default());
59
60 info!("Rotating directories");
61 file_handler::shuffle_at_boot();
62
63 let mut fs = ServiceFs::new();
64
65 let (fetch_requester, _fetcher_task) =
67 on_error!(Fetcher::new(&config), "Error initializing fetcher: {}")?;
68
69 let scheduler = Scheduler::new(fetch_requester, &config);
70
71 let scope = fasync::Scope::new();
73 let service_scope = scope.new_child_with_name("services");
74 spawn_persist_services(&config, &mut fs, scheduler, &service_scope);
75
76 fs.take_and_serve_directory_handle()?;
77 scope.spawn(fs.collect::<()>());
78
79 scope.spawn(async move {
84 info!("Waiting for post-boot update check...");
85 match fuchsia_component::client::connect_to_protocol::<fidl_fuchsia_update::ListenerMarker>(
86 ) {
87 Ok(proxy) => match proxy.wait_for_first_update_check_to_complete().await {
88 Ok(()) => {}
89 Err(e) => {
90 warn!(e:?; "Error waiting for first update check; not publishing.");
91 return;
92 }
93 },
94 Err(e) => {
95 warn!(
96 e:?;
97 "Unable to connect to fuchsia.update.Listener; will publish immediately."
98 );
99 }
100 }
101 info!("...Update check has completed; publishing previous boot data");
103 inspector.root().record_child(PERSIST_NODE_NAME, |node| {
104 inspect_server::serve_persisted_data(node);
105 health.set_ok();
106 info!("Diagnostics Persistence Service ready");
107 });
108 inspector.root().record_int(PUBLISHED_TIME_KEY, BootInstant::get().into_nanos());
109 });
110
111 scope.await;
112
113 Ok(())
114}
115
116fn spawn_persist_services(
119 config: &Config,
120 fs: &mut ServiceFs<ServiceObj<'static, ()>>,
121 scheduler: Scheduler,
122 scope: &fasync::Scope,
123) {
124 let mut started_persist_services = 0;
125
126 for (service_name, tags) in config {
127 info!("Launching persist service for {service_name}");
128 let unique_service_name =
129 format!("{}-{}", constants::PERSIST_SERVICE_NAME_PREFIX, service_name);
130
131 let server = PersistServer::create(
132 service_name.clone(),
133 tags.keys().cloned().collect(),
134 scheduler.clone(),
135 scope.new_child_with_name(&service_name.clone()),
137 );
138 fs.dir("svc").add_fidl_service_at(unique_service_name, move |stream| {
139 server.spawn(stream);
140 });
141 started_persist_services += 1;
142 }
143
144 info!("Started {} persist services", started_persist_services);
145}