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::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")]
16
pub 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")]
29
pub 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)]
42
pub enum VarlinkRwhodClientResponse {
43
    Rwho(VarlinkRwhoResponse),
44
    Ruptime(VarlinkRuptimeResponse),
45
}
46

            
47
pub type VarlinkRwhoResponse = Vec<(String, WhodUserEntry)>;
48
pub type VarlinkRuptimeResponse = Vec<WhodStatusUpdate>;
49

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

            
56
// Types for 'no.ntnu.pvv.roowho2.finger'
57

            
58
#[zlink::proxy("no.ntnu.pvv.roowho2.finger")]
59
pub 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")]
68
pub 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)]
75
pub enum VarlinkFingerClientResponse {
76
    Finger(VarlinkFingerResponse),
77
}
78

            
79
pub type VarlinkFingerResponse = Vec<Option<FingerResponseUserEntry>>;
80

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

            
87
// --------------------
88

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

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

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

            
113
#[derive(Debug, Clone)]
114
pub struct VarlinkRoowhoo2ClientServer {
115
    whod_status_store: RwhodStatusStore,
116
}
117

            
118
impl VarlinkRoowhoo2ClientServer {
119
    pub fn new(whod_status_store: RwhodStatusStore) -> Self {
120
        Self { whod_status_store }
121
    }
122
}
123

            
124
impl 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

            
156
impl 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

            
191
pub 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
}