1
use anyhow::Context;
2
use serde::{Deserialize, Serialize};
3
use zlink::{ReplyError, service::MethodReply};
4

            
5
use crate::{
6
    proto::{WhodStatusUpdate, WhodUserEntry, finger_protocol::FingerResponse},
7
    server::rwhod::RwhodStatusStore,
8
};
9

            
10
// Types for 'no.ntnu.pvv.roowho2.rwhod'
11

            
12
#[zlink::proxy("no.ntnu.pvv.roowho2.rwhod")]
13
pub trait VarlinkRwhodClientProxy {
14
    async fn rwho(
15
        &mut self,
16
        all: bool,
17
    ) -> zlink::Result<Result<VarlinkRwhoResponse, VarlinkRwhodClientError>>;
18

            
19
    async fn ruptime(
20
        &mut self,
21
    ) -> zlink::Result<Result<VarlinkRuptimeResponse, VarlinkRwhodClientError>>;
22
}
23

            
24
#[derive(Debug, Deserialize)]
25
#[serde(tag = "method", content = "parameters")]
26
pub enum VarlinkRwhodClientRequest {
27
    #[serde(rename = "no.ntnu.pvv.roowho2.rwhod.Rwho")]
28
    Rwho {
29
        /// Retrieve all users, even those that have been idle for a long time.
30
        all: bool,
31
    },
32

            
33
    #[serde(rename = "no.ntnu.pvv.roowho2.rwhod.Ruptime")]
34
    Ruptime,
35
}
36

            
37
#[derive(Debug, Clone, PartialEq, Serialize)]
38
#[serde(untagged)]
39
pub enum VarlinkRwhodClientResponse {
40
    Rwho(VarlinkRwhoResponse),
41
    Ruptime(VarlinkRuptimeResponse),
42
}
43

            
44
pub type VarlinkRwhoResponse = Vec<(String, WhodUserEntry)>;
45
pub type VarlinkRuptimeResponse = Vec<WhodStatusUpdate>;
46

            
47
#[derive(Debug, Clone, PartialEq, ReplyError)]
48
#[zlink(interface = "no.ntnu.pvv.roowho2.rwhod")]
49
pub enum VarlinkRwhodClientError {
50
    InvalidRequest,
51
}
52

            
53
// Types for 'no.ntnu.pvv.roowho2.finger'
54

            
55
#[zlink::proxy("no.ntnu.pvv.roowho2.finger")]
56
pub trait VarlinkFingerClientProxy {
57
    async fn finger(
58
        &mut self,
59
        user_queries: Vec<String>,
60
    ) -> zlink::Result<Result<VarlinkFingerResponse, VarlinkFingerClientError>>;
61
}
62

            
63
#[derive(Debug, Deserialize)]
64
#[serde(tag = "method", content = "parameters")]
65
pub enum VarlinkFingerClientRequest {
66
    #[serde(rename = "no.ntnu.pvv.roowho2.finger.Finger")]
67
    Finger { user_queries: Vec<String> },
68
}
69

            
70
#[derive(Debug, Serialize)]
71
#[serde(untagged)]
72
pub enum VarlinkFingerClientResponse {
73
    Finger(VarlinkFingerResponse),
74
}
75

            
76
pub type VarlinkFingerResponse = FingerResponse;
77

            
78
#[derive(Debug, Clone, PartialEq, ReplyError)]
79
#[zlink(interface = "no.ntnu.pvv.roowho2.finger")]
80
pub enum VarlinkFingerClientError {
81
    InvalidRequest,
82
}
83

            
84
// --------------------
85

            
86
#[derive(Debug, Deserialize)]
87
#[serde(untagged)]
88
#[allow(unused)]
89
pub enum VarlinkMethod {
90
    Rwhod(VarlinkRwhodClientRequest),
91
    Finger(VarlinkFingerClientRequest),
92
}
93

            
94
#[derive(Debug, Serialize)]
95
#[serde(untagged)]
96
#[allow(unused)]
97
pub enum VarlinkReply {
98
    Rwhod(VarlinkRwhodClientResponse),
99
    Finger(VarlinkFingerClientResponse),
100
}
101

            
102
#[derive(Debug, Clone, PartialEq, Serialize)]
103
#[serde(untagged)]
104
#[allow(unused)]
105
pub enum VarlinkReplyError {
106
    Rwhod(VarlinkRwhodClientError),
107
    Finger(VarlinkFingerClientError),
108
}
109

            
110
#[derive(Debug, Clone)]
111
pub struct VarlinkRoowhoo2ClientServer {
112
    whod_status_store: RwhodStatusStore,
113
}
114

            
115
impl VarlinkRoowhoo2ClientServer {
116
    pub fn new(whod_status_store: RwhodStatusStore) -> Self {
117
        Self { whod_status_store }
118
    }
119
}
120

            
121
impl VarlinkRoowhoo2ClientServer {
122
    // TODO: handle 'all' parameter
123
    async fn handle_rwho_request(&self, _all: bool) -> VarlinkRwhoResponse {
124
        let store = self.whod_status_store.read().await;
125

            
126
        let mut all_user_entries = Vec::with_capacity(store.len());
127
        for status_update in store.values() {
128
            all_user_entries.extend_from_slice(
129
                &status_update
130
                    .users
131
                    .iter()
132
                    .map(|user| (status_update.hostname.clone(), user.clone()))
133
                    .collect::<Vec<(String, WhodUserEntry)>>(),
134
            );
135
        }
136

            
137
        all_user_entries
138
    }
139

            
140
    async fn handle_ruptime_request(&self) -> VarlinkRuptimeResponse {
141
        let store = self.whod_status_store.read().await;
142
        store.values().cloned().collect()
143
    }
144
}
145

            
146
impl zlink::Service for VarlinkRoowhoo2ClientServer {
147
    type MethodCall<'de> = VarlinkMethod;
148
    type ReplyParams<'se> = VarlinkReply;
149
    type ReplyStreamParams = ();
150
    type ReplyStream = futures_util::stream::Empty<zlink::Reply<()>>;
151
    type ReplyError<'se> = VarlinkReplyError;
152

            
153
    async fn handle<'service, Sock: zlink::connection::Socket>(
154
        &'service mut self,
155
        call: &'service zlink::Call<Self::MethodCall<'_>>,
156
        _conn: &mut zlink::Connection<Sock>,
157
    ) -> MethodReply<Self::ReplyParams<'service>, Self::ReplyStream, Self::ReplyError<'service>>
