roowho2_lib/server/
varlink_api.rs1use 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#[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 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#[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#[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 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}