Skip to main content

decant_client/
lib.rs

1//! Blocking TCP client for the Decant daemon RPC protocol.
2
3use std::net::TcpStream;
4use std::time::Duration;
5
6use decant_protocol::{
7    Diagnostics, MemRegion, ModuleInfo, Pid, ProcessInfo, ProtoError, Request, Response, read_msg,
8    write_msg,
9};
10
11pub use decant_protocol as protocol;
12
13pub const DEFAULT_ENDPOINT: &str = "127.0.0.1:7878";
14
15#[derive(Debug, thiserror::Error)]
16pub enum ClientError {
17    #[error("io: {0}")]
18    Io(#[from] std::io::Error),
19    #[error("{0}")]
20    Protocol(ProtoError),
21    #[error("unexpected response: {0:?}")]
22    Unexpected(Response),
23}
24
25pub type Result<T> = std::result::Result<T, ClientError>;
26
27pub struct Client {
28    endpoint: String,
29    conn: Option<TcpStream>,
30    timeout: Duration,
31}
32
33impl Client {
34    pub fn new(endpoint: impl Into<String>) -> Self {
35        Client {
36            endpoint: endpoint.into(),
37            conn: None,
38            timeout: Duration::from_secs(10),
39        }
40    }
41
42    pub fn from_env() -> Self {
43        Self::new(std::env::var("DECANT_ENDPOINT").unwrap_or_else(|_| DEFAULT_ENDPOINT.into()))
44    }
45
46    pub fn endpoint(&self) -> &str {
47        &self.endpoint
48    }
49
50    pub fn set_timeout(&mut self, timeout: Duration) {
51        self.timeout = timeout;
52    }
53
54    pub fn send(&mut self, req: Request) -> Result<Response> {
55        let mut last: Option<std::io::Error> = None;
56        let attempts = if req.retry_safe() { 2 } else { 1 };
57        for _ in 0..attempts {
58            if self.conn.is_none() {
59                let stream = TcpStream::connect(&self.endpoint)?;
60                let _ = stream.set_nodelay(true);
61                stream.set_read_timeout(Some(self.timeout))?;
62                stream.set_write_timeout(Some(self.timeout))?;
63                self.conn = Some(stream);
64            }
65            let stream = self.conn.as_mut().unwrap();
66            match write_msg(stream, &req).and_then(|()| read_msg::<_, Response>(stream)) {
67                Ok(resp) => return Ok(resp),
68                Err(e) => {
69                    self.conn = None;
70                    last = Some(e);
71                }
72            }
73        }
74        Err(ClientError::Io(
75            last.unwrap_or_else(|| std::io::Error::other("request failed")),
76        ))
77    }
78
79    pub fn ping(&mut self) -> Result<()> {
80        expect(self.send(Request::Ping)?, |r| match r {
81            Response::Pong => Ok(()),
82            o => Err(o),
83        })
84    }
85
86    pub fn processes(&mut self) -> Result<Vec<ProcessInfo>> {
87        expect(self.send(Request::ListProcesses)?, |r| match r {
88            Response::Processes(p) => Ok(p),
89            o => Err(o),
90        })
91    }
92
93    pub fn process_by_pid(&mut self, pid: Pid) -> Result<ProcessInfo> {
94        expect(self.send(Request::ProcessByPid(pid))?, |r| match r {
95            Response::Process(p) => Ok(p),
96            o => Err(o),
97        })
98    }
99
100    pub fn process_by_name(&mut self, name: &str) -> Result<ProcessInfo> {
101        expect(
102            self.send(Request::ProcessByName(name.to_string()))?,
103            |r| match r {
104                Response::Process(p) => Ok(p),
105                o => Err(o),
106            },
107        )
108    }
109
110    pub fn modules(&mut self, pid: Pid) -> Result<Vec<ModuleInfo>> {
111        expect(self.send(Request::ModuleList(pid))?, |r| match r {
112            Response::Modules(m) => Ok(m),
113            o => Err(o),
114        })
115    }
116
117    pub fn module_by_name(&mut self, pid: Pid, name: &str) -> Result<ModuleInfo> {
118        expect(
119            self.send(Request::ModuleByName(pid, name.to_string()))?,
120            |r| match r {
121                Response::Module(m) => Ok(m),
122                o => Err(o),
123            },
124        )
125    }
126
127    pub fn exports(&mut self, pid: Pid, module: &str) -> Result<Vec<(String, u64)>> {
128        expect(
129            self.send(Request::ModuleExports(pid, module.to_string()))?,
130            |r| match r {
131                Response::Exports(e) => Ok(e),
132                o => Err(o),
133            },
134        )
135    }
136
137    pub fn read(&mut self, pid: Pid, addr: u64, len: usize) -> Result<Vec<u8>> {
138        expect(
139            self.send(Request::Read {
140                pid,
141                addr,
142                len: len as u64,
143            })?,
144            |r| match r {
145                Response::Data(d) => Ok(d),
146                o => Err(o),
147            },
148        )
149    }
150
151    pub fn write(&mut self, pid: Pid, addr: u64, data: &[u8]) -> Result<usize> {
152        expect(
153            self.send(Request::Write {
154                pid,
155                addr,
156                data: data.to_vec(),
157            })?,
158            |r| match r {
159                Response::Written(n) => Ok(n as usize),
160                o => Err(o),
161            },
162        )
163    }
164
165    pub fn memory_map(&mut self, pid: Pid) -> Result<Vec<MemRegion>> {
166        expect(self.send(Request::MemoryMap(pid))?, |r| match r {
167            Response::MemoryMap(m) => Ok(m),
168            o => Err(o),
169        })
170    }
171
172    pub fn scan(&mut self, pid: Pid, pattern: &str) -> Result<Vec<u64>> {
173        expect(
174            self.send(Request::Scan {
175                pid,
176                pattern: pattern.to_string(),
177            })?,
178            |r| match r {
179                Response::ScanHits(h) => Ok(h),
180                o => Err(o),
181            },
182        )
183    }
184
185    pub fn resolve(&mut self, pid: Pid, base: u64, offsets: &[u64]) -> Result<(u64, Vec<u8>)> {
186        expect(
187            self.send(Request::Resolve {
188                pid,
189                base,
190                offsets: offsets.to_vec(),
191            })?,
192            |r| match r {
193                Response::Resolved { address, value } => Ok((address, value)),
194                o => Err(o),
195            },
196        )
197    }
198
199    pub fn diagnostics(&mut self) -> Result<Diagnostics> {
200        expect(self.send(Request::Diagnostics)?, |r| match r {
201            Response::Diagnostics(d) => Ok(d),
202            o => Err(o),
203        })
204    }
205}
206
207fn expect<T>(
208    resp: Response,
209    f: impl FnOnce(Response) -> std::result::Result<T, Response>,
210) -> Result<T> {
211    match resp {
212        Response::Err(e) => Err(ClientError::Protocol(e)),
213        other => f(other).map_err(ClientError::Unexpected),
214    }
215}