Files
wayvr/src/backend/wayvr/process.rs
2025-01-19 15:20:52 +01:00

184 lines
4.8 KiB
Rust

use wayvr_ipc::packet_server;
use crate::gen_id;
use super::display;
#[derive(Debug)]
pub struct WayVRProcess {
pub auth_key: String,
pub child: std::process::Child,
pub display_handle: display::DisplayHandle,
pub exec_path: String,
pub args: Vec<String>,
pub env: Vec<(String, String)>,
}
#[derive(Debug)]
pub struct ExternalProcess {
pub pid: u32,
pub display_handle: display::DisplayHandle,
}
#[derive(Debug)]
pub enum Process {
Managed(WayVRProcess), // Process spawned by WayVR
External(ExternalProcess), // External process not directly controlled by us
}
impl Process {
pub fn display_handle(&self) -> display::DisplayHandle {
match self {
Process::Managed(p) => p.display_handle,
Process::External(p) => p.display_handle,
}
}
pub fn is_running(&mut self) -> bool {
match self {
Process::Managed(p) => p.is_running(),
Process::External(p) => p.is_running(),
}
}
pub fn terminate(&mut self) {
match self {
Process::Managed(p) => p.terminate(),
Process::External(p) => p.terminate(),
}
}
pub fn get_name(&self) -> String {
match self {
Process::Managed(p) => p.get_name().unwrap_or(String::from("unknown")),
Process::External(p) => p.get_name().unwrap_or(String::from("unknown")),
}
}
pub fn to_packet(&self, handle: ProcessHandle) -> packet_server::WvrProcess {
match self {
Process::Managed(p) => packet_server::WvrProcess {
name: p.get_name().unwrap_or(String::from("unknown")),
display_handle: p.display_handle.as_packet(),
handle: handle.as_packet(),
},
Process::External(p) => packet_server::WvrProcess {
name: p.get_name().unwrap_or(String::from("unknown")),
display_handle: p.display_handle.as_packet(),
handle: handle.as_packet(),
},
}
}
}
impl Drop for WayVRProcess {
fn drop(&mut self) {
log::info!(
"Sending SIGTERM (graceful exit) to process {}",
self.exec_path.as_str()
);
self.terminate();
}
}
impl WayVRProcess {
fn is_running(&mut self) -> bool {
match self.child.try_wait() {
Ok(Some(_exit_status)) => false,
Ok(None) => true,
Err(e) => {
// this shouldn't happen
log::error!("Child::try_wait failed: {}", e);
false
}
}
}
fn terminate(&mut self) {
unsafe {
// Gracefully stop process
libc::kill(self.child.id() as i32, libc::SIGTERM);
}
}
pub fn get_name(&self) -> Option<String> {
get_exec_name_from_pid(self.child.id())
}
}
fn get_exec_name_from_pid(pid: u32) -> Option<String> {
let path = format!("/proc/{}/exe", pid);
match std::fs::read_link(&path) {
Ok(buf) => {
if let Some(process_name) = buf.file_name().and_then(|s| s.to_str()) {
return Some(String::from(process_name));
}
None
}
Err(_) => None,
}
}
impl ExternalProcess {
fn is_running(&self) -> bool {
if self.pid == 0 {
false
} else {
std::fs::metadata(format!("/proc/{}", self.pid)).is_ok()
}
}
fn terminate(&mut self) {
if self.pid != 0 {
unsafe {
// send SIGINT (^C)
libc::kill(self.pid as i32, libc::SIGINT);
}
}
self.pid = 0;
}
pub fn get_name(&self) -> Option<String> {
get_exec_name_from_pid(self.pid)
}
}
gen_id!(ProcessVec, Process, ProcessCell, ProcessHandle);
pub fn find_by_pid(processes: &ProcessVec, pid: u32) -> Option<ProcessHandle> {
for (idx, cell) in processes.vec.iter().enumerate() {
if let Some(cell) = cell {
match &cell.obj {
Process::Managed(wayvr_process) => {
if wayvr_process.child.id() == pid {
return Some(ProcessVec::get_handle(cell, idx));
}
}
Process::External(external_process) => {
if external_process.pid == pid {
return Some(ProcessVec::get_handle(cell, idx));
}
}
}
}
}
None
}
impl ProcessHandle {
pub fn from_packet(handle: packet_server::WvrProcessHandle) -> Self {
Self {
generation: handle.generation,
idx: handle.idx,
}
}
pub fn as_packet(&self) -> packet_server::WvrProcessHandle {
packet_server::WvrProcessHandle {
idx: self.idx,
generation: self.generation,
}
}
}