Skip to main content

roowho2_lib/server/
varlink_api.rs

1use anyhow::Context;
2use serde::{Deserialize, Serialize};
3use zlink::{ReplyError, service::MethodReply};
4
5use crate::{
6    proto::{WhodStatusUpdate, WhodUserEntry, finger_protocol::FingerResponseUserEntry},
7    server::{
8        fingerd::{self},
9        rwhod::RwhodStatusStore,
10    },
11};
12
13// Types for 'no.ntnu.pvv.roowho2.rwhod'
14
15#[zlink::proxy("no.ntnu.pvv.roowho2.rwhod")]
16pub trait VarlinkRwhodClientProxy {
17    async fn rwho(
18        &mut self,
19        all: bool,
20    ) -> zlink::Result<Result<VarlinkRwhoResponse, VarlinkRwhodClientError>>;
21
22    async fn ruptime(
23        &mut self,
24    ) -> zlink::Result<Result<VarlinkRuptimeResponse, VarlinkRwhodClientError>>;
25}
26
27#[derive(Debug, Deserialize)]
28#[serde(tag = "method", content = "parameters")]
29pub enum VarlinkRwhodClientRequest {
30    #[serde(rename = "no.ntnu.pvv.roowho2.rwhod.Rwho")]
31    Rwho {
32        /// Retrieve all users, even those that have been idle for a long time.
33        all: bool,
34    },
35
36    #[serde(rename = "no.ntnu.pvv.roowho2.rwhod.Ruptime")]
37    Ruptime,
38}
39
40#[derive(Debug, Clone, PartialEq, Serialize)]
41#[serde(untagged)]
42pub enum VarlinkRwhodClientResponse {
43    Rwho(VarlinkRwhoResponse),
44    Ruptime(VarlinkRuptimeResponse),
45}
46
47pub type VarlinkRwhoResponse = Vec<(String, WhodUserEntry)>;
48pub type VarlinkRuptimeResponse = Vec<WhodStatusUpdate>;
49
50#[derive(Debug, Clone, PartialEq, ReplyError)]
51#[zlink(interface = "no.ntnu.pvv.roowho2.rwhod")]
52pub enum VarlinkRwhodClientError {
53    InvalidRequest,
54}
55
56// Types for 'no.ntnu.pvv.roowho2.finger'
57
58#[zlink::proxy("no.ntnu.pvv.roowho2.finger")]
59pub trait VarlinkFingerClientProxy {
60    async fn finger(
61        &mut self,
62        user_queries: Vec<String>,
63    ) -> zlink::Result<Result<VarlinkFingerResponse, VarlinkFingerClientError>>;
64}
65
66#[derive(Debug, Deserialize)]
67#[serde(tag = "method", content = "parameters")]
68pub enum VarlinkFingerClientRequest {
69    #[serde(rename = "no.ntnu.pvv.roowho2.finger.Finger")]
70    Finger { user_queries: Vec<String> },
71}
72
73#[derive(Debug, Serialize)]
74#[serde(untagged)]
75pub enum VarlinkFingerClientResponse {
76    Finger(VarlinkFingerResponse),
77}
78
79pub type VarlinkFingerResponse = Vec<Option<FingerResponseUserEntry>>;
80
81#[derive(Debug, Clone, PartialEq, ReplyError)]
82#[zlink(interface = "no.ntnu.pvv.roowho2.finger")]
83pub enum VarlinkFingerClientError {
84    InvalidRequest,
85}
86
87// --------------------
88
89#[derive(Debug, Deserialize)]
90#[serde(untagged)]
91#[allow(unused)]
92pub enum VarlinkMethod {
93    Rwhod(VarlinkRwhodClientRequest),
94    Finger(VarlinkFingerClientRequest),
95}
96
97#[derive(Debug, Serialize)]
98#[serde(untagged)]
99#[allow(unused)]
100pub enum VarlinkReply {
101    Rwhod(VarlinkRwhodClientResponse),
102    Finger(VarlinkFingerClientResponse),
103}
104
105#[derive(Debug, Clone, PartialEq, Serialize)]
106#[serde(untagged)]
107#[allow(unused)]
108pub enum VarlinkReplyError {
109    Rwhod(VarlinkRwhodClientError),
110    Finger(VarlinkFingerClientError),
111}
112
113#[derive(Debug, Clone)]
114pub struct VarlinkRoowhoo2ClientServer {
115    whod_status_store: RwhodStatusStore,
116}
117
118impl VarlinkRoowhoo2ClientServer {
119    pub fn new(whod_status_store: RwhodStatusStore) -> Self {
120        Self { whod_status_store }
121    }
122}
123
124impl VarlinkRoowhoo2ClientServer {
125    // TODO: handle 'all' parameter
126    async fn handle_rwho_request(&self, _all: bool) -> VarlinkRwhoResponse {
127        let store = self.whod_status_store.read().await;
128
129        let mut all_user_entries = Vec::with_capacity(store.len());
130        for status_update in store.values() {
131            all_user_entries.extend_from_slice(
132                &status_update
133                    .users
134                    .iter()
135                    .map(|user| (status_update.hostname.clone(), user.clone()))
136                    .collect::<Vec<(String, WhodUserEntry)>>(),
137            );
138        }
139
140        all_user_entries
141    }
142
143    async fn handle_ruptime_request(&self) -> VarlinkRuptimeResponse {
144        let store = self.whod_status_store.read().await;
145        store.values().cloned().collect()
146    }
147
148    async fn handle_finger_request(&self, user_queries: Vec<String>) -> VarlinkFingerResponse {
149        user_queries
150            .into_iter()
151            .map(|username| fingerd::get_local_user(&username).unwrap())
152            .collect()
153    }
154}
155
156impl zlink::Service for VarlinkRoowhoo2ClientServer {
157    type MethodCall<'de> = VarlinkMethod;
158    type ReplyParams<'se> = VarlinkReply;
159    type ReplyStreamParams = ();
160    type ReplyStream = futures_util::stream::Empty<zlink::Reply<()>>;
161    type ReplyError<'se> = VarlinkReplyError;
162
163    async fn handle<'service, Sock: zlink::connection::Socket>(
164        &'service mut self,
165        call: &'service zlink::Call<Self::MethodCall<'_>>,
166        _conn: &mut zlink::Connection<Sock>,
167    ) -> MethodReply<Self::ReplyParams<'service>, Self::ReplyStream, Self::ReplyError<'service>>
168    {
169        match call.method() {
170            VarlinkMethod::Rwhod(VarlinkRwhodClientRequest::Rwho { all }) => {
171                MethodReply::Single(Some(VarlinkReply::Rwhod(VarlinkRwhodClientResponse::Rwho(
172                    self.handle_rwho_request(*all).await,
173                ))))
174            }
175            VarlinkMethod::Rwhod(VarlinkRwhodClientRequest::Ruptime) => {
176                MethodReply::Single(Some(VarlinkReply::Rwhod(
177                    VarlinkRwhodClientResponse::Ruptime(self.handle_ruptime_request().await),
178                )))
179            }
180            VarlinkMethod::Finger(VarlinkFingerClientRequest::Finger { user_queries }) => {
181                MethodReply::Single(Some(VarlinkReply::Finger(
182                    VarlinkFingerClientResponse::Finger(
183                        self.handle_finger_request(user_queries.clone()).await,
184                    ),
185                )))
186            }
187        }
188    }
189}
190
191pub async fn varlink_client_server_task(
192    socket: zlink::unix::Listener,
193    whod_status_store: RwhodStatusStore,
194) -> anyhow::Result<()> {
195    let service = VarlinkRoowhoo2ClientServer::new(whod_status_store);
196
197    let server = zlink::Server::new(socket, service);
198
199    tracing::info!("Starting Rwhod client API server");
200
201    server
202        .run()
203        .await
204        .context("Rwhod client API server failed")?;
205
206    Ok(())
207}