cobble_core/minecraft/launch/detached_launch/
unix.rs1use super::GameProcess;
2use crate::error::{LaunchError, LaunchResult};
3use async_trait::async_trait;
4use fork::{fork, Fork};
5use ipc_channel::ipc::{bytes_channel, IpcBytesReceiver, IpcBytesSender, TryRecvError};
6use nix::sys::signal::{kill, Signal};
7use nix::sys::wait::waitpid;
8use nix::unistd::Pid;
9use parking_lot::Mutex;
10use std::process::{exit, Command};
11use std::sync::Arc;
12use std::thread::sleep;
13use std::time::Duration;
14use tokio::task;
15
16#[cfg_attr(doc_cfg, doc(cfg(unix)))]
18#[derive(Clone, Debug)]
19pub struct GameProcessHandle {
20 pid: Arc<Mutex<Option<Pid>>>,
21 stop: Arc<Mutex<Option<IpcBytesSender>>>,
22 stopped: Arc<Mutex<Option<IpcBytesReceiver>>>,
23}
24
25#[async_trait]
26impl GameProcess for GameProcessHandle {
27 fn launch(mut command: Command) -> LaunchResult<Self> {
31 let (stop_sender, stop_receiver) = bytes_channel()?;
32 let (stopped_sender, stopped_receiver) = bytes_channel()?;
33
34 match fork() {
35 Ok(Fork::Child) => {
36 let mut child = match command.spawn() {
37 Ok(child) => child,
38 Err(err) => {
39 error!("{}", err);
40 exit(1);
41 }
42 };
43 let pid = Pid::from_raw(child.id() as i32);
44
45 loop {
46 match child.try_wait() {
48 Ok(Some(status)) => {
49 match status.success() {
51 true => debug!("Game exited successfully"),
52 false => warn!("Game exited with status '{:?}'", status.code()),
53 }
54
55 if let Err(err) = stopped_sender.send(&[]) {
57 error!("{}", err);
58 exit(1);
59 }
60
61 exit(0);
62 }
63 Err(err) => {
64 error!("Could not check if game is still running: {}", err);
65 exit(1);
66 }
67 _ => {}
68 }
69
70 match stop_receiver.try_recv() {
72 Ok(_) => {
73 let signal = Signal::SIGINT;
75 if let Err(err) = kill(pid, signal) {
76 error!("{}", err);
77 exit(1);
78 }
79 }
80 Err(TryRecvError::Empty) => {}
81 Err(err) => {
82 error!("{}", err);
83 exit(1);
84 }
85 }
86
87 sleep(Duration::from_millis(500));
88 }
89 }
90 Ok(Fork::Parent(pid)) => {
91 debug!("Child process forked with PID {}", pid);
92 Ok(Self {
93 pid: Arc::new(Mutex::new(Some(Pid::from_raw(pid)))),
94 stop: Arc::new(Mutex::new(Some(stop_sender))),
95 stopped: Arc::new(Mutex::new(Some(stopped_receiver))),
96 })
97 }
98 Err(err) => {
99 error!("{}", err);
100 Err(LaunchError::ProcessForking)
101 }
102 }
103 }
104
105 async fn stop(&self) -> LaunchResult<()> {
108 let sender = self.stop.clone();
109 task::spawn_blocking(move || {
110 let sender = sender.lock();
111 if let Some(sender) = sender.as_ref() {
112 sender.send(&[])?;
113 }
114 Ok(())
115 })
116 .await
117 .unwrap()
118 }
119
120 async fn wait(&self) -> LaunchResult<()> {
123 let handle = Self {
124 pid: self.pid.clone(),
125 stop: self.stop.clone(),
126 stopped: self.stopped.clone(),
127 };
128
129 task::spawn_blocking(move || {
130 loop {
132 if handle.is_stopped_blocking()? {
133 break;
134 }
135
136 sleep(Duration::from_millis(500));
138 }
139
140 let mut pid = handle.pid.lock();
142 let mut stop = handle.stop.lock();
143 let mut stopped = handle.stopped.lock();
144
145 if let Some(pid) = pid.as_ref() {
146 waitpid(Some(*pid), None).map_err(|err| {
147 error!("{}", err);
148 LaunchError::WaitPid
149 })?;
150 }
151
152 *pid = None;
153 *stop = None;
154 *stopped = None;
155
156 Ok(())
157 })
158 .await
159 .unwrap()
160 }
161
162 async fn is_stopped(&self) -> LaunchResult<bool> {
164 let receiver = self.stopped.clone();
165 task::spawn_blocking(move || {
166 let receiver = receiver.lock();
167
168 match receiver.as_ref() {
169 Some(receiver) => match receiver.try_recv() {
170 Ok(_) => Ok(true),
171 Err(TryRecvError::Empty) => Ok(false),
172 Err(TryRecvError::IpcError(err)) => Err(LaunchError::Ipc(err)),
173 },
174 None => Ok(true),
175 }
176 })
177 .await
178 .unwrap()
179 }
180
181 fn is_stopped_blocking(&self) -> LaunchResult<bool> {
183 let receiver = self.stopped.lock();
184
185 match receiver.as_ref() {
186 Some(receiver) => match receiver.try_recv() {
187 Ok(_) => Ok(true),
188 Err(TryRecvError::Empty) => Ok(false),
189 Err(TryRecvError::IpcError(err)) => Err(LaunchError::Ipc(err)),
190 },
191 None => Ok(true),
192 }
193 }
194}