Skip to content

Commit 591fe0d

Browse files
committed
Fix hosts restore semantics and cleanup fallback
1 parent 2c4f750 commit 591fe0d

4 files changed

Lines changed: 67 additions & 20 deletions

File tree

src/gui.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use tray_icon::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent};
2020

2121
use crate::branding;
2222
use crate::config::AppConfig;
23+
use crate::hosts::validate_hosts_backup_file;
2324
use crate::hosts_store::{BackupState, backup_state};
2425
#[cfg(target_os = "windows")]
2526
use crate::paths::AppPaths;
@@ -1340,7 +1341,16 @@ fn service_state_changed(before: &ServiceState, after: &ServiceState) -> bool {
13401341

13411342
fn load_hosts_backup_state(config_path: &Path) -> BackupState {
13421343
service::resolve_paths(Some(config_path.to_path_buf()))
1343-
.map(|paths| backup_state(&paths))
1344+
.map(|paths| match backup_state(&paths) {
1345+
BackupState::Ready => {
1346+
if validate_hosts_backup_file(&paths).is_ok() {
1347+
BackupState::Ready
1348+
} else {
1349+
BackupState::Inconsistent
1350+
}
1351+
}
1352+
state => state,
1353+
})
13441354
.unwrap_or(BackupState::Missing)
13451355
}
13461356

src/hosts.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ pub fn apply_hosts(config: &AppConfig, paths: &AppPaths) -> Result<()> {
2121
#[cfg(not(target_os = "android"))]
2222
{
2323
let path = hosts_path();
24-
ensure_hosts_backup(paths, &path)?;
2524
let original = fs::read_to_string(&path)
2625
.with_context(|| format!("failed to read hosts file {}", path.display()))?;
26+
let backup_baseline = backup_baseline_content(&original);
27+
ensure_hosts_backup(paths, &path, &backup_baseline)?;
2728
let content = render_managed_hosts(&original, config);
2829

2930
if content == original {
@@ -68,7 +69,11 @@ pub fn backup_hosts_file(paths: &AppPaths) -> Result<()> {
6869

6970
#[cfg(not(target_os = "android"))]
7071
{
71-
ensure_hosts_backup(paths, &hosts_path())
72+
let path = hosts_path();
73+
let original = fs::read_to_string(&path)
74+
.with_context(|| format!("failed to read hosts file {}", path.display()))?;
75+
let backup_baseline = backup_baseline_content(&original);
76+
ensure_hosts_backup(paths, &path, &backup_baseline)
7277
}
7378
}
7479

@@ -126,6 +131,11 @@ fn render_managed_hosts(original: &str, config: &AppConfig) -> String {
126131
content
127132
}
128133

134+
#[cfg(not(target_os = "android"))]
135+
fn backup_baseline_content(original: &str) -> String {
136+
strip_managed_block(original)
137+
}
138+
129139
#[cfg(target_os = "android")]
130140
fn apply_android_hosts(_config: &AppConfig, paths: &AppPaths) -> Result<()> {
131141
let marker_path = android_hosts_marker_path(paths);
@@ -235,7 +245,7 @@ fn hosts_path() -> PathBuf {
235245

236246
#[cfg(test)]
237247
mod tests {
238-
use super::{render_managed_hosts, strip_managed_block};
248+
use super::{backup_baseline_content, render_managed_hosts, strip_managed_block};
239249
use crate::config::AppConfig;
240250

241251
#[test]
@@ -293,4 +303,19 @@ mod tests {
293303
let stripped = strip_managed_block(&content);
294304
assert_eq!(stripped, content);
295305
}
306+
307+
#[test]
308+
fn backup_baseline_strips_existing_managed_block() {
309+
let content = [
310+
"127.0.0.1 localhost",
311+
"# >>> linuxdo-accelerator >>>",
312+
"127.211.73.84 linux.do",
313+
"# <<< linuxdo-accelerator <<<",
314+
"1.1.1.1 example.com",
315+
]
316+
.join("\n");
317+
318+
let backup = backup_baseline_content(&content);
319+
assert_eq!(backup, "127.0.0.1 localhost\n1.1.1.1 example.com");
320+
}
296321
}

src/hosts_store.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ pub(crate) enum BackupState {
4343
Inconsistent,
4444
}
4545

46-
pub(crate) fn ensure_hosts_backup(paths: &AppPaths, source_path: &Path) -> Result<()> {
46+
pub(crate) fn ensure_hosts_backup(
47+
paths: &AppPaths,
48+
source_path: &Path,
49+
backup_content: &str,
50+
) -> Result<()> {
4751
match backup_state(paths) {
4852
BackupState::Ready => {
4953
validate_hosts_backup(paths, source_path)?;
@@ -59,8 +63,6 @@ pub(crate) fn ensure_hosts_backup(paths: &AppPaths, source_path: &Path) -> Resul
5963
BackupState::Missing => {}
6064
}
6165

62-
let original = fs::read_to_string(source_path)
63-
.with_context(|| format!("failed to read hosts file {}", source_path.display()))?;
6466
let attributes = snapshot_file_attributes(source_path)?;
6567
let meta = HostsBackupMeta {
6668
source_path: source_path.display().to_string(),
@@ -75,7 +77,7 @@ pub(crate) fn ensure_hosts_backup(paths: &AppPaths, source_path: &Path) -> Resul
7577

7678
cleanup_temp_file(&backup_temp_path)?;
7779
cleanup_temp_file(&meta_temp_path)?;
78-
write_temp_file(&backup_temp_path, &original)?;
80+
write_temp_file(&backup_temp_path, backup_content)?;
7981
write_temp_file(
8082
&meta_temp_path,
8183
std::str::from_utf8(&serialized).context("failed to encode hosts backup metadata")?,
@@ -531,10 +533,10 @@ mod tests {
531533
let source_path = test_dir.join("hosts");
532534

533535
std::fs::write(&source_path, "first").unwrap();
534-
ensure_hosts_backup(&paths, &source_path).unwrap();
536+
ensure_hosts_backup(&paths, &source_path, "first").unwrap();
535537

536538
std::fs::write(&source_path, "second").unwrap();
537-
ensure_hosts_backup(&paths, &source_path).unwrap();
539+
ensure_hosts_backup(&paths, &source_path, "second").unwrap();
538540

539541
assert_eq!(
540542
std::fs::read_to_string(&paths.hosts_backup_path).unwrap(),
@@ -571,7 +573,7 @@ mod tests {
571573
std::fs::create_dir_all(&paths.runtime_dir).unwrap();
572574
let source_path = test_dir.join("hosts");
573575
std::fs::write(&source_path, "original").unwrap();
574-
ensure_hosts_backup(&paths, &source_path).unwrap();
576+
ensure_hosts_backup(&paths, &source_path, "original").unwrap();
575577

576578
let mut meta = load_backup_meta(&paths).unwrap();
577579
meta.source_path = r"C:\different\hosts".to_string();

src/service.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,10 @@ pub fn helper_stop(config_path: Option<PathBuf>) -> Result<()> {
173173
let result = (|| -> Result<()> {
174174
let config = AppConfig::load_or_create(&paths.config_path)?;
175175
ensure_elevated(&config, true)?;
176-
terminate_running_service(&paths)?;
177-
178176
let mut issues = Vec::new();
177+
if let Some(issue) = terminate_running_service(&paths)? {
178+
issues.push(issue);
179+
}
179180
if let Err(error) = remove_hosts(&paths) {
180181
issues.push(format!("failed to clean managed hosts block: {error:#}"));
181182
}
@@ -189,7 +190,7 @@ pub fn helper_stop(config_path: Option<PathBuf>) -> Result<()> {
189190
let status_message = if issues.is_empty() {
190191
"加速已停止".to_string()
191192
} else {
192-
"加速服务已停止,但存在残留清理问题".to_string()
193+
"已执行停止请求,但存在残留清理问题".to_string()
193194
};
194195
if let Err(error) = state::mark_stopped(&paths, &status_message) {
195196
issues.push(format!("failed to update service state: {error:#}"));
@@ -214,9 +215,10 @@ pub fn cleanup(config_path: Option<PathBuf>) -> Result<()> {
214215
let result = (|| -> Result<()> {
215216
let config = AppConfig::load_or_create(&paths.config_path)?;
216217
ensure_elevated(&config, true)?;
217-
terminate_running_service(&paths)?;
218-
219218
let mut issues = Vec::new();
219+
if let Some(issue) = terminate_running_service(&paths)? {
220+
issues.push(issue);
221+
}
220222
let (hosts_message, hosts_warning) = cleanup_hosts_state(&paths);
221223
if let Some(warning) = hosts_warning {
222224
issues.push(warning);
@@ -366,14 +368,22 @@ fn ensure_service_stopped_for_hosts_change(paths: &AppPaths, command_name: &str)
366368
Ok(())
367369
}
368370

369-
fn terminate_running_service(paths: &AppPaths) -> Result<()> {
371+
fn terminate_running_service(paths: &AppPaths) -> Result<Option<String>> {
370372
if let Some(pid) = state::read_pid(paths)? {
371373
if is_process_running(pid) {
372-
terminate_process(pid)?;
373-
wait_until_stopped(pid, Duration::from_secs(5))?;
374+
if let Err(error) = terminate_process(pid) {
375+
return Ok(Some(format!(
376+
"failed to terminate running service pid {pid}: {error:#}"
377+
)));
378+
}
379+
if let Err(error) = wait_until_stopped(pid, Duration::from_secs(5)) {
380+
return Ok(Some(format!(
381+
"running service pid {pid} did not stop cleanly: {error:#}"
382+
)));
383+
}
374384
}
375385
}
376-
Ok(())
386+
Ok(None)
377387
}
378388

379389
fn cleanup_hosts_state(paths: &AppPaths) -> (String, Option<String>) {

0 commit comments

Comments
 (0)