mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-14 21:20:48 +08:00
fix(service): harden transient IPC handling
This commit is contained in:
@@ -17,11 +17,15 @@ use log::{Level, LevelFilter, Record};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
|
||||
use crate::{
|
||||
core::service,
|
||||
core::{CoreManager, manager::RunningMode, service},
|
||||
singleton,
|
||||
utils::dirs::{self, sidecar_log_dir},
|
||||
};
|
||||
|
||||
const fn should_sync_service_writer(running_mode: RunningMode) -> bool {
|
||||
matches!(running_mode, RunningMode::Service)
|
||||
}
|
||||
|
||||
pub struct Logger {
|
||||
handle: Arc<Mutex<Option<LoggerHandle>>>,
|
||||
sidecar_file_writer: Arc<RwLock<Option<FileLogWriter>>>,
|
||||
@@ -178,14 +182,17 @@ impl Logger {
|
||||
let sidecar_writer = self.generate_sidecar_writer()?;
|
||||
*self.sidecar_file_writer.write() = Some(sidecar_writer);
|
||||
|
||||
// update service writer config
|
||||
if service::SERVICE_MANAGER.is_ready_cached() {
|
||||
service::update_writer_by_service(&clash_verge_service_ipc::WriterConfig {
|
||||
// The service writer is auxiliary to the local logger. Synchronize it only
|
||||
// for an active service session and do not roll back local settings on failure.
|
||||
if should_sync_service_writer(*CoreManager::global().get_running_mode())
|
||||
&& let Err(error) = service::update_writer_by_service(&clash_verge_service_ipc::WriterConfig {
|
||||
directory: String::new(),
|
||||
max_log_size: log_max_size * 1024,
|
||||
max_log_files: log_max_count,
|
||||
})
|
||||
.await?;
|
||||
.await
|
||||
{
|
||||
logging!(warn, Type::Service, "failed to update service writer config: {error:#}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -224,3 +231,16 @@ impl Logger {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::should_sync_service_writer;
|
||||
use crate::core::manager::RunningMode;
|
||||
|
||||
#[test]
|
||||
fn service_writer_sync_requires_service_running_mode() {
|
||||
assert!(should_sync_service_writer(RunningMode::Service));
|
||||
assert!(!should_sync_service_writer(RunningMode::Sidecar));
|
||||
assert!(!should_sync_service_writer(RunningMode::NotRunning));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,6 @@ const fn classify_service_install_state(current: CurrentServiceProbe, has_instal
|
||||
match current {
|
||||
CurrentServiceProbe::Ready => ServiceInstallState::Ready,
|
||||
CurrentServiceProbe::VersionMismatch => ServiceInstallState::NeedsReinstall,
|
||||
CurrentServiceProbe::Unavailable if has_install_marker => ServiceInstallState::NeedsReinstall,
|
||||
CurrentServiceProbe::Unavailable => ServiceInstallState::Unavailable,
|
||||
CurrentServiceProbe::Missing if has_install_marker => ServiceInstallState::NeedsReinstall,
|
||||
CurrentServiceProbe::Missing => ServiceInstallState::NotInstalled,
|
||||
@@ -304,7 +303,7 @@ fn trusted_service_evidence() -> Result<bool> {
|
||||
}
|
||||
|
||||
async fn detect_service_install_state() -> ServiceInstallState {
|
||||
let markers = match trusted_service_evidence() {
|
||||
let has_trusted_install_marker = match trusted_service_evidence() {
|
||||
Ok(exists) => exists,
|
||||
Err(error) => {
|
||||
logging!(
|
||||
@@ -316,7 +315,7 @@ async fn detect_service_install_state() -> ServiceInstallState {
|
||||
}
|
||||
};
|
||||
|
||||
if !markers {
|
||||
if !has_trusted_install_marker {
|
||||
return classify_service_install_state(CurrentServiceProbe::Missing, false);
|
||||
}
|
||||
|
||||
@@ -329,7 +328,7 @@ async fn detect_service_install_state() -> ServiceInstallState {
|
||||
}
|
||||
};
|
||||
|
||||
classify_service_install_state(current, markers)
|
||||
classify_service_install_state(current, has_trusted_install_marker)
|
||||
}
|
||||
|
||||
pub struct ServiceManager {
|
||||
@@ -1412,7 +1411,6 @@ impl ServiceManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub async fn confirm_ready(&self) -> Result<()> {
|
||||
let reply = probe_service_version_once()
|
||||
.await
|
||||
@@ -1545,14 +1543,7 @@ impl ServiceManager {
|
||||
}
|
||||
|
||||
pub async fn refresh(&self) -> Result<()> {
|
||||
self.run_operation(async {
|
||||
apply_service_version_result(
|
||||
self,
|
||||
probe_service_version_once().await,
|
||||
"failed to refresh service protocol",
|
||||
)
|
||||
})
|
||||
.await
|
||||
self.run_operation(async { self.confirm_ready().await }).await
|
||||
}
|
||||
|
||||
pub async fn handle_service_status(&self, status: ServiceStatus) -> Result<()> {
|
||||
@@ -2204,7 +2195,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
classify_service_install_state(CurrentServiceProbe::Unavailable, true),
|
||||
ServiceInstallState::NeedsReinstall
|
||||
ServiceInstallState::Unavailable
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ interface ProxyEmptyStateInput {
|
||||
const isSubscriptionProfile = (item: { type?: string }): boolean =>
|
||||
item.type === 'remote' || item.type === 'local'
|
||||
|
||||
export const hasRenderableProxyContent = (
|
||||
const hasRenderableProxyContent = (
|
||||
proxyView: ProxyViewV1 | undefined,
|
||||
mode: string,
|
||||
isChainMode: boolean,
|
||||
|
||||
@@ -135,7 +135,7 @@ const ProxyControlSwitches = ({
|
||||
const { uninstallServiceAndStartSidecar } = useServiceUninstaller()
|
||||
const { indicator: systemProxyIndicator, toggleSystemProxy } =
|
||||
useSystemProxyState()
|
||||
const { isServiceOk, isTunModeAvailable, mutateSystemState } =
|
||||
const { isServiceInstallReady, isTunModeAvailable, mutateSystemState } =
|
||||
useSystemState()
|
||||
|
||||
const sysproxyRef = useRef<DialogRef>(null)
|
||||
@@ -225,7 +225,7 @@ const ProxyControlSwitches = ({
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{isServiceOk && (
|
||||
{isServiceInstallReady && (
|
||||
<TooltipIcon
|
||||
title={t(
|
||||
'settings.sections.proxyControl.actions.uninstallService',
|
||||
|
||||
@@ -16,14 +16,14 @@ import { useVisibility } from './use-visibility'
|
||||
export interface SystemState {
|
||||
runningMode: RunningMode
|
||||
isAdminMode: boolean
|
||||
isServiceOk: boolean
|
||||
isServiceInstallReady: boolean
|
||||
serviceInstallState: ServiceInstallState | null
|
||||
}
|
||||
|
||||
const defaultSystemState = {
|
||||
runningMode: 'Sidecar',
|
||||
isAdminMode: false,
|
||||
isServiceOk: false,
|
||||
isServiceInstallReady: false,
|
||||
serviceInstallState: null,
|
||||
} as SystemState
|
||||
|
||||
@@ -36,19 +36,19 @@ export const fetchSystemState = async (): Promise<SystemState> => {
|
||||
return {
|
||||
runningMode,
|
||||
isAdminMode,
|
||||
isServiceOk: serviceInstallState === 'ready',
|
||||
isServiceInstallReady: serviceInstallState === 'ready',
|
||||
serviceInstallState,
|
||||
}
|
||||
}
|
||||
|
||||
const isServiceChoicePending = (
|
||||
state: ServiceInstallState | null,
|
||||
serviceAvailable: boolean,
|
||||
serviceInstallReady: boolean,
|
||||
queryFailed: boolean,
|
||||
) =>
|
||||
queryFailed ||
|
||||
state === 'sidecarAllowed' ||
|
||||
!(state === 'notInstalled' || (state === 'ready' && serviceAvailable))
|
||||
!(state === 'notInstalled' || (state === 'ready' && serviceInstallReady))
|
||||
|
||||
// Grace period for service initialization during startup
|
||||
const STARTUP_GRACE_MS = 10_000
|
||||
@@ -81,17 +81,18 @@ export function useSystemState() {
|
||||
|
||||
const isSidecarMode = systemState.runningMode === 'Sidecar'
|
||||
const isServiceMode = systemState.runningMode === 'Service'
|
||||
const isTunModeAvailable = systemState.isAdminMode || systemState.isServiceOk
|
||||
const isTunModeAvailable =
|
||||
systemState.isAdminMode || systemState.isServiceInstallReady
|
||||
const serviceChoicePending = isServiceChoicePending(
|
||||
systemState.serviceInstallState,
|
||||
systemState.isServiceOk,
|
||||
systemState.isServiceInstallReady,
|
||||
systemStateError !== undefined,
|
||||
)
|
||||
|
||||
return {
|
||||
runningMode: systemState.runningMode,
|
||||
isAdminMode: systemState.isAdminMode,
|
||||
isServiceOk: systemState.isServiceOk,
|
||||
isServiceInstallReady: systemState.isServiceInstallReady,
|
||||
isSidecarMode,
|
||||
isServiceMode,
|
||||
isTunModeAvailable,
|
||||
|
||||
Reference in New Issue
Block a user