fix(sre): implement PRR-01 stale mount self-healing and PRR-02 win32 console close handler
This commit is contained in:
+35
-2
@@ -59,11 +59,36 @@ pub fn unmount_drive(drive_letter: char) -> Result<()> {
|
|||||||
fn run_net_use_mount(drive_str: &str, port: u16, session_token: &str) -> Result<()> {
|
fn run_net_use_mount(drive_str: &str, port: u16, session_token: &str) -> Result<()> {
|
||||||
let url = format!("http://127.0.0.1:{}/{}/", port, session_token);
|
let url = format!("http://127.0.0.1:{}/{}/", port, session_token);
|
||||||
|
|
||||||
let output = Command::new("net")
|
let mut output = Command::new("net")
|
||||||
.args(["use", drive_str, &url, "/persistent:no"])
|
.args(["use", drive_str, &url, "/persistent:no"])
|
||||||
.output()
|
.output()
|
||||||
.context("Fehler beim Ausführen des Befehls 'net use'")?;
|
.context("Fehler beim Ausführen des Befehls 'net use'")?;
|
||||||
|
|
||||||
|
// PRR-01: Selbstreparatur bei verwaister Zuordnung nach unsauberem Vorläufer (Systemfehler 85)
|
||||||
|
if !output.status.success() {
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let is_already_in_use = stderr.contains("85")
|
||||||
|
|| stderr.contains("bereits verwendet")
|
||||||
|
|| stderr.contains("already in use")
|
||||||
|
|| stdout.contains("85")
|
||||||
|
|| stdout.contains("bereits verwendet")
|
||||||
|
|| stdout.contains("already in use");
|
||||||
|
|
||||||
|
if is_already_in_use {
|
||||||
|
debug!("Verwaiste Zuordnung für {} entdeckt — führe automatische Bereinigung durch...", drive_str);
|
||||||
|
let _ = Command::new("net")
|
||||||
|
.args(["use", drive_str, "/delete", "/y"])
|
||||||
|
.output();
|
||||||
|
|
||||||
|
// Zweiter Versuch nach automatischer Bereinigung
|
||||||
|
output = Command::new("net")
|
||||||
|
.args(["use", drive_str, &url, "/persistent:no"])
|
||||||
|
.output()
|
||||||
|
.context("Fehler beim erneuten Ausführen des Befehls 'net use'")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
@@ -318,13 +343,21 @@ pub async fn mount_container(
|
|||||||
println!(" [{}] Drücke [Ctrl+C] oder nutze das Tray-Icon zum Beenden.", ui::yellow("Tipp"));
|
println!(" [{}] Drücke [Ctrl+C] oder nutze das Tray-Icon zum Beenden.", ui::yellow("Tipp"));
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
// Warten auf Beendigungssignal (Ctrl+C, Tray-Klick, Inaktivität, Win+L)
|
// Windows Console Close Monitor (CTRL_CLOSE_EVENT / CTRL_SHUTDOWN_EVENT)
|
||||||
|
let (console_close_tx, mut console_close_rx) = tokio::sync::mpsc::channel::<()>(1);
|
||||||
|
let _console_guard = crate::windows::start_console_ctrl_monitor(console_close_tx, drive_letter).ok();
|
||||||
|
|
||||||
|
// Warten auf Beendigungssignal (Ctrl+C, Tray-Klick, Inaktivität, Win+L, Konsolenfenster-Schließen)
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
res = tokio::signal::ctrl_c() => {
|
res = tokio::signal::ctrl_c() => {
|
||||||
let _ = res;
|
let _ = res;
|
||||||
println!();
|
println!();
|
||||||
println!(" {} Beendigungssignal (Ctrl+C) empfangen.", ui::yellow("[!]"));
|
println!(" {} Beendigungssignal (Ctrl+C) empfangen.", ui::yellow("[!]"));
|
||||||
}
|
}
|
||||||
|
_ = console_close_rx.recv() => {
|
||||||
|
println!();
|
||||||
|
println!(" {} Konsolenfenster wird geschlossen — sichere Trennung ausgeführt!", ui::yellow("[!]"));
|
||||||
|
}
|
||||||
_ = tray_shutdown_rx.recv() => {
|
_ = tray_shutdown_rx.recv() => {
|
||||||
println!();
|
println!();
|
||||||
println!(" {} Beendigungssignal aus System-Tray empfangen.", ui::yellow("[!]"));
|
println!(" {} Beendigungssignal aus System-Tray empfangen.", ui::yellow("[!]"));
|
||||||
|
|||||||
+117
@@ -419,6 +419,114 @@ pub fn start_session_lock_monitor(
|
|||||||
Ok(SessionLockGuard)
|
Ok(SessionLockGuard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────
|
||||||
|
// Console Close Event Monitor (CTRL_CLOSE_EVENT / CTRL_SHUTDOWN_EVENT)
|
||||||
|
// ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
static CONSOLE_CTRL_TX: std::sync::Mutex<Option<tokio::sync::mpsc::Sender<()>>> = std::sync::Mutex::new(None);
|
||||||
|
#[cfg(windows)]
|
||||||
|
static CONSOLE_CTRL_DRIVE: std::sync::Mutex<Option<char>> = std::sync::Mutex::new(None);
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
unsafe extern "system" fn console_ctrl_routine(ctrl_type: u32) -> i32 {
|
||||||
|
const CTRL_C_EVENT: u32 = 0;
|
||||||
|
const CTRL_BREAK_EVENT: u32 = 1;
|
||||||
|
const CTRL_CLOSE_EVENT: u32 = 2;
|
||||||
|
const CTRL_LOGOFF_EVENT: u32 = 5;
|
||||||
|
const CTRL_SHUTDOWN_EVENT: u32 = 6;
|
||||||
|
|
||||||
|
match ctrl_type {
|
||||||
|
CTRL_CLOSE_EVENT | CTRL_LOGOFF_EVENT | CTRL_SHUTDOWN_EVENT => {
|
||||||
|
// 1. Sofortiges Notfall-Unmount direkt aus dem Win32-Callback ausführen
|
||||||
|
if let Ok(guard) = CONSOLE_CTRL_DRIVE.lock() {
|
||||||
|
if let Some(dl) = *guard {
|
||||||
|
let drive_str = format!("{}:", dl.to_ascii_uppercase());
|
||||||
|
let _ = std::process::Command::new("net")
|
||||||
|
.args(["use", &drive_str, "/delete", "/y"])
|
||||||
|
.output();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2. Asynchronen Shutdown-Kanal benachrichtigen (für SQLite WAL Checkpoint)
|
||||||
|
if let Ok(guard) = CONSOLE_CTRL_TX.lock() {
|
||||||
|
if let Some(ref tx) = *guard {
|
||||||
|
let _ = tx.blocking_send(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1 // TRUE: Event abgearbeitet
|
||||||
|
}
|
||||||
|
CTRL_C_EVENT | CTRL_BREAK_EVENT => {
|
||||||
|
// Ctrl+C wird primär von tokio::signal::ctrl_c() behandelt
|
||||||
|
0
|
||||||
|
}
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RAII Guard zur sauberen Deregistrierung des Win32 Console-Control-Handlers.
|
||||||
|
pub struct ConsoleCtrlGuard;
|
||||||
|
|
||||||
|
impl Drop for ConsoleCtrlGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
extern "system" {
|
||||||
|
fn SetConsoleCtrlHandler(
|
||||||
|
handler: Option<unsafe extern "system" fn(u32) -> i32>,
|
||||||
|
add: i32,
|
||||||
|
) -> i32;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
SetConsoleCtrlHandler(Some(console_ctrl_routine), 0);
|
||||||
|
}
|
||||||
|
if let Ok(mut guard) = CONSOLE_CTRL_TX.lock() {
|
||||||
|
*guard = None;
|
||||||
|
}
|
||||||
|
if let Ok(mut guard) = CONSOLE_CTRL_DRIVE.lock() {
|
||||||
|
*guard = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registriert einen Win32 Console Control Handler für CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT
|
||||||
|
/// und CTRL_SHUTDOWN_EVENT, um verwaiste Netzlaufwerke beim Schließen des Konsolenfensters zu verhindern.
|
||||||
|
pub fn start_console_ctrl_monitor(
|
||||||
|
shutdown_tx: tokio::sync::mpsc::Sender<()>,
|
||||||
|
drive_letter: char,
|
||||||
|
) -> Result<ConsoleCtrlGuard> {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
extern "system" {
|
||||||
|
fn SetConsoleCtrlHandler(
|
||||||
|
handler: Option<unsafe extern "system" fn(u32) -> i32>,
|
||||||
|
add: i32,
|
||||||
|
) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut guard) = CONSOLE_CTRL_TX.lock() {
|
||||||
|
*guard = Some(shutdown_tx);
|
||||||
|
}
|
||||||
|
if let Ok(mut guard) = CONSOLE_CTRL_DRIVE.lock() {
|
||||||
|
*guard = Some(drive_letter);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = unsafe { SetConsoleCtrlHandler(Some(console_ctrl_routine), 1) };
|
||||||
|
if res == 0 {
|
||||||
|
bail!("Konnte Win32 SetConsoleCtrlHandler nicht registrieren");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ConsoleCtrlGuard)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
let _ = shutdown_tx;
|
||||||
|
let _ = drive_letter;
|
||||||
|
Ok(ConsoleCtrlGuard)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -438,4 +546,13 @@ mod tests {
|
|||||||
// Dropping monitor closes message loop and joins thread cleanly
|
// Dropping monitor closes message loop and joins thread cleanly
|
||||||
drop(monitor);
|
drop(monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_console_ctrl_monitor_lifecycle() {
|
||||||
|
let (tx, _rx) = tokio::sync::mpsc::channel(1);
|
||||||
|
let monitor = start_console_ctrl_monitor(tx, 'Z');
|
||||||
|
assert!(monitor.is_ok());
|
||||||
|
drop(monitor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user