mirror of
https://github.com/Stremio/stremio-shell-ng.git
synced 2026-05-12 13:20:52 +00:00
fix: guard JobObject setup with Once and check return values
CreateJobObjectA / SetInformationJobObject / AssignProcessToJobObject were called inside the per-start thread and their return values were ignored. Two consequences: 1. Each server crash-restart created a fresh kernel JobObject HANDLE that was never CloseHandle'd. The HANDLE went out of scope when the spawned thread exited, leaking a kernel object every crash. 2. On Win 7/8 (single-job systems) and inside parent jobs that disallow breakaway, AssignProcessToJobObject silently failed, so stremio-runtime could survive the shell's death and hold port 11470. Hoist the setup into ensure_parent_job_object() guarded by sync::Once so it runs exactly once per shell process, and check each return value explicitly with a clear log message when the OS-level safety net is degraded. The HANDLE is intentionally not closed: closing it while KILL_ON_JOB_CLOSE is set would terminate the shell itself. Closes #47 Closes #48
This commit is contained in:
parent
a9d9673f2a
commit
2294f52407
1 changed files with 48 additions and 26 deletions
|
|
@ -2,13 +2,13 @@ use crate::stremio_app::constants::{SRV_BUFFER_SIZE, SRV_LOG_SIZE, STREMIO_SERVE
|
||||||
use native_windows_gui::{self as nwg, PartialUi};
|
use native_windows_gui::{self as nwg, PartialUi};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::{
|
use std::{
|
||||||
env, fs,
|
env, fs, io,
|
||||||
io::Read,
|
io::Read,
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
os::windows::process::CommandExt,
|
os::windows::process::CommandExt,
|
||||||
path,
|
path,
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex, Once},
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
use winapi::um::{
|
use winapi::um::{
|
||||||
|
|
@ -21,6 +21,50 @@ use winapi::um::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Guarded by Once: avoids HANDLE leak per crash and re-assignment failure on Win 7/8.
|
||||||
|
fn ensure_parent_job_object() {
|
||||||
|
static ONCE: Once = Once::new();
|
||||||
|
ONCE.call_once(|| unsafe {
|
||||||
|
let job = CreateJobObjectA(std::ptr::null_mut(), std::ptr::null_mut());
|
||||||
|
if job.is_null() {
|
||||||
|
eprintln!(
|
||||||
|
"CreateJobObjectA failed: {}; child stremio-runtime may outlive the shell on crash",
|
||||||
|
io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let jeli = JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
|
||||||
|
BasicLimitInformation: JOBOBJECT_BASIC_LIMIT_INFORMATION {
|
||||||
|
LimitFlags: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
|
||||||
|
| JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
|
||||||
|
| JOB_OBJECT_LIMIT_BREAKAWAY_OK,
|
||||||
|
..std::mem::zeroed()
|
||||||
|
},
|
||||||
|
..std::mem::zeroed()
|
||||||
|
};
|
||||||
|
if winapi::um::jobapi2::SetInformationJobObject(
|
||||||
|
job,
|
||||||
|
JobObjectExtendedLimitInformation,
|
||||||
|
&jeli as *const _ as *mut _,
|
||||||
|
std::mem::size_of::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>() as u32,
|
||||||
|
) == 0
|
||||||
|
{
|
||||||
|
eprintln!(
|
||||||
|
"SetInformationJobObject failed: {}",
|
||||||
|
io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if winapi::um::jobapi2::AssignProcessToJobObject(job, GetCurrentProcess()) == 0 {
|
||||||
|
eprintln!(
|
||||||
|
"AssignProcessToJobObject failed: {}; child stremio-runtime may outlive the shell",
|
||||||
|
io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Don't CloseHandle: KILL_ON_JOB_CLOSE would terminate the shell itself.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct StremioServer {
|
pub struct StremioServer {
|
||||||
development: bool,
|
development: bool,
|
||||||
|
|
@ -38,31 +82,9 @@ impl StremioServer {
|
||||||
let logs = self.logs.clone();
|
let logs = self.logs.clone();
|
||||||
let sender = self.crash_notice.sender();
|
let sender = self.crash_notice.sender();
|
||||||
|
|
||||||
|
ensure_parent_job_object();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
// Use Win32JobObject to kill the child process when the parent process is killed
|
|
||||||
// With the JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK and JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flags
|
|
||||||
unsafe {
|
|
||||||
let job_main_process = CreateJobObjectA(std::ptr::null_mut(), std::ptr::null_mut());
|
|
||||||
let jeli = JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
|
|
||||||
BasicLimitInformation: JOBOBJECT_BASIC_LIMIT_INFORMATION {
|
|
||||||
LimitFlags: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
|
|
||||||
| JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
|
|
||||||
| JOB_OBJECT_LIMIT_BREAKAWAY_OK,
|
|
||||||
..std::mem::zeroed()
|
|
||||||
},
|
|
||||||
..std::mem::zeroed()
|
|
||||||
};
|
|
||||||
winapi::um::jobapi2::SetInformationJobObject(
|
|
||||||
job_main_process,
|
|
||||||
JobObjectExtendedLimitInformation,
|
|
||||||
&jeli as *const _ as *mut _,
|
|
||||||
std::mem::size_of::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>() as u32,
|
|
||||||
);
|
|
||||||
winapi::um::jobapi2::AssignProcessToJobObject(
|
|
||||||
job_main_process,
|
|
||||||
GetCurrentProcess(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let mut path = env::current_exe()
|
let mut path = env::current_exe()
|
||||||
.and_then(fs::canonicalize)
|
.and_then(fs::canonicalize)
|
||||||
.expect("Cannot get the current executable path");
|
.expect("Cannot get the current executable path");
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue