1use 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}