killm 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -5
- package/dist/src/hosts.js +33 -0
- package/dist/src/index.js +40 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -163,12 +163,26 @@ target hostname at `0.0.0.0` (and `::1` for IPv6):
|
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
It then flushes the OS DNS cache so the change takes effect immediately. When
|
|
166
|
-
the timer expires
|
|
167
|
-
block back out and flushes again. The markers mean
|
|
168
|
-
lines — your existing hosts entries are left
|
|
166
|
+
the timer expires — or on `Ctrl+C`, `SIGTERM`, or closing the terminal
|
|
167
|
+
(`SIGHUP`) — it strips that block back out and flushes again. The markers mean
|
|
168
|
+
it only ever touches its own lines — your existing hosts entries are left
|
|
169
|
+
alone.
|
|
169
170
|
|
|
170
|
-
If
|
|
171
|
-
|
|
171
|
+
### If the killm process dies, the block stays — until killm heals it
|
|
172
|
+
|
|
173
|
+
The timer lives in the killm process, so **keep it running** for the duration.
|
|
174
|
+
Ctrl+C and closing the terminal both lift the block cleanly, but a `kill -9`,
|
|
175
|
+
a crash, or a machine shutdown can strand the block in your hosts file.
|
|
176
|
+
|
|
177
|
+
killm writes the expiry time into the block itself, so it self-heals: **any
|
|
178
|
+
later killm command** (`--status`, `--restore`, or starting a new block)
|
|
179
|
+
notices an expired stranded block and removes it. To lift one immediately:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
sudo npx killm --restore
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`killm --status` also tells you when an active block is scheduled to lift.
|
|
172
186
|
|
|
173
187
|
## Firewall mode (`--firewall`)
|
|
174
188
|
|
package/dist/src/hosts.js
CHANGED
|
@@ -4,6 +4,8 @@ import path from 'node:path';
|
|
|
4
4
|
import { execFile } from 'node:child_process';
|
|
5
5
|
export const BEGIN = '# >>> killm block (do not edit between markers) >>>';
|
|
6
6
|
export const END = '# <<< killm block <<<';
|
|
7
|
+
/** Machine-parseable expiry marker inside the block. */
|
|
8
|
+
export const EXPIRES_PREFIX = '# killm-expires: ';
|
|
7
9
|
const SINK4 = '0.0.0.0';
|
|
8
10
|
const SINK6 = '::1';
|
|
9
11
|
function errnoCode(err) {
|
|
@@ -59,6 +61,7 @@ export function buildBlock(hosts, opts = {}) {
|
|
|
59
61
|
lines.push(`# added by killm at ${new Date().toISOString()}`);
|
|
60
62
|
if (opts.until) {
|
|
61
63
|
lines.push(`# auto-removed at ${opts.until.toISOString()} (or when killm exits)`);
|
|
64
|
+
lines.push(`${EXPIRES_PREFIX}${opts.until.toISOString()}`);
|
|
62
65
|
}
|
|
63
66
|
for (const h of hosts) {
|
|
64
67
|
lines.push(`${SINK4}\t${h}`);
|
|
@@ -125,6 +128,36 @@ export function removeBlock() {
|
|
|
125
128
|
export function isBlocked() {
|
|
126
129
|
return readHosts().includes(BEGIN);
|
|
127
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* When the current block is scheduled to expire, or null when no block is
|
|
133
|
+
* present or it carries no (valid) expiry marker — e.g. blocks written by
|
|
134
|
+
* killm <= 1.0.2.
|
|
135
|
+
*/
|
|
136
|
+
export function blockExpiry(text = readHosts()) {
|
|
137
|
+
const begin = text.indexOf(BEGIN);
|
|
138
|
+
if (begin === -1)
|
|
139
|
+
return null;
|
|
140
|
+
const idx = text.indexOf(EXPIRES_PREFIX, begin);
|
|
141
|
+
if (idx === -1)
|
|
142
|
+
return null;
|
|
143
|
+
const start = idx + EXPIRES_PREFIX.length;
|
|
144
|
+
const lineEnd = text.indexOf('\n', start);
|
|
145
|
+
const stamp = text.slice(start, lineEnd === -1 ? undefined : lineEnd).trim();
|
|
146
|
+
const date = new Date(stamp);
|
|
147
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Remove the block if its expiry has passed — heals hosts files stranded by
|
|
151
|
+
* a killed process (terminal closed, kill -9, crash, machine shutdown).
|
|
152
|
+
*
|
|
153
|
+
* @returns true when an expired block was found and removed
|
|
154
|
+
*/
|
|
155
|
+
export function clearExpiredBlock(now = new Date()) {
|
|
156
|
+
const expiry = blockExpiry();
|
|
157
|
+
if (expiry === null || expiry.getTime() > now.getTime())
|
|
158
|
+
return false;
|
|
159
|
+
return removeBlock();
|
|
160
|
+
}
|
|
128
161
|
/**
|
|
129
162
|
* Best-effort DNS cache flush so the new hosts entries take effect immediately.
|
|
130
163
|
* Silently ignores failures — the block still works, it may just take a moment.
|
package/dist/src/index.js
CHANGED
|
@@ -94,6 +94,15 @@ async function runBlock(parsed) {
|
|
|
94
94
|
return 0;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
+
// Heal any expired block stranded by a killed process before applying ours.
|
|
98
|
+
try {
|
|
99
|
+
if (hosts.clearExpiredBlock()) {
|
|
100
|
+
out(c.dim(' (cleaned up an expired block left behind by a previous run)'));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
/* no privileges; the apply below will surface the real error */
|
|
105
|
+
}
|
|
97
106
|
const endTime = Date.now() + durationMs;
|
|
98
107
|
const until = new Date(endTime);
|
|
99
108
|
try {
|
|
@@ -138,6 +147,11 @@ async function runBlock(parsed) {
|
|
|
138
147
|
'\n' +
|
|
139
148
|
c.dim(' file entirely — restart the browser / disable Secure DNS if needed.'));
|
|
140
149
|
out();
|
|
150
|
+
out(c.yellow(' ⚠ keep this process running — killm lifts the block when the timer'));
|
|
151
|
+
out(c.yellow(' ends or you press Ctrl+C / close the terminal. If it dies harder'));
|
|
152
|
+
out(c.yellow(' than that (kill -9, crash, shutdown), the block stays in place'));
|
|
153
|
+
out(c.yellow(' until you run any killm command again, e.g. killm --restore'));
|
|
154
|
+
out();
|
|
141
155
|
let timer = null;
|
|
142
156
|
let ticker = null;
|
|
143
157
|
let restored = false;
|
|
@@ -199,6 +213,9 @@ async function runBlock(parsed) {
|
|
|
199
213
|
};
|
|
200
214
|
process.once('SIGINT', () => onSignal('interrupted'));
|
|
201
215
|
process.once('SIGTERM', () => onSignal('terminated'));
|
|
216
|
+
// Closing the terminal window sends SIGHUP; without this handler Node
|
|
217
|
+
// dies without cleanup and the block is stranded in the hosts file.
|
|
218
|
+
process.once('SIGHUP', () => onSignal('terminal closed'));
|
|
202
219
|
});
|
|
203
220
|
return 0;
|
|
204
221
|
}
|
|
@@ -224,9 +241,31 @@ export async function main(argv) {
|
|
|
224
241
|
out(VERSION);
|
|
225
242
|
return 0;
|
|
226
243
|
case 'status': {
|
|
244
|
+
const expiry = hosts.blockExpiry();
|
|
245
|
+
const expired = expiry !== null && expiry.getTime() <= Date.now();
|
|
246
|
+
if (expired) {
|
|
247
|
+
// A previous run was killed before it could clean up. Heal it now.
|
|
248
|
+
try {
|
|
249
|
+
hosts.clearExpiredBlock();
|
|
250
|
+
await hosts.flushDns();
|
|
251
|
+
out(c.green('killm: no block active.'));
|
|
252
|
+
out(c.dim('(cleaned up an expired block left behind by a previous run)'));
|
|
253
|
+
return 0;
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
err(c.yellow('killm: an EXPIRED block is still stuck in the hosts file.'));
|
|
257
|
+
err(' Clean it up with elevated privileges: sudo npx killm --restore');
|
|
258
|
+
return 1;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
227
261
|
if (hosts.isBlocked()) {
|
|
228
262
|
out(c.yellow('killm: a block is currently ACTIVE.'));
|
|
229
|
-
|
|
263
|
+
if (expiry) {
|
|
264
|
+
out(c.dim(`Scheduled to lift at ${expiry.toLocaleString()}.`));
|
|
265
|
+
out(c.dim('If the killm process that created it is no longer running, it will not\n' +
|
|
266
|
+
'lift itself on time — but any later killm command cleans it up once expired.'));
|
|
267
|
+
}
|
|
268
|
+
out(c.dim('Run "killm --restore" to lift it now.'));
|
|
230
269
|
}
|
|
231
270
|
else {
|
|
232
271
|
out(c.green('killm: no block active.'));
|