fix kill occupied ipc process, find with enumerate, kill with NtTerminateProcess (#8289)
* I reproduced the issue, that process did't have title, couldn't be connected to and taskkill not work * Test whether ipc is opccupied with enumerating named pipe * With NtTerminateProcess, it was killed successfully. * There is a way to find the exact process which occupy the ipc, I have not check it, it's from https://github.com/winsiderss/systeminformer Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
		
							parent
							
								
									987da00be0
								
							
						
					
					
						commit
						0bb537b872
					
				| @ -1700,6 +1700,15 @@ pub fn read_custom_client(config: &str) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[inline] | ||||||
|  | pub fn is_empty_uni_link(arg: &str) -> bool { | ||||||
|  |     let prefix = crate::get_uri_prefix(); | ||||||
|  |     if !arg.starts_with(&prefix) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     arg[prefix.len()..].chars().all(|c| c == '/') | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use super::*; |     use super::*; | ||||||
|  | |||||||
| @ -22,15 +22,6 @@ macro_rules! my_println{ | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[inline] |  | ||||||
| fn is_empty_uni_link(arg: &str) -> bool { |  | ||||||
|     let prefix = crate::get_uri_prefix(); |  | ||||||
|     if !arg.starts_with(&prefix) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     arg[prefix.len()..].chars().all(|c| c == '/') |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// shared by flutter and sciter main function
 | /// shared by flutter and sciter main function
 | ||||||
| ///
 | ///
 | ||||||
| /// [Note]
 | /// [Note]
 | ||||||
| @ -168,7 +159,7 @@ pub fn core_main() -> Option<Vec<String>> { | |||||||
|     #[cfg(not(any(target_os = "android", target_os = "ios")))] |     #[cfg(not(any(target_os = "android", target_os = "ios")))] | ||||||
|     init_plugins(&args); |     init_plugins(&args); | ||||||
|     log::info!("main start args:{:?}", args); |     log::info!("main start args:{:?}", args); | ||||||
|     if args.is_empty() || is_empty_uni_link(&args[0]) { |     if args.is_empty() || crate::common::is_empty_uni_link(&args[0]) { | ||||||
|         std::thread::spawn(move || crate::start_server(false)); |         std::thread::spawn(move || crate::start_server(false)); | ||||||
|     } else { |     } else { | ||||||
|         #[cfg(windows)] |         #[cfg(windows)] | ||||||
|  | |||||||
							
								
								
									
										25
									
								
								src/ipc.rs
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								src/ipc.rs
									
									
									
									
									
								
							| @ -970,10 +970,27 @@ pub async fn test_rendezvous_server() -> ResultType<()> { | |||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[tokio::main(flavor = "current_thread")] | #[cfg(windows)] | ||||||
| pub async fn test_ipc_connection() -> ResultType<()> { | pub fn is_ipc_file_exist(suffix: &str) -> ResultType<bool> { | ||||||
|     connect(1000, "").await?; |     let file_name = format!("{}\\query{}", crate::get_app_name(), suffix); | ||||||
|     Ok(()) |     let mut err = None; | ||||||
|  |     for entry in std::fs::read_dir("\\\\.\\pipe\\")? { | ||||||
|  |         match entry { | ||||||
|  |             Ok(entry) => { | ||||||
|  |                 if entry.file_name().into_string().unwrap_or_default() == file_name { | ||||||
|  |                     return Ok(true); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Err(e) => { | ||||||
|  |                 err = Some(e); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if let Some(e) = err { | ||||||
|  |         Err(e.into()) | ||||||
|  |     } else { | ||||||
|  |         Ok(false) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[tokio::main(flavor = "current_thread")] | #[tokio::main(flavor = "current_thread")] | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ use hbb_common::{ | |||||||
| use std::process::{Command, Stdio}; | use std::process::{Command, Stdio}; | ||||||
| use std::{ | use std::{ | ||||||
|     collections::HashMap, |     collections::HashMap, | ||||||
|     ffi::OsString, |     ffi::{CString, OsString}, | ||||||
|     fs, io, |     fs, io, | ||||||
|     io::prelude::*, |     io::prelude::*, | ||||||
|     mem, |     mem, | ||||||
| @ -36,6 +36,7 @@ use winapi::{ | |||||||
|     um::{ |     um::{ | ||||||
|         errhandlingapi::GetLastError, |         errhandlingapi::GetLastError, | ||||||
|         handleapi::CloseHandle, |         handleapi::CloseHandle, | ||||||
|  |         libloaderapi::{GetProcAddress, LoadLibraryA}, | ||||||
|         minwinbase::STILL_ACTIVE, |         minwinbase::STILL_ACTIVE, | ||||||
|         processthreadsapi::{ |         processthreadsapi::{ | ||||||
|             GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess, |             GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess, | ||||||
| @ -47,8 +48,8 @@ use winapi::{ | |||||||
|         wingdi::*, |         wingdi::*, | ||||||
|         winnt::{ |         winnt::{ | ||||||
|             TokenElevation, ES_AWAYMODE_REQUIRED, ES_CONTINUOUS, ES_DISPLAY_REQUIRED, |             TokenElevation, ES_AWAYMODE_REQUIRED, ES_CONTINUOUS, ES_DISPLAY_REQUIRED, | ||||||
|             ES_SYSTEM_REQUIRED, HANDLE, PROCESS_QUERY_LIMITED_INFORMATION, TOKEN_ELEVATION, |             ES_SYSTEM_REQUIRED, HANDLE, PROCESS_ALL_ACCESS, PROCESS_QUERY_LIMITED_INFORMATION, | ||||||
|             TOKEN_QUERY, |             TOKEN_ELEVATION, TOKEN_QUERY, | ||||||
|         }, |         }, | ||||||
|         winreg::HKEY_CURRENT_USER, |         winreg::HKEY_CURRENT_USER, | ||||||
|         winuser::*, |         winuser::*, | ||||||
| @ -2420,36 +2421,80 @@ pub fn is_x64() -> bool { | |||||||
|     unsafe { sys_info.u.s().wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 } |     unsafe { sys_info.u.s().wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "flutter")] | pub fn try_kill_rustdesk_main_window_process() -> ResultType<()> { | ||||||
| pub fn try_kill_flutter_main_window_process() { |     // Kill rustdesk.exe without extra arg, should only be called by --server
 | ||||||
|     // It's called when --server failed to start ipc, because the ipc may be occupied by the main window process.
 |     // We can find the exact process which occupies the ipc, see more from https://github.com/winsiderss/systeminformer
 | ||||||
|     // When --service quit the ipc process, ipc process will call std::process::exit, std::process::exit not work may be the reason.
 |     log::info!("try kill rustdesk main window process"); | ||||||
|     // FindWindow not work in --service, https://forums.codeguru.com/showthread.php?169091-FindWindow-in-service
 |     use hbb_common::sysinfo::System; | ||||||
|     log::info!("try kill flutter main window process"); |     let mut sys = System::new(); | ||||||
|  |     sys.refresh_processes(); | ||||||
|  |     let my_uid = sys | ||||||
|  |         .process((std::process::id() as usize).into()) | ||||||
|  |         .map(|x| x.user_id()) | ||||||
|  |         .unwrap_or_default(); | ||||||
|  |     let my_pid = std::process::id(); | ||||||
|  |     let app_name = crate::get_app_name().to_lowercase(); | ||||||
|  |     if app_name.is_empty() { | ||||||
|  |         bail!("app name is empty"); | ||||||
|  |     } | ||||||
|  |     for (_, p) in sys.processes().iter() { | ||||||
|  |         let p_name = p.name().to_lowercase(); | ||||||
|  |         // name equal
 | ||||||
|  |         if !(p_name == app_name || p_name == app_name.clone() + ".exe") { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         // arg more than 1
 | ||||||
|  |         if p.cmd().len() < 1 { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         // first arg contain app name
 | ||||||
|  |         if !p.cmd()[0].to_lowercase().contains(&p_name) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         // only one arg or the second arg is empty uni link
 | ||||||
|  |         let is_empty_uni = p.cmd().len() == 2 && crate::common::is_empty_uni_link(&p.cmd()[1]); | ||||||
|  |         if !(p.cmd().len() == 1 || is_empty_uni) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         // skip self
 | ||||||
|  |         if p.pid().as_u32() == my_pid { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         // because we call it with --server, so we can check user_id, remove this if call it with user process
 | ||||||
|  |         if p.user_id() == my_uid { | ||||||
|  |             log::info!("user id equal, continue"); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         log::info!("try kill process: {:?}, pid = {:?}", p.cmd(), p.pid()); | ||||||
|  |         nt_terminate_process(p.pid().as_u32())?; | ||||||
|  |         log::info!("kill process success: {:?}, pid = {:?}", p.cmd(), p.pid()); | ||||||
|  |         return Ok(()); | ||||||
|  |     } | ||||||
|  |     bail!("failed to find rustdesk main window process"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn nt_terminate_process(process_id: DWORD) -> ResultType<()> { | ||||||
|  |     type NtTerminateProcess = unsafe extern "system" fn(HANDLE, DWORD) -> DWORD; | ||||||
|     unsafe { |     unsafe { | ||||||
|         let window_name = wide_string(&crate::get_app_name()); |         let h_module = LoadLibraryA(CString::new("ntdll.dll")?.as_ptr()); | ||||||
|         let class_name = wide_string(FLUTTER_RUNNER_WIN32_WINDOW_CLASS); |         if !h_module.is_null() { | ||||||
|         let hwnd = FindWindowW(class_name.as_ptr(), window_name.as_ptr()); |             let f_nt_terminate_process: NtTerminateProcess = std::mem::transmute(GetProcAddress( | ||||||
|         if hwnd.is_null() { |                 h_module, | ||||||
|             log::info!("not found flutter main window"); |                 CString::new("NtTerminateProcess")?.as_ptr(), | ||||||
|             return; |             )); | ||||||
|         } |             let h_token = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id); | ||||||
|         let mut process_id: u32 = 0; |             if !h_token.is_null() { | ||||||
|         GetWindowThreadProcessId(hwnd, &mut process_id as *mut u32); |                 if f_nt_terminate_process(h_token, 1) == 0 { | ||||||
|         if process_id == 0 { |                     log::info!("terminate process {} success", process_id); | ||||||
|             log::info!("failed to get flutter window process id"); |                     return Ok(()); | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         let output = Command::new("taskkill") |  | ||||||
|             .arg("/F") |  | ||||||
|             .arg("/PID") |  | ||||||
|             .arg(process_id.to_string()) |  | ||||||
|             .output() |  | ||||||
|             .expect("Failed to execute command"); |  | ||||||
|         if output.status.success() { |  | ||||||
|             log::info!("kill flutter main window process success"); |  | ||||||
|                 } else { |                 } else { | ||||||
|             log::error!("kill flutter main window process failed"); |                     bail!("NtTerminateProcess {} failed", process_id); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 bail!("OpenProcess {} failed", process_id); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             bail!("Failed to load ntdll.dll"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -470,10 +470,12 @@ pub async fn start_server(is_server: bool) { | |||||||
|         std::thread::spawn(move || { |         std::thread::spawn(move || { | ||||||
|             if let Err(err) = crate::ipc::start("") { |             if let Err(err) = crate::ipc::start("") { | ||||||
|                 log::error!("Failed to start ipc: {}", err); |                 log::error!("Failed to start ipc: {}", err); | ||||||
|                 #[cfg(all(windows, feature = "flutter"))] |                 #[cfg(windows)] | ||||||
|                 if crate::is_server() && crate::ipc::test_ipc_connection().is_ok() { |                 if crate::is_server() && crate::ipc::is_ipc_file_exist("").unwrap_or(false) { | ||||||
|                     log::error!("ipc is occupied by another process, try kill it"); |                     log::error!("ipc is occupied by another process, try kill it"); | ||||||
|                     crate::platform::try_kill_flutter_main_window_process(); |                     if let Err(e) = crate::platform::try_kill_rustdesk_main_window_process() { | ||||||
|  |                         log::error!("kill failed: {}", e); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|                 std::process::exit(-1); |                 std::process::exit(-1); | ||||||
|             } |             } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user