Skip to main content

archivist_lib/
archivist.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::accessor::{ArchiveAccessorServer, BatchRetrievalTimeout};
6use crate::component_lifecycle;
7use crate::error::Error;
8use crate::events::router::{ConsumerConfig, EventRouter, ProducerConfig};
9use crate::events::sources::EventSource;
10use crate::events::types::*;
11use crate::inspect::repository::InspectRepository;
12use crate::inspect::servers::*;
13use crate::logs::debuglog::KernelDebugLog;
14use crate::logs::repository::{ComponentInitialInterest, LogsRepository};
15use crate::logs::serial::{SerialSink, launch_serial};
16use crate::logs::servers::*;
17use crate::pipeline::PipelineManager;
18use archivist_config::Config;
19use fidl_fuchsia_component_runtime as fruntime;
20use fidl_fuchsia_process_lifecycle::LifecycleRequestStream;
21use fuchsia_async as fasync;
22use fuchsia_component::runtime::DictionaryRouterReceiver;
23use fuchsia_component::server::{ServiceFs, ServiceObj};
24use fuchsia_inspect::component;
25use fuchsia_inspect::health::Reporter;
26use futures::channel::mpsc::unbounded;
27use futures::prelude::*;
28use log::{debug, error, info, warn};
29use std::path::PathBuf;
30use std::str::FromStr;
31use std::sync::Arc;
32
33/// Responsible for initializing an `Archivist` instance. Supports multiple configurations by
34/// either calling or not calling methods on the builder like `serve_test_controller_protocol`.
35pub struct Archivist {
36    /// Handles event routing between archivist parts.
37    event_router: EventRouter,
38
39    /// The diagnostics pipelines that have been installed.
40    pipeline_manager: PipelineManager,
41
42    /// The repository holding Inspect data.
43    _inspect_repository: Arc<InspectRepository>,
44
45    /// The repository holding active log connections.
46    logs_repository: Arc<LogsRepository>,
47
48    /// The server handling fuchsia.diagnostics.ArchiveAccessor
49    accessor_server: Arc<ArchiveAccessorServer>,
50
51    /// The server handling fuchsia.logger.Log
52    log_server: Arc<LogServer>,
53
54    /// The server handling fuchsia.diagnostics.system.LogFlush
55    flush_server: Arc<LogFlushServer>,
56
57    /// The server handling fuchsia.inspect.InspectSink
58    _inspect_sink_server: Arc<InspectSinkServer>,
59
60    /// Top level scope.
61    general_scope: fasync::Scope,
62
63    /// Tasks receiving external events from component manager.
64    incoming_events_scope: fasync::Scope,
65
66    /// All tasks for FIDL servers that ingest data into the Archivist must run in this scope.
67    servers_scope: fasync::Scope,
68
69    /// Freeze server.
70    freeze_server: LogFreezeServer,
71
72    /// The server handling fuchsia.diagnostics.Sample
73    sample_server: Arc<SampleServer>,
74}
75
76impl Archivist {
77    /// Creates new instance, sets up inspect and adds 'archive' directory to output folder.
78    /// Also installs `fuchsia.diagnostics.Archive` service.
79    /// Call `install_log_services`
80    pub async fn new(ring_buffer: ring_buffer::Reader, config: Config) -> Self {
81        let general_scope = fasync::Scope::new_with_name("general");
82        let servers_scope = fasync::Scope::new_with_name("servers");
83
84        // Initialize the pipelines that the archivist will expose.
85        let pipeline_manager = PipelineManager::new(
86            PathBuf::from(&config.pipelines_path),
87            component::inspector().root().create_child("pipelines"),
88            component::inspector().root().create_child("archive_accessor_stats"),
89            general_scope.new_child_with_name("pipeline_manager"),
90        )
91        .await;
92
93        // Initialize the core event router
94        let mut event_router =
95            EventRouter::new(component::inspector().root().create_child("events"));
96        let incoming_events_scope = general_scope.new_child_with_name("incoming_events");
97        Self::initialize_external_event_sources(&mut event_router, &incoming_events_scope).await;
98
99        let initial_interests =
100            config.component_initial_interests.into_iter().filter_map(|interest| {
101                ComponentInitialInterest::from_str(&interest)
102                    .map_err(|err| {
103                        warn!(err:?, invalid:% = interest; "Failed to load initial interest");
104                    })
105                    .ok()
106            });
107        let logs_repo = LogsRepository::new(
108            ring_buffer,
109            initial_interests,
110            component::inspector().root(),
111            general_scope.new_child_with_name("logs_repository"),
112        );
113        let (freeze_server, freeze_receiver) = LogFreezeServer::new();
114        let (flush_sender, flush_receiver) = unbounded();
115        if !config.allow_serial_logs.is_empty() {
116            let logs_repo_clone = Arc::clone(&logs_repo);
117            general_scope.spawn(async move {
118                launch_serial(
119                    config.allow_serial_logs,
120                    config.deny_serial_log_tags,
121                    logs_repo_clone,
122                    SerialSink,
123                    freeze_receiver,
124                    flush_receiver,
125                )
126                .await;
127            });
128        }
129        let inspect_repo = Arc::new(InspectRepository::new(
130            pipeline_manager.weak_pipelines(),
131            general_scope.new_child_with_name("inspect_repository"),
132        ));
133
134        let inspect_sink_server = Arc::new(InspectSinkServer::new(
135            Arc::clone(&inspect_repo),
136            servers_scope.new_child_with_name("InspectSink"),
137        ));
138
139        let sample_server = Arc::new(SampleServer::new(
140            Arc::clone(&inspect_repo),
141            zx::MonotonicDuration::from_seconds(
142                config.fuchsia_diagnostics_sample_min_period_seconds,
143            ),
144            servers_scope.new_child_with_name("SampleServer"),
145        ));
146
147        // Initialize our FIDL servers. This doesn't start serving yet.
148        let accessor_server = Arc::new(ArchiveAccessorServer::new(
149            Arc::clone(&inspect_repo),
150            Arc::clone(&logs_repo),
151            config.maximum_concurrent_snapshots_per_reader,
152            BatchRetrievalTimeout::from_seconds(config.per_component_batch_timeout_seconds),
153            servers_scope.new_child_with_name("ArchiveAccessor"),
154        ));
155
156        let flush_server = Arc::new(LogFlushServer::new(
157            servers_scope.new_child_with_name("LogFlush"),
158            flush_sender,
159        ));
160
161        let log_server = Arc::new(LogServer::new(
162            Arc::clone(&logs_repo),
163            servers_scope.new_child_with_name("Log"),
164        ));
165
166        // Initialize the external event providers containing incoming diagnostics directories and
167        // log sink connections.
168        event_router.add_consumer(ConsumerConfig {
169            consumer: &logs_repo,
170            events: vec![EventType::LogSinkRequested],
171        });
172        event_router.add_consumer(ConsumerConfig {
173            consumer: &inspect_sink_server,
174            events: vec![EventType::InspectSinkRequested],
175        });
176
177        // Drain klog and publish it to syslog.
178        if config.enable_klog {
179            match KernelDebugLog::new().await {
180                Ok(klog) => logs_repo.drain_debuglog(klog),
181                Err(err) => warn!(
182                    err:?;
183                    "Failed to start the kernel debug log reader. Klog won't be in syslog"
184                ),
185            };
186        }
187
188        // Start related services that should start once the Archivist has started.
189        for name in &config.bind_services {
190            info!("Connecting to service {}", name);
191            let (_local, remote) = zx::Channel::create();
192            if let Err(e) = fdio::service_connect(&format!("/svc/{name}"), remote) {
193                error!("Couldn't connect to service {}: {:?}", name, e);
194            }
195        }
196
197        Self {
198            accessor_server,
199            log_server,
200            flush_server,
201            event_router,
202            _inspect_sink_server: inspect_sink_server,
203            pipeline_manager,
204            _inspect_repository: inspect_repo,
205            logs_repository: logs_repo,
206            general_scope,
207            servers_scope,
208            freeze_server,
209            incoming_events_scope,
210            sample_server,
211        }
212    }
213
214    pub async fn initialize_external_event_sources(
215        event_router: &mut EventRouter,
216        scope: &fasync::Scope,
217    ) {
218        match EventSource::new("/events/log_sink_requested_event_stream").await {
219            Err(err) => warn!(err:?; "Failed to create event source for log sink requests"),
220            Ok(mut event_source) => {
221                event_router.add_producer(ProducerConfig {
222                    producer: &mut event_source,
223                    events: vec![EventType::LogSinkRequested],
224                });
225                scope.spawn(async move {
226                    // This should never exit.
227                    let _ = event_source.spawn().await;
228                });
229            }
230        }
231
232        match EventSource::new("/events/inspect_sink_requested_event_stream").await {
233            Err(err) => {
234                warn!(err:?; "Failed to create event source for InspectSink requests");
235            }
236            Ok(mut event_source) => {
237                event_router.add_producer(ProducerConfig {
238                    producer: &mut event_source,
239                    events: vec![EventType::InspectSinkRequested],
240                });
241                scope.spawn(async move {
242                    // This should never exit.
243                    let _ = event_source.spawn().await;
244                });
245            }
246        }
247    }
248
249    /// Run archivist to completion.
250    /// # Arguments:
251    /// * `outgoing_channel`- channel to serve outgoing directory on.
252    pub async fn run(
253        mut self,
254        mut fs: ServiceFs<ServiceObj<'static, ()>>,
255        is_embedded: bool,
256        request_stream: LifecycleRequestStream,
257    ) -> Result<(), Error> {
258        debug!("Running Archivist.");
259
260        // Start servicing all outgoing services.
261        self.serve_protocols(&mut fs).await;
262        let svc_task = self.general_scope.spawn(fs.collect::<()>());
263
264        let _inspect_server_task = inspect_runtime::publish(
265            component::inspector(),
266            inspect_runtime::PublishOptions::default(),
267        );
268
269        let Self {
270            _inspect_repository,
271            mut pipeline_manager,
272            logs_repository,
273            accessor_server: _accessor_server,
274            log_server: _log_server,
275            _inspect_sink_server,
276            general_scope,
277            incoming_events_scope,
278            freeze_server: _freeze_server,
279            servers_scope,
280            event_router,
281            flush_server: _flush_server,
282            sample_server: _sample_server,
283        } = self;
284
285        // Start ingesting events.
286        let (terminate_handle, drain_events_fut) = event_router
287            .start()
288            // panic: can only panic if we didn't register event producers and consumers correctly.
289            .expect("Failed to start event router");
290        general_scope.spawn(drain_events_fut);
291
292        let servers_scope_handle = servers_scope.to_handle();
293        general_scope.spawn(component_lifecycle::on_stop_request(request_stream, || async move {
294            terminate_handle.terminate().await;
295            debug!("Stopped ingesting new CapabilityRequested events");
296            incoming_events_scope.cancel().await;
297            debug!("Cancel all tasks currently executing in our event router");
298            servers_scope_handle.close();
299            logs_repository.stop_accepting_new_log_sinks();
300            debug!("Close any new connections to FIDL servers");
301            svc_task.abort().await;
302            pipeline_manager.cancel().await;
303            debug!("Stop allowing new connections through the incoming namespace.");
304            logs_repository.wait_for_termination().await;
305            debug!("All LogSink connections have finished");
306            servers_scope.join().await;
307            debug!("All servers stopped.");
308        }));
309        if is_embedded {
310            debug!("Entering core loop.");
311        } else {
312            info!("archivist: Entering core loop.");
313        }
314
315        component::health().set_ok();
316        general_scope.await;
317
318        Ok(())
319    }
320
321    async fn serve_protocols(&mut self, fs: &mut ServiceFs<ServiceObj<'static, ()>>) {
322        component::serve_inspect_stats();
323        let mut svc_dir = fs.dir("svc");
324
325        // Serve fuchsia.diagnostics.ArchiveAccessors backed by a pipeline.
326        let accessors_dict =
327            self.pipeline_manager.serve_pipelines(Arc::clone(&self.accessor_server)).await;
328
329        // Serve fuchsia.logger.Log
330        let log_server = Arc::clone(&self.log_server);
331        svc_dir.add_fidl_service(move |stream| {
332            debug!("fuchsia.logger.Log connection");
333            log_server.spawn(stream);
334        });
335
336        // Serve fuchsia.diagnostics.system.LogFlush
337        let flush_server = Arc::clone(&self.flush_server);
338        svc_dir.add_fidl_service(move |stream| {
339            debug!("fuchsia.diagnostics.system.LogFlush connection");
340            flush_server.spawn(stream);
341        });
342
343        // Install sample server to the accessors dictionary and start serving.
344        // Note that the await is not awaiting on the request stream or anything like that;
345        // the server spawns onto its owned Scope.
346        self.sample_server.install_and_serve(&accessors_dict).await;
347
348        let server = self.freeze_server.clone();
349        let scope = self.general_scope.clone();
350
351        svc_dir.add_fidl_service(move |stream| {
352            debug!("fuchsia.diagnostics.system.SerialLogControl connection");
353            let server = server.clone();
354            scope.spawn(async move {
355                let _ = server.handle_requests(stream).await;
356            });
357        });
358
359        // Server fuchsia.diagnostics.LogSettings
360        let log_settings_server = LogSettingsServer::new(
361            Arc::clone(&self.logs_repository),
362            // Don't create this in the servers scope. We don't care about this protocol for
363            // shutdown purposes.
364            self.general_scope.new_child_with_name("LogSettings"),
365        );
366        svc_dir.add_fidl_service(move |stream| {
367            debug!("fuchsia.diagnostics.LogSettings connection");
368            log_settings_server.spawn(stream);
369        });
370
371        // Serve fuchsia.component.runtime.DictionaryRouter
372        let router_scope =
373            self.servers_scope.new_child_with_name("fuchsia.component.runtime.DictionaryRouter");
374        svc_dir.add_fidl_service(move |stream: fruntime::DictionaryRouterRequestStream| {
375            let accessors_dict = accessors_dict.clone();
376            let router_receiver = DictionaryRouterReceiver::from(stream);
377            router_scope.spawn(router_receiver.handle_with(move |_request, _instance_token| {
378                debug!("Got route request for the dynamic accessors dictionary");
379                futures::future::ready(Ok(Some(accessors_dict.clone()))).boxed()
380            }));
381        });
382    }
383}