wl_proxy/
simple.rs

1//! Helpers that take care of most of the boilerplate for simple proxies.
2
3use {
4    crate::{
5        acceptor::{Acceptor, AcceptorError},
6        baseline::Baseline,
7        client::ClientHandler,
8        protocols::wayland::wl_display::WlDisplayHandler,
9        state::{Destructor, State},
10        utils::env::WAYLAND_DISPLAY,
11    },
12    error_reporter::Report,
13    parking_lot::Mutex,
14    run_on_drop::on_drop,
15    std::{
16        io,
17        os::unix::prelude::ExitStatusExt,
18        process::{Command, exit},
19        rc::Rc,
20        sync::atomic::{AtomicUsize, Ordering::Relaxed},
21        thread,
22    },
23    thiserror::Error,
24    uapi::raise,
25};
26
27/// A simple proxy server that spawns a thread for each client.
28///
29/// This server will create an acceptor and create a [`State`] for
30/// each client that connects to the acceptor.
31pub struct SimpleProxy {
32    baseline: Baseline,
33    acceptor: Rc<Acceptor>,
34}
35
36/// An error returned by a [`SimpleProxy`].
37#[derive(Debug, Error)]
38#[error(transparent)]
39pub struct SimpleProxyError(#[from] SimpleProxyErrorKind);
40
41#[derive(Debug, Error)]
42enum SimpleProxyErrorKind {
43    #[error("could not create an acceptor")]
44    CreateAcceptor(#[source] AcceptorError),
45    #[error("could not accept a connection")]
46    AcceptConnection(#[source] AcceptorError),
47    #[error("could not spawn a thread")]
48    SpawnThread(#[source] io::Error),
49}
50
51impl SimpleProxy {
52    /// Creates a new [`SimpleProxy`].
53    pub fn new(baseline: Baseline) -> Result<SimpleProxy, SimpleProxyError> {
54        Ok(Self {
55            baseline,
56            acceptor: Acceptor::new(1000, false).map_err(SimpleProxyErrorKind::CreateAcceptor)?,
57        })
58    }
59
60    /// Returns the name of the display used by this proxy.
61    ///
62    /// The `WAYLAND_DISPLAY` environment variable should be set to this value for clients
63    /// that should connect to this proxy. See [`SimpleCommandExt::with_wayland_display`].
64    pub fn display(&self) -> &str {
65        self.acceptor.display()
66    }
67
68    /// Runs the proxy indefinitely.
69    ///
70    /// This function does not return unless a fatal error occurs.
71    pub fn run<H>(self, display_handler: impl Fn() -> H + Sync) -> SimpleProxyError
72    where
73        H: WlDisplayHandler,
74    {
75        static ID: AtomicUsize = AtomicUsize::new(1);
76        let display_handler = &display_handler;
77        let destructors = Mutex::new(Some(vec![]));
78        let destructors = &destructors;
79        let err = thread::scope(|s| {
80            let _stop_all_proxies = on_drop(|| *destructors.lock() = None);
81            loop {
82                let socket = match self.acceptor.accept() {
83                    Ok(s) => s.expect("blocking acceptor returned None"),
84                    Err(e) => return SimpleProxyErrorKind::AcceptConnection(e),
85                };
86                let id = ID.fetch_add(1, Relaxed);
87                let name = format!("socket-{id}");
88                log::debug!("Client {id} connected");
89                let res = thread::Builder::new()
90                    .name(name.clone())
91                    .spawn_scoped(s, move || {
92                        let state = State::builder(self.baseline).with_log_prefix(&name).build();
93                        let state = match state {
94                            Ok(s) => s,
95                            Err(e) => {
96                                log::error!("Could not create a new state: {}", Report::new(e));
97                                return;
98                            }
99                        };
100                        match state.create_remote_destructor() {
101                            Ok(d) => match &mut *destructors.lock() {
102                                Some(des) => des.push(d),
103                                _ => return,
104                            },
105                            Err(e) => {
106                                log::error!(
107                                    "Could not create a remote destructor: {}",
108                                    Report::new(e),
109                                );
110                                return;
111                            }
112                        }
113                        let client = match state.add_client(&Rc::new(socket)) {
114                            Ok(c) => c,
115                            Err(e) => {
116                                log::error!("Could not add client to state: {}", Report::new(e));
117                                return;
118                            }
119                        };
120                        client.set_handler(ClientHandlerImpl {
121                            id,
122                            _destructor: state.create_destructor(),
123                        });
124                        let handler = display_handler();
125                        client.display().set_handler(handler);
126                        while state.is_not_destroyed() {
127                            if let Err(e) = state.dispatch_blocking() {
128                                log::error!("Could not dispatch state: {}", Report::new(e));
129                            }
130                        }
131                    });
132                if let Err(e) = res {
133                    return SimpleProxyErrorKind::SpawnThread(e);
134                }
135            }
136        });
137        SimpleProxyError(err)
138    }
139}
140
141struct ClientHandlerImpl {
142    id: usize,
143    _destructor: Destructor,
144}
145
146impl ClientHandler for ClientHandlerImpl {
147    fn disconnected(self: Box<Self>) {
148        log::debug!("Client {} disconnected", self.id);
149    }
150}
151
152/// Extensions for [`Command`].
153pub trait SimpleCommandExt {
154    /// Sets the `WAYLAND_DISPLAY` environment variable.
155    fn with_wayland_display(&mut self, display: &str) -> &mut Command;
156    /// Spawns the application, waits for it to exit, and then calls [`exit`] with the
157    /// same exit code.
158    fn spawn_and_forward_exit_code(&mut self) -> Result<(), io::Error>;
159}
160
161impl SimpleCommandExt for Command {
162    fn with_wayland_display(&mut self, display: &str) -> &mut Command {
163        self.env(WAYLAND_DISPLAY, display)
164    }
165
166    fn spawn_and_forward_exit_code(&mut self) -> Result<(), io::Error> {
167        let mut child = self.spawn()?;
168        thread::spawn(move || match child.wait() {
169            Ok(e) => {
170                if let Some(code) = e.code() {
171                    exit(code);
172                }
173                if let Some(signal) = e.signal() {
174                    let _ = raise(signal);
175                    exit(1);
176                }
177                eprintln!("Child terminated with neither a signal nor an exit code");
178                exit(1);
179            }
180            Err(e) => {
181                eprintln!("Could not wait for child: {}", Report::new(e));
182                exit(1);
183            }
184        });
185        Ok(())
186    }
187}