158
    {
159
        match call.method() {
160
            VarlinkMethod::Rwhod(VarlinkRwhodClientRequest::Rwho { all }) => {
161
                MethodReply::Single(Some(VarlinkReply::Rwhod(VarlinkRwhodClientResponse::Rwho(
162
                    self.handle_rwho_request(*all).await,
163
                ))))
164
            }
165
            VarlinkMethod::Rwhod(VarlinkRwhodClientRequest::Ruptime) => {
166
                MethodReply::Single(Some(VarlinkReply::Rwhod(
167
                    VarlinkRwhodClientResponse::Ruptime(self.handle_ruptime_request().await),
168
                )))
169
            }
170
            VarlinkMethod::Finger(VarlinkFingerClientRequest::Finger { user_queries: _ }) => {
171
                unimplemented!()
172
            }
173
        }
174
    }
175
}
176

            
177
pub async fn varlink_client_server_task(
178
    socket: zlink::unix::Listener,
179
    whod_status_store: RwhodStatusStore,
180
) -> anyhow::Result<()> {
181
    let service = VarlinkRoowhoo2ClientServer::new(whod_status_store);
182

            
183
    let server = zlink::Server::new(socket, service);
184

            
185
    tracing::info!("Starting Rwhod client API server");
186

            
187
    server
188
        .run()
189
        .await
190
        .context("Rwhod client API server failed")?;
191

            
192
    Ok(())
193
}