@@ -14,7 +14,6 @@ class HostsEditor {
1414 var block = " \n \( blockMarkerStart) \n "
1515 for domain in unique {
1616 block += " 0.0.0.0 \( domain) \n "
17- // also block www. variant if not already present
1817 if !domain. hasPrefix ( " www. " ) {
1918 block += " 0.0.0.0 www. \( domain) \n "
2019 }
@@ -23,17 +22,68 @@ class HostsEditor {
2322 content += block
2423 }
2524
26- try ? content. write ( toFile: hostsPath, atomically: true , encoding: . utf8)
25+ do {
26+ try content. write ( toFile: hostsPath, atomically: true , encoding: . utf8)
27+ } catch {
28+ log ( " ERROR: failed to write /etc/hosts during block: \( error) " )
29+ return
30+ }
31+
2732 flushDNSCache ( )
33+ verifyBlock ( expectedDomains: domains)
2834 }
2935
3036 func removeAllBlocks( ) {
3137 var content = ( try ? String ( contentsOfFile: hostsPath, encoding: . utf8) ) ?? " "
3238 content = removeBlockSection ( from: content)
33- try ? content. write ( toFile: hostsPath, atomically: true , encoding: . utf8)
39+
40+ do {
41+ try content. write ( toFile: hostsPath, atomically: true , encoding: . utf8)
42+ } catch {
43+ log ( " ERROR: failed to write /etc/hosts during unblock: \( error) " )
44+ return
45+ }
46+
3447 flushDNSCache ( )
48+ verifyUnblock ( )
49+ }
50+
51+ // MARK: - Verification
52+
53+ private func verifyBlock( expectedDomains: [ String ] ) {
54+ guard let current = try ? String ( contentsOfFile: hostsPath, encoding: . utf8) else {
55+ log ( " VERIFY ERROR: could not re-read /etc/hosts after block " )
56+ return
57+ }
58+ guard current. contains ( blockMarkerStart) , current. contains ( blockMarkerEnd) else {
59+ log ( " VERIFY ERROR: tome-block markers missing from /etc/hosts after block " )
60+ return
61+ }
62+ // spot-check first 3 domains
63+ let sample = Array ( expectedDomains. prefix ( 3 ) )
64+ for domain in sample {
65+ if !current. contains ( " 0.0.0.0 \( domain) " ) {
66+ log ( " VERIFY ERROR: domain \( domain) not found in /etc/hosts after block " )
67+ return
68+ }
69+ }
70+ log ( " VERIFY OK: \( expectedDomains. count) domain(s) confirmed in /etc/hosts " )
3571 }
3672
73+ private func verifyUnblock( ) {
74+ guard let current = try ? String ( contentsOfFile: hostsPath, encoding: . utf8) else {
75+ log ( " VERIFY ERROR: could not re-read /etc/hosts after unblock " )
76+ return
77+ }
78+ if current. contains ( blockMarkerStart) || current. contains ( blockMarkerEnd) {
79+ log ( " VERIFY ERROR: tome-block markers still present in /etc/hosts after unblock " )
80+ return
81+ }
82+ log ( " VERIFY OK: /etc/hosts clean after unblock " )
83+ }
84+
85+ // MARK: - Helpers
86+
3787 private func removeBlockSection( from content: String ) -> String {
3888 var result = " "
3989 var inBlock = false
@@ -50,7 +100,6 @@ class HostsEditor {
50100 result += line + " \n "
51101 }
52102 }
53- // trim trailing newlines added by section removal, preserve one
54103 return result. trimmingCharacters ( in: . newlines) + " \n "
55104 }
56105
@@ -61,17 +110,21 @@ class HostsEditor {
61110 try ? task. run ( )
62111 task. waitUntilExit ( )
63112
113+ guard let pid = mDNSResponderPID ( ) else {
114+ log ( " WARN: could not find mDNSResponder PID — DNS cache not flushed via HUP " )
115+ return
116+ }
64117 let task2 = Process ( )
65118 task2. launchPath = " /bin/kill "
66- task2. arguments = [ " -HUP " , String ( mDNSResponderPID ( ) ?? 0 ) ]
119+ task2. arguments = [ " -HUP " , String ( pid ) ]
67120 try ? task2. run ( )
68121 task2. waitUntilExit ( )
69122 }
70123
71124 private func mDNSResponderPID( ) -> Int32 ? {
72125 let task = Process ( )
73126 task. launchPath = " /bin/pgrep "
74- task. arguments = [ " mDNSResponder " ]
127+ task. arguments = [ " -x " , " mDNSResponder " ] // -x: exact match, avoids multi-match
75128 let pipe = Pipe ( )
76129 task. standardOutput = pipe
77130 try ? task. run ( )
0 commit comments