mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-03 13:32:16 +08:00
fix(dns): save the DNS override decision on every runtime replacement
Refs #8431
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
- 修复 自定义 CSS 无法修改编辑器字体和字号的问题
|
||||
- 修复服务模式下,SSH 节点的 `private-key` 使用私钥文件时内核无法启动的问题
|
||||
- 修复服务模式下,内核更新的 GEO 数据库不会同步回应用目录,导致使用新分类的规则无法通过校验的问题
|
||||
- 修复订阅的 DNS 配置变化后,「DNS 覆写」实际已关闭但开关仍显示开启且没有提示的问题
|
||||
|
||||
**🍎 macOS**
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ pub async fn change_clash_core(clash_core: std::string::String) -> CmdResult<Opt
|
||||
|
||||
match CoreManager::global().change_core(&clash_core).await {
|
||||
Ok(_) => {
|
||||
logging_error!(Type::Config, Config::sync_dns_override().await);
|
||||
logging_error!(Type::Core, profiles_save_file_safe().await);
|
||||
|
||||
match CoreManager::global().restart_core().await {
|
||||
|
||||
@@ -6,6 +6,7 @@ use serde_yaml_ng::{Mapping, Value};
|
||||
use sha2::{Digest as _, Sha256};
|
||||
use smartstring::alias::String;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use tokio::sync::MutexGuard;
|
||||
|
||||
use super::{Config, IVerge};
|
||||
use crate::core::handle::Handle;
|
||||
@@ -128,7 +129,12 @@ impl IVerge {
|
||||
|
||||
impl Config {
|
||||
pub(crate) async fn sync_dns_override() -> Result<()> {
|
||||
let _config_write = Self::lock_config_write().await;
|
||||
let config_write = Self::lock_config_write().await;
|
||||
Self::sync_dns_override_locked(&config_write).await
|
||||
}
|
||||
|
||||
/// Callers must have ended their own draft transaction, which claims the same Verge layer.
|
||||
pub(crate) async fn sync_dns_override_locked(_config_write: &MutexGuard<'_, ()>) -> Result<()> {
|
||||
let state = Self::runtime().await.data_arc().dns_override.clone();
|
||||
let Some(state) = state else {
|
||||
return Ok(());
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::core::manager::CLASH_LOGGER;
|
||||
use crate::core::proxy_control::{self, SysproxyFailure};
|
||||
use crate::core::service::{SERVICE_MANAGER, ServiceStatus};
|
||||
use anyhow::Result;
|
||||
use clash_verge_logging::{Type, logging};
|
||||
use clash_verge_logging::{Type, logging, logging_error};
|
||||
use scopeguard::defer;
|
||||
use smartstring::alias::String;
|
||||
use std::path::Path;
|
||||
@@ -428,6 +428,7 @@ impl CoreManager {
|
||||
}
|
||||
// Drop the guard last so an earlier failure cannot leave a running Sidecar unguarded.
|
||||
proxy_control::stop_guard().await;
|
||||
let starts_core = matches!(*self.get_running_mode(), RunningMode::NotRunning);
|
||||
let result = async {
|
||||
self.start_core_inner().await?;
|
||||
if !matches!(*self.get_running_mode(), RunningMode::Sidecar)
|
||||
@@ -435,6 +436,10 @@ impl CoreManager {
|
||||
{
|
||||
anyhow::bail!("Sidecar did not become ready");
|
||||
}
|
||||
// A Sidecar that was already running never loaded this generation.
|
||||
if starts_core {
|
||||
Config::runtime().await.apply();
|
||||
}
|
||||
tracing::Span::current().record("readiness_generation", self.current_core_readiness_generation());
|
||||
self.apply_proxy_after_start().await
|
||||
}
|
||||
@@ -451,7 +456,12 @@ impl CoreManager {
|
||||
Ok(())
|
||||
}
|
||||
.await;
|
||||
result.map_err(|error| report_sidecar_failure(error, service_rejection))
|
||||
let result = result.map_err(|error| report_sidecar_failure(error, service_rejection));
|
||||
// Lock order is config then lifecycle.
|
||||
drop(_life);
|
||||
// A failure after the commit still leaves that runtime for the Sidecar or the next start.
|
||||
logging_error!(Type::Config, Config::sync_dns_override().await);
|
||||
result
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, level = "info", fields(readiness_generation = tracing::field::Empty))]
|
||||
@@ -463,7 +473,7 @@ impl CoreManager {
|
||||
self.finish_config_update();
|
||||
}
|
||||
// Lock order is config then lifecycle, for the unclaimed draft write below.
|
||||
let _config_write = Config::lock_config_write().await;
|
||||
let config_write = Config::lock_config_write().await;
|
||||
let _life = self.lifecycle_lock.lock().await;
|
||||
|
||||
self.controlled_stop_core_inner().await?;
|
||||
@@ -492,14 +502,22 @@ impl CoreManager {
|
||||
{
|
||||
anyhow::bail!("Sidecar did not become ready after service uninstall");
|
||||
}
|
||||
Config::runtime().await.apply();
|
||||
tracing::Span::current().record("readiness_generation", self.current_core_readiness_generation());
|
||||
self.apply_proxy_after_start().await
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
let cleanup = if result.is_err() {
|
||||
self.rollback_failed_sidecar_transition().await
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
// A failure after the commit still leaves that runtime for the Sidecar or the next start.
|
||||
logging_error!(Type::Config, Config::sync_dns_override_locked(&config_write).await);
|
||||
if let Err(error) = result {
|
||||
if let Err(cleanup_error) = self.rollback_failed_sidecar_transition().await {
|
||||
if let Err(cleanup_error) = cleanup {
|
||||
return Err(proxy_control::rollback_failure(error, cleanup_error)
|
||||
.context("failed to clear the proxy before rolling back"));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ mod state;
|
||||
|
||||
use anyhow::Result;
|
||||
use arc_swap::{ArcSwap, ArcSwapOption};
|
||||
use clash_verge_logging::{LogRing, Type, logging};
|
||||
use clash_verge_logging::{LogRing, Type, logging, logging_error};
|
||||
use std::{
|
||||
fmt,
|
||||
sync::{
|
||||
@@ -303,6 +303,7 @@ impl CoreManager {
|
||||
}
|
||||
match crate::config::Config::resolve_startup_mixed_port().await {
|
||||
Ok(true) => {
|
||||
logging_error!(Type::Config, crate::config::Config::sync_dns_override().await);
|
||||
let retries = retries + 1;
|
||||
tracing::Span::current().record("retries", retries);
|
||||
logging!(
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::core::notify::{Refresh, announce};
|
||||
use crate::{
|
||||
config::{
|
||||
Config, IVerge,
|
||||
runtime::IRuntime,
|
||||
snapshot::{capture_config_files, restore_files},
|
||||
},
|
||||
core::auto_backup::AutoBackupManager,
|
||||
@@ -12,7 +13,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use clash_verge_draft::DraftTransaction;
|
||||
use clash_verge_draft::{DraftTransaction, SharedDraft};
|
||||
use clash_verge_logging::{Type, logging_error};
|
||||
use serde_yaml_ng::Mapping;
|
||||
use std::sync::Arc;
|
||||
@@ -108,6 +109,12 @@ async fn ensure_effect(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn sync_dns_of_replaced_runtime(config_write: &MutexGuard<'_, ()>, original: &SharedDraft<IRuntime>) {
|
||||
if !Arc::ptr_eq(original, &Config::runtime().await.data_arc()) {
|
||||
logging_error!(Type::Config, Config::sync_dns_override_locked(config_write).await);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn apply(config_write: &MutexGuard<'_, ()>, patch: Patch<'_>, effects: Effects) -> Result<()> {
|
||||
let manager = CoreManager::global();
|
||||
let update = manager.claim_config_update(config_write)?;
|
||||
@@ -155,12 +162,16 @@ pub(super) async fn apply(config_write: &MutexGuard<'_, ()>, patch: Patch<'_>, e
|
||||
let runtime_path = crate::utils::dirs::app_home_dir()?.join(crate::constants::files::RUNTIME_CONFIG);
|
||||
snapshots.retain(|snapshot| snapshot.path != runtime_path);
|
||||
}
|
||||
return match restore_files(&snapshots).await {
|
||||
let restored = restore_files(&snapshots).await;
|
||||
// After the restore, which would otherwise overwrite the saved decision.
|
||||
sync_dns_of_replaced_runtime(config_write, &original_runtime).await;
|
||||
return match restored {
|
||||
Ok(()) => Err(error),
|
||||
Err(rollback_error) => Err(proxy_control::rollback_failure(error, rollback_error)),
|
||||
};
|
||||
}
|
||||
transaction.commit();
|
||||
sync_dns_of_replaced_runtime(config_write, &original_runtime).await;
|
||||
match patch {
|
||||
Patch::Verge { patch, .. } => {
|
||||
if patch.auto_check_update == Some(false) {
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::{
|
||||
};
|
||||
use anyhow::{Context as _, Result, anyhow, bail};
|
||||
use clash_verge_draft::DraftTransaction;
|
||||
use clash_verge_logging::{Type, logging};
|
||||
use clash_verge_logging::{Type, logging, logging_error};
|
||||
use scopeguard::{ScopeGuard, defer, guard};
|
||||
|
||||
pub async fn probe_listener(request: ListenerProbe) -> Result<ListenerProbeOutcome> {
|
||||
@@ -30,7 +30,7 @@ pub async fn probe_listener(request: ListenerProbe) -> Result<ListenerProbeOutco
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub async fn save_proxy_ports(settings: ProxyPortSettings) -> Result<SaveProxyPortsOutcome> {
|
||||
settings.validate()?;
|
||||
let _config_write = Config::lock_config_write().await;
|
||||
let config_write = Config::lock_config_write().await;
|
||||
let manager = CoreManager::global();
|
||||
if !manager.try_start_config_update() {
|
||||
bail!("configuration update is already running");
|
||||
@@ -124,6 +124,7 @@ pub async fn save_proxy_ports(settings: ProxyPortSettings) -> Result<SaveProxyPo
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
logging_error!(Type::Config, Config::sync_dns_override_locked(&config_write).await);
|
||||
// The save landed, so the port the app had borrowed is now irrelevant.
|
||||
let _ = ScopeGuard::into_inner(borrowed_port);
|
||||
announce(Refresh::Clash);
|
||||
|
||||
@@ -146,7 +146,10 @@ async fn refresh_core_config() {
|
||||
"Deep link import set current profile; refreshing core config"
|
||||
);
|
||||
match CoreManager::global().update_config_forced().await {
|
||||
Ok(outcome) if outcome.is_valid() => handle::Handle::refresh_clash(),
|
||||
Ok(outcome) if outcome.is_valid() => {
|
||||
logging_error!(Type::Config, Config::sync_dns_override().await);
|
||||
handle::Handle::refresh_clash();
|
||||
}
|
||||
Ok(outcome) => {
|
||||
let message = outcome.to_string();
|
||||
logging!(warn, Type::Config, "Apply config failed: {}", message);
|
||||
|
||||
Reference in New Issue
Block a user