Skip to main content

archivist_lib/logs/servers/
log_freeze.rs

1// Copyright 2025 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 anyhow::Error;
6use fidl_fuchsia_diagnostics_system as ftarget;
7use futures::StreamExt;
8use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded};
9use futures::channel::oneshot;
10use log::warn;
11
12#[derive(Clone)]
13pub struct LogFreezeServer {
14    freezer: UnboundedSender<oneshot::Sender<zx::EventPair>>,
15}
16
17impl LogFreezeServer {
18    pub fn new() -> (Self, UnboundedReceiver<oneshot::Sender<zx::EventPair>>) {
19        let (freezer, rx) = unbounded();
20        (Self { freezer }, rx)
21    }
22
23    /// Actually handle the FIDL request. This handles only a single request, then exits.
24    pub async fn handle_requests(
25        &self,
26        mut stream: ftarget::SerialLogControlRequestStream,
27    ) -> Result<(), Error> {
28        while let Some(request) = stream.next().await {
29            match request? {
30                fidl_fuchsia_diagnostics_system::SerialLogControlRequest::FreezeSerialForwarding { responder } => {
31                    let (tx, rx) = oneshot::channel();
32                    self.freezer.unbounded_send(tx)?;
33                    // Ignore errors.
34                    let _ = responder.send(rx.await?);
35                },
36                ftarget::SerialLogControlRequest::_UnknownMethod {
37                                            ordinal,
38                                            method_type,
39                                            control_handle,
40                                            ..
41                                        } => {
42                                            warn!(ordinal, method_type:?; "Unknown request. Closing connection");
43                                            control_handle.shutdown_with_epitaph(zx::Status::UNAVAILABLE);
44                                        }
45            }
46        }
47        Ok(())
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54    use assert_matches::assert_matches;
55    use fidl::endpoints::{Proxy, create_proxy_and_stream};
56
57    #[fuchsia::test]
58    async fn freeze_serial_forwarding_success() {
59        let (server, mut rx) = LogFreezeServer::new();
60        let (proxy, stream) = create_proxy_and_stream::<ftarget::SerialLogControlMarker>();
61
62        let server_task = fuchsia_async::Task::spawn(async move {
63            server.handle_requests(stream).await.unwrap();
64        });
65
66        let freeze_fut = proxy.freeze_serial_forwarding();
67
68        let (p1, p2) = zx::EventPair::create();
69        let tx = rx.next().await.expect("received freezer channel");
70        tx.send(p1).expect("send eventpair to server");
71
72        let received_p2 = freeze_fut.await.expect("freeze response received");
73        assert_eq!(p2.basic_info().unwrap().related_koid, received_p2.koid().unwrap());
74
75        drop(proxy);
76        server_task.await;
77    }
78
79    #[fuchsia::test]
80    async fn unknown_method_closes_with_unavailable_epitaph() {
81        let (server, _rx) = LogFreezeServer::new();
82        let (proxy, stream) = create_proxy_and_stream::<ftarget::SerialLogControlMarker>();
83
84        let server_task = fuchsia_async::Task::spawn(async move {
85            server.handle_requests(stream).await.unwrap();
86        });
87
88        // Send a flexible one-way unknown method ordinal.
89        let header = fidl::encoding::TransactionHeader::new(
90            0,
91            0x1234_5678,
92            fidl::encoding::DynamicFlags::FLEXIBLE,
93        );
94        let bytes = unsafe {
95            std::slice::from_raw_parts(
96                &header as *const _ as *const u8,
97                std::mem::size_of_val(&header),
98            )
99        };
100        proxy.as_channel().write(bytes, &mut []).expect("write raw unknown method header");
101
102        let mut event_stream = proxy.take_event_stream();
103        let event = event_stream.next().await;
104        assert_matches!(
105            event,
106            Some(Err(fidl::Error::ClientChannelClosed {
107                epitaph: fidl::Epitaph::Explicit(Err(zx::Status::UNAVAILABLE)),
108                ..
109            }))
110        );
111
112        server_task.await;
113    }
114
115    #[fuchsia::test]
116    async fn client_closes_channel() {
117        let (server, _rx) = LogFreezeServer::new();
118        let (proxy, stream) = create_proxy_and_stream::<ftarget::SerialLogControlMarker>();
119
120        drop(proxy);
121        assert!(server.handle_requests(stream).await.is_ok());
122    }
123
124    #[fuchsia::test]
125    async fn freezer_dropped_returns_error() {
126        let (server, rx) = LogFreezeServer::new();
127        drop(rx);
128
129        let (proxy, stream) = create_proxy_and_stream::<ftarget::SerialLogControlMarker>();
130
131        let server_task =
132            fuchsia_async::Task::spawn(async move { server.handle_requests(stream).await });
133
134        let freeze_fut = proxy.freeze_serial_forwarding();
135        let (result, _) = futures::join!(server_task, freeze_fut);
136        assert!(result.is_err());
137    }
138}