1use std::collections::VecDeque;
8use std::io::{BufRead, BufReader, BufWriter, Write};
9use std::path::{Path, PathBuf};
10use std::process::{Child, ChildStdin, Command, ExitStatus, Stdio};
11use std::sync::mpsc::{Receiver, RecvTimeoutError, channel};
12use std::sync::{Arc, Mutex, OnceLock};
13use std::time::Duration;
14
15use arrow::array::RecordBatch;
16use arrow::datatypes::SchemaRef;
17use serde_json::{Map, Value};
18
19use crate::frame::{self, Frame, FrameError};
20use crate::msg::{ConnectionField, DeliveryFile, Request, Response, ResultSetMeta};
21use crate::options::OptionField;
22use crate::{Kind, MAX_VERSION, MIN_VERSION, PluginId, parse_executable_name};
23
24pub type LogSink = Arc<dyn Fn(&str, &str) + Send + Sync>;
26
27pub fn stderr_log() -> LogSink {
29 Arc::new(|plugin, line| eprintln!("[{plugin}] {line}"))
30}
31
32pub const DEFAULT_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(30);
33
34pub const INTERACTIVE_ENV: &str = "DRE_INTERACTIVE";
36const STDERR_TAIL: usize = 20;
37
38#[derive(Debug, Clone, PartialEq)]
39pub struct PluginInfo {
40 pub protocol_version: u32,
41 pub kind: Kind,
42 pub name: String,
43 pub version: String,
44 pub capabilities: Vec<String>,
45 pub provides: Vec<PluginId>,
47}
48
49#[derive(Debug, Clone, PartialEq, Default)]
51pub struct Description {
52 pub connection_fields: Vec<ConnectionField>,
53 pub option_fields: Vec<OptionField>,
54 pub identifier_quote: Option<String>,
56 pub message_limit: Option<u64>,
58}
59
60#[derive(Debug)]
61pub enum HostError {
62 Spawn {
63 plugin: String,
64 error: std::io::Error,
65 },
66 Crashed {
67 plugin: String,
68 status: Option<ExitStatus>,
69 stderr: Vec<String>,
70 },
71 Malformed {
72 plugin: String,
73 message: String,
74 },
75 Incompatible {
76 plugin: String,
77 core: (u32, u32),
78 plugin_range: (u32, u32),
79 },
80 Timeout {
81 plugin: String,
82 waiting_for: &'static str,
83 },
84 Unexpected {
85 plugin: String,
86 expected: &'static str,
87 got: String,
88 },
89 Plugin {
91 plugin: String,
92 message: String,
93 },
94 Arrow {
95 plugin: String,
96 message: String,
97 },
98}
99
100impl std::fmt::Display for HostError {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 match self {
103 HostError::Spawn { plugin, error } => write!(f, "can't start plugin `{plugin}`: {error}"),
104 HostError::Crashed {
105 plugin,
106 status,
107 stderr,
108 } => {
109 match status {
110 Some(s) => write!(f, "plugin `{plugin}` exited unexpectedly ({s})")?,
111 None => write!(f, "plugin `{plugin}` closed its output unexpectedly")?,
112 }
113 if !stderr.is_empty() {
114 write!(f, "; its last log lines:\n {}", stderr.join("\n "))?;
115 }
116 Ok(())
117 }
118 HostError::Malformed { plugin, message } => {
119 write!(f, "plugin `{plugin}` broke the protocol: {message}")
120 }
121 HostError::Incompatible {
122 plugin,
123 core,
124 plugin_range,
125 } => write!(
126 f,
127 "plugin `{plugin}` speaks protocol versions {}..={}, but this DRE core speaks {}..={}; update {}",
128 plugin_range.0,
129 plugin_range.1,
130 core.0,
131 core.1,
132 if plugin_range.1 < core.0 {
133 "the plugin"
134 } else {
135 "DRE"
136 }
137 ),
138 HostError::Timeout { plugin, waiting_for } => {
139 write!(
140 f,
141 "plugin `{plugin}` didn't answer in time (waiting for {waiting_for})"
142 )
143 }
144 HostError::Unexpected {
145 plugin,
146 expected,
147 got,
148 } => {
149 write!(f, "plugin `{plugin}` sent {got} where {expected} was expected")
150 }
151 HostError::Plugin { message, .. } => f.write_str(message),
152 HostError::Arrow { plugin, message } => {
153 write!(f, "plugin `{plugin}` sent invalid Arrow data: {message}")
154 }
155 }
156 }
157}
158
159impl std::error::Error for HostError {}
160
161pub type Result<T> = std::result::Result<T, HostError>;
162
163pub enum Incoming {
165 Json(Response),
166 Arrow(Vec<u8>),
167}
168
169impl std::fmt::Debug for Incoming {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 match self {
172 Incoming::Json(r) => write!(f, "Json({r:?})"),
173 Incoming::Arrow(b) => write!(f, "Arrow frame ({} bytes)", b.len()),
174 }
175 }
176}
177
178#[derive(Debug)]
180pub enum Execution {
181 NoResult { rows_affected: Option<u64> },
182 Result { schema: SchemaRef, rows: u64 },
183}
184
185pub struct PluginProcess {
186 label: String,
187 path: PathBuf,
188 child: Child,
189 stdin: Option<BufWriter<ChildStdin>>,
190 rx: Receiver<std::result::Result<Frame, FrameError>>,
191 stderr: Arc<Mutex<VecDeque<String>>>,
192 stderr_thread: Option<std::thread::JoinHandle<()>>,
194 info: Option<PluginInfo>,
195 early: Option<std::result::Result<Frame, FrameError>>,
197 serve: Option<PluginId>,
200}
201
202impl PluginProcess {
203 pub fn start(path: &Path, log: LogSink) -> Result<PluginProcess> {
205 Self::start_in(path, log, None)
206 }
207
208 pub fn start_in(path: &Path, log: LogSink, cwd: Option<&Path>) -> Result<PluginProcess> {
210 Self::start_for(path, None, log, cwd)
211 }
212
213 pub fn start_for(
215 path: &Path,
216 plugin: Option<&PluginId>,
217 log: LogSink,
218 cwd: Option<&Path>,
219 ) -> Result<PluginProcess> {
220 let timeout = std::env::var("DRE_PLUGIN_HANDSHAKE_TIMEOUT_MS")
221 .ok()
222 .and_then(|v| v.parse().ok())
223 .map(Duration::from_millis)
224 .unwrap_or(DEFAULT_HANDSHAKE_TIMEOUT);
225 let mut p = Self::spawn_in(path, log, &[], cwd)?;
226 if let Some(id) = plugin {
227 p.serve = Some(id.clone());
228 }
229 p.handshake((MIN_VERSION, MAX_VERSION), timeout)?;
230 Ok(p)
231 }
232
233 pub fn start_with(
235 path: &Path,
236 log: LogSink,
237 versions: (u32, u32),
238 timeout: Duration,
239 ) -> Result<PluginProcess> {
240 let mut p = Self::spawn(path, log)?;
241 p.handshake(versions, timeout)?;
242 Ok(p)
243 }
244
245 pub fn spawn(path: &Path, log: LogSink) -> Result<PluginProcess> {
247 Self::spawn_env(path, log, &[])
248 }
249
250 pub fn spawn_env(path: &Path, log: LogSink, env: &[(&str, &str)]) -> Result<PluginProcess> {
252 Self::spawn_in(path, log, env, None)
253 }
254
255 pub fn spawn_in(
257 path: &Path,
258 log: LogSink,
259 env: &[(&str, &str)],
260 cwd: Option<&Path>,
261 ) -> Result<PluginProcess> {
262 let label = path
263 .file_name()
264 .map(|f| f.to_string_lossy().to_string())
265 .unwrap_or_default();
266 let mut cmd = Command::new(path);
267 if let Some(dir) = cwd {
268 cmd.current_dir(dir);
269 }
270 if std::env::var_os(INTERACTIVE_ENV).is_none() {
273 use std::io::IsTerminal;
274 let there = std::io::stderr().is_terminal() && std::io::stdin().is_terminal();
275 cmd.env(INTERACTIVE_ENV, if there { "1" } else { "0" });
276 }
277 let mut child = cmd
278 .envs(env.iter().copied())
279 .stdin(Stdio::piped())
280 .stdout(Stdio::piped())
281 .stderr(Stdio::piped())
282 .spawn()
283 .map_err(|error| HostError::Spawn {
284 plugin: label.clone(),
285 error,
286 })?;
287 let stdout = child.stdout.take().unwrap();
288 let stderr_pipe = child.stderr.take().unwrap();
289 let stdin = child.stdin.take().map(BufWriter::new);
290
291 let (tx, rx) = channel();
292 std::thread::spawn(move || {
293 let mut r = BufReader::new(stdout);
294 loop {
295 let f = frame::read_frame(&mut r);
296 let stop = f.is_err();
297 if tx.send(f).is_err() || stop {
298 break;
299 }
300 }
301 });
302 let stderr = Arc::new(Mutex::new(VecDeque::new()));
303 let tail = stderr.clone();
304 let who = label.clone();
305 let stderr_thread = std::thread::spawn(move || {
306 for line in BufReader::new(stderr_pipe).lines() {
307 let Ok(line) = line else { break };
308 log(&who, &line);
309 let mut t = tail.lock().unwrap();
310 if t.len() == STDERR_TAIL {
311 t.pop_front();
312 }
313 t.push_back(line);
314 }
315 });
316 let serve = parse_executable_name(&label).map(|(k, n)| PluginId::new(k, n));
317 Ok(PluginProcess {
318 label,
319 path: path.to_path_buf(),
320 child,
321 stdin,
322 rx,
323 stderr,
324 stderr_thread: Some(stderr_thread),
325 info: None,
326 early: None,
327 serve,
328 })
329 }
330
331 pub fn ask_for(&mut self, plugin: Option<PluginId>) {
333 self.serve = plugin;
334 }
335
336 pub fn handshake(&mut self, (min, max): (u32, u32), timeout: Duration) -> Result<()> {
338 self.send(&Request::Hello {
339 min_version: min,
340 max_version: max,
341 core_version: core_version(),
342 plugin: self.serve.clone(),
343 })?;
344 match self.recv(Some(timeout), "the hello reply")? {
345 Incoming::Json(Response::Hello {
346 protocol_version,
347 kind,
348 name,
349 version,
350 capabilities,
351 mut provides,
352 }) => {
353 if protocol_version < min || protocol_version > max {
354 return Err(HostError::Incompatible {
355 plugin: self.label.clone(),
356 core: (min, max),
357 plugin_range: (protocol_version, protocol_version),
358 });
359 }
360 if let Some(want) = &self.serve
361 && (want.kind != kind || want.name != name)
362 {
363 return Err(HostError::Plugin {
364 plugin: self.label.clone(),
365 message: format!(
366 "`{}` was asked for the {want} plugin but serves {kind}/{name}",
367 self.label
368 ),
369 });
370 }
371 if provides.is_empty() {
372 provides.push(PluginId::new(kind, name.clone()));
373 }
374 self.info = Some(PluginInfo {
375 protocol_version,
376 kind,
377 name,
378 version,
379 capabilities,
380 provides,
381 });
382 Ok(())
383 }
384 Incoming::Json(Response::VersionMismatch {
385 min_version,
386 max_version,
387 }) => Err(HostError::Incompatible {
388 plugin: self.label.clone(),
389 core: (min, max),
390 plugin_range: (min_version, max_version),
391 }),
392 Incoming::Json(Response::Error { message }) => Err(HostError::Plugin {
393 plugin: self.label.clone(),
394 message,
395 }),
396 other => Err(self.unexpected("a hello reply", &other)),
397 }
398 }
399
400 pub fn label(&self) -> &str {
401 &self.label
402 }
403
404 pub fn path(&self) -> &Path {
405 &self.path
406 }
407
408 pub fn info(&self) -> &PluginInfo {
409 self.info.as_ref().expect("handshake completed")
410 }
411
412 pub fn has(&self, capability: &str) -> bool {
413 self.info
414 .as_ref()
415 .is_some_and(|i| i.capabilities.iter().any(|c| c == capability))
416 }
417
418 fn unexpected(&self, expected: &'static str, got: &Incoming) -> HostError {
419 let got = match got {
420 Incoming::Json(r) => format!("{:?}", r)
421 .split([' ', '{'])
422 .next()
423 .unwrap_or("?")
424 .to_string(),
425 Incoming::Arrow(_) => "Arrow data".into(),
426 };
427 HostError::Unexpected {
428 plugin: self.label.clone(),
429 expected,
430 got: format!("`{got}`"),
431 }
432 }
433
434 fn crashed(&mut self) -> HostError {
435 let mut status = None;
437 for _ in 0..50 {
438 if let Ok(Some(s)) = self.child.try_wait() {
439 status = Some(s);
440 break;
441 }
442 std::thread::sleep(Duration::from_millis(20));
443 }
444 std::thread::sleep(Duration::from_millis(50));
445 let stderr = self.stderr.lock().unwrap().iter().cloned().collect();
446 HostError::Crashed {
447 plugin: self.label.clone(),
448 status,
449 stderr,
450 }
451 }
452
453 pub fn send(&mut self, req: &Request) -> Result<()> {
454 let ok = match self.stdin.as_mut() {
455 Some(w) => frame::write_json(w, req).is_ok(),
456 None => false,
457 };
458 if ok { Ok(()) } else { Err(self.write_failed()) }
459 }
460
461 pub fn send_batch(&mut self, batch: &RecordBatch) -> Result<()> {
462 if self.early.is_none()
465 && let Ok(f) = self.rx.try_recv()
466 {
467 self.early = Some(f);
468 }
469 if let Some(Ok(Frame::Json(v))) = &self.early
470 && let Ok(Response::Error { message }) = serde_json::from_value::<Response>(v.clone())
471 {
472 return Err(HostError::Plugin {
473 plugin: self.label.clone(),
474 message,
475 });
476 }
477 let ipc = frame::encode_batch(batch).map_err(|e| HostError::Arrow {
478 plugin: self.label.clone(),
479 message: e.to_string(),
480 })?;
481 let ok = match self.stdin.as_mut() {
482 Some(w) => frame::write_arrow(w, &ipc).is_ok(),
483 None => false,
484 };
485 if ok { Ok(()) } else { Err(self.write_failed()) }
486 }
487
488 fn write_failed(&mut self) -> HostError {
490 match self.rx.recv_timeout(Duration::from_secs(2)) {
491 Ok(Ok(Frame::Json(v))) => match serde_json::from_value::<Response>(v) {
492 Ok(Response::Error { message }) => HostError::Plugin {
493 plugin: self.label.clone(),
494 message,
495 },
496 _ => self.crashed(),
497 },
498 _ => self.crashed(),
499 }
500 }
501
502 pub fn recv(&mut self, timeout: Option<Duration>, waiting_for: &'static str) -> Result<Incoming> {
504 let got = match timeout {
505 _ if self.early.is_some() => self.early.take(),
506 Some(t) => match self.rx.recv_timeout(t) {
507 Ok(f) => Some(f),
508 Err(RecvTimeoutError::Timeout) => {
509 return Err(HostError::Timeout {
510 plugin: self.label.clone(),
511 waiting_for,
512 });
513 }
514 Err(RecvTimeoutError::Disconnected) => None,
515 },
516 None => self.rx.recv().ok(),
517 };
518 match got {
519 Some(Ok(Frame::Json(v))) => match serde_json::from_value::<Response>(v.clone()) {
520 Ok(r) => Ok(Incoming::Json(r)),
521 Err(e) => Err(HostError::Malformed {
522 plugin: self.label.clone(),
523 message: format!("unknown message {v}: {e}"),
524 }),
525 },
526 Some(Ok(Frame::Arrow(b))) => Ok(Incoming::Arrow(b)),
527 Some(Err(FrameError::Malformed(m))) => Err(HostError::Malformed {
528 plugin: self.label.clone(),
529 message: m,
530 }),
531 Some(Err(_)) | None => Err(self.crashed()),
532 }
533 }
534
535 pub fn recv_json(&mut self, waiting_for: &'static str) -> Result<Response> {
537 match self.recv(None, waiting_for)? {
538 Incoming::Json(Response::Error { message }) => Err(HostError::Plugin {
539 plugin: self.label.clone(),
540 message,
541 }),
542 Incoming::Json(r) => Ok(r),
543 other => Err(self.unexpected(waiting_for, &other)),
544 }
545 }
546
547 fn call_ok(&mut self, req: &Request, what: &'static str) -> Result<()> {
549 self.send(req)?;
550 match self.recv_json(what)? {
551 Response::Ok {} => Ok(()),
552 other => Err(self.unexpected(what, &Incoming::Json(other))),
553 }
554 }
555
556 pub fn describe(&mut self) -> Result<Vec<ConnectionField>> {
557 Ok(self.describe_all()?.0)
558 }
559
560 pub fn describe_all(&mut self) -> Result<(Vec<ConnectionField>, Vec<OptionField>)> {
562 let d = self.description()?;
563 Ok((d.connection_fields, d.option_fields))
564 }
565
566 pub fn description(&mut self) -> Result<Description> {
568 self.send(&Request::Describe {})?;
569 match self.recv_json("a describe reply")? {
570 Response::Describe {
571 connection_fields,
572 option_fields,
573 identifier_quote,
574 message_limit,
575 } => Ok(Description {
576 connection_fields,
577 option_fields,
578 identifier_quote,
579 message_limit,
580 }),
581 other => Err(self.unexpected("a describe reply", &Incoming::Json(other))),
582 }
583 }
584
585 pub fn validate(&mut self, options: Map<String, Value>) -> Result<Vec<String>> {
587 self.send(&Request::Validate { options })?;
588 match self.recv_json("a validate reply")? {
589 Response::Validated { errors } => Ok(errors),
590 other => Err(self.unexpected("a validate reply", &Incoming::Json(other))),
591 }
592 }
593
594 pub fn open(&mut self, connection: Map<String, Value>, read_only: bool) -> Result<()> {
595 self.call_ok(
596 &Request::Open {
597 connection,
598 read_only,
599 },
600 "an open reply",
601 )
602 }
603
604 pub fn check(&mut self, sql: &str) -> Result<()> {
605 self.call_ok(&Request::Check { sql: sql.to_string() }, "a check reply")
606 }
607
608 pub fn execute(
610 &mut self,
611 sql: &str,
612 row_limit: Option<u64>,
613 mut on_batch: impl FnMut(&SchemaRef, RecordBatch) -> std::result::Result<(), String>,
614 ) -> Result<Execution> {
615 self.send(&Request::Execute {
616 sql: sql.to_string(),
617 row_limit,
618 })?;
619 match self.recv_json("an execute reply")? {
620 Response::NoResult { rows_affected } => return Ok(Execution::NoResult { rows_affected }),
621 Response::Result { .. } => {}
622 other => return Err(self.unexpected("an execute reply", &Incoming::Json(other))),
623 }
624 let mut schema: Option<SchemaRef> = None;
625 let mut rows = 0u64;
626 let mut sink_error = None;
627 loop {
628 match self.recv(None, "result data")? {
629 Incoming::Arrow(ipc) => {
630 let (s, batches) = frame::decode_batches(&ipc).map_err(|e| HostError::Arrow {
631 plugin: self.label.clone(),
632 message: e.to_string(),
633 })?;
634 let schema = schema.get_or_insert(s);
635 for b in batches {
636 rows += b.num_rows() as u64;
637 if sink_error.is_none()
638 && let Err(e) = on_batch(schema, b)
639 {
640 sink_error = Some(e);
642 }
643 }
644 }
645 Incoming::Json(Response::ResultEnd { .. }) => break,
646 Incoming::Json(Response::Error { message }) => {
647 return Err(HostError::Plugin {
648 plugin: self.label.clone(),
649 message,
650 });
651 }
652 other => return Err(self.unexpected("result data", &other)),
653 }
654 }
655 if let Some(e) = sink_error {
656 return Err(HostError::Plugin {
657 plugin: self.label.clone(),
658 message: e,
659 });
660 }
661 let schema = schema.ok_or_else(|| HostError::Malformed {
662 plugin: self.label.clone(),
663 message: "a result ended without any Arrow frame carrying its schema".into(),
664 })?;
665 Ok(Execution::Result { schema, rows })
666 }
667
668 pub fn write_begin(
670 &mut self,
671 path: &str,
672 format: &str,
673 options: Map<String, Value>,
674 result_sets: Vec<ResultSetMeta>,
675 template: Option<Value>,
676 ) -> Result<()> {
677 self.send(&Request::Write {
678 path: path.to_string(),
679 format: format.to_string(),
680 options,
681 result_sets,
682 template,
683 })
684 }
685
686 pub fn write_result_set(
688 &mut self,
689 schema: &SchemaRef,
690 batches: impl IntoIterator<Item = RecordBatch>,
691 ) -> Result<()> {
692 let mut any = false;
693 for b in batches {
694 any = true;
695 self.send_batch(&b)?;
696 }
697 if !any {
698 self.send_batch(&RecordBatch::new_empty(schema.clone()))?;
699 }
700 self.send(&Request::ResultSetEnd {})
701 }
702
703 pub fn load(
706 &mut self,
707 name: &str,
708 schema: &SchemaRef,
709 batches: impl IntoIterator<Item = RecordBatch>,
710 ) -> Result<(String, Option<String>)> {
711 self.send(&Request::Load {
712 name: name.to_string(),
713 })?;
714 self.write_result_set(schema, batches)?;
715 match self.recv_json("a loaded reply")? {
716 Response::Loaded {
717 relation, warning, ..
718 } => Ok((relation, warning)),
719 other => Err(self.unexpected("a loaded reply", &Incoming::Json(other))),
720 }
721 }
722
723 pub fn write_finish(&mut self) -> Result<Vec<String>> {
724 Ok(self.write_finish_with_warnings()?.0)
725 }
726
727 pub fn write_finish_with_warnings(&mut self) -> Result<(Vec<String>, Vec<String>)> {
729 self.send(&Request::Finish {})?;
730 match self.recv_json("a written reply")? {
731 Response::Written { files, warnings } => Ok((files, warnings)),
732 other => Err(self.unexpected("a written reply", &Incoming::Json(other))),
733 }
734 }
735
736 pub fn deliver(
738 &mut self,
739 local_path: &str,
740 remote_path: Option<&str>,
741 connection: Map<String, Value>,
742 ) -> Result<String> {
743 let file = DeliveryFile {
744 local_path: local_path.to_string(),
745 remote_path: remote_path.map(str::to_string),
746 };
747 self.deliver_files(&[file], connection, Map::new())
748 }
749
750 pub fn deliver_files(
753 &mut self,
754 files: &[DeliveryFile],
755 connection: Map<String, Value>,
756 options: Map<String, Value>,
757 ) -> Result<String> {
758 let req = match files {
759 [] => {
760 return Err(HostError::Malformed {
761 plugin: self.label.clone(),
762 message: "nothing to deliver".into(),
763 });
764 }
765 [one] => Request::Deliver {
766 local_path: Some(one.local_path.clone()),
767 remote_path: one.remote_path.clone(),
768 files: Vec::new(),
769 connection,
770 options,
771 message: None,
772 },
773 many => {
774 if !self.has(crate::CAP_MULTI_FILE) {
775 return Err(HostError::Plugin {
776 plugin: self.label.clone(),
777 message: format!(
778 "{} files in one delivery, but the plugin doesn't advertise `multi_file`",
779 many.len()
780 ),
781 });
782 }
783 Request::Deliver {
784 local_path: None,
785 remote_path: None,
786 files: many.to_vec(),
787 connection,
788 options,
789 message: None,
790 }
791 }
792 };
793 self.send(&req)?;
794 match self.recv_json("a delivered reply")? {
795 Response::Delivered { location } => Ok(location),
796 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
797 }
798 }
799
800 pub fn deliver_message(
802 &mut self,
803 message: &crate::msg::Message,
804 attach: &[DeliveryFile],
805 connection: Map<String, Value>,
806 options: Map<String, Value>,
807 ) -> Result<String> {
808 if !self.has(crate::CAP_MESSAGE) {
809 return Err(HostError::Plugin {
810 plugin: self.label.clone(),
811 message: "a message, but the plugin doesn't advertise `message`".into(),
812 });
813 }
814 self.send(&Request::Deliver {
815 local_path: None,
816 remote_path: None,
817 files: attach.to_vec(),
818 connection,
819 options,
820 message: Some(message.clone()),
821 })?;
822 match self.recv_json("a delivered reply")? {
823 Response::Delivered { location } => Ok(location),
824 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
825 }
826 }
827
828 pub fn close(mut self) -> Result<()> {
830 let _ = self.send(&Request::Close {});
831 let _ = self.recv(Some(Duration::from_secs(5)), "the close reply");
832 self.stdin.take();
833 for _ in 0..250 {
834 if let Ok(Some(_)) = self.child.try_wait() {
835 return Ok(());
836 }
837 std::thread::sleep(Duration::from_millis(20));
838 }
839 let _ = self.child.kill();
840 Ok(())
841 }
842
843 pub fn write_raw(&mut self, bytes: &[u8]) -> Result<()> {
845 let ok = match self.stdin.as_mut() {
846 Some(w) => w.write_all(bytes).and_then(|_| w.flush()).is_ok(),
847 None => false,
848 };
849 if ok { Ok(()) } else { Err(self.write_failed()) }
850 }
851
852 pub fn wait_exit(&mut self, timeout: Duration) -> Option<ExitStatus> {
854 self.stdin.take();
855 let deadline = std::time::Instant::now() + timeout;
856 while std::time::Instant::now() < deadline {
857 if let Ok(Some(s)) = self.child.try_wait() {
858 return Some(s);
859 }
860 std::thread::sleep(Duration::from_millis(20));
861 }
862 None
863 }
864}
865
866impl PluginProcess {
867 fn drain_stderr(&mut self) {
869 if let Some(t) = self.stderr_thread.take() {
870 let deadline = std::time::Instant::now() + Duration::from_secs(2);
871 while !t.is_finished() && std::time::Instant::now() < deadline {
872 std::thread::sleep(Duration::from_millis(5));
873 }
874 if t.is_finished() {
875 let _ = t.join();
876 }
877 }
878 }
879}
880
881impl Drop for PluginProcess {
882 fn drop(&mut self) {
883 self.stdin.take();
884 if let Ok(None) = self.child.try_wait() {
885 for _ in 0..25 {
887 if let Ok(Some(_)) = self.child.try_wait() {
888 break;
889 }
890 std::thread::sleep(Duration::from_millis(10));
891 }
892 let _ = self.child.kill();
893 let _ = self.child.wait();
894 }
895 self.drain_stderr();
896 }
897}
898
899static CORE_VERSION: OnceLock<String> = OnceLock::new();
900
901pub fn set_core_version(version: &str) {
904 let _ = CORE_VERSION.set(version.to_string());
905}
906
907fn core_version() -> String {
908 CORE_VERSION.get().cloned().unwrap_or_else(|| "unreleased".into())
909}