mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-05 01:33:56 +08:00
🏅 Sync 2026-10-30 20:56:14
This commit is contained in:
1 parent
f470897df5
commit
2d2368945f
32 files changed
+2793
-2828
No files matched your search
@@ -36,25 +36,60 @@ write_state() {
|
||||
# Only one worker at a time: a second click while a reload is running would
|
||||
# queue another network reload on top of an unfinished one. mkdir is atomic and
|
||||
# is the only portable primitive available here; a test-then-write pair lets
|
||||
# two workers through at the same moment. Stale locks are recognised by age so
|
||||
# a recycled pid or a killed worker cannot block forever.
|
||||
# two workers through at the same moment. Stale locks are recognised by age and
|
||||
# by a dead pid so a killed worker cannot block forever, but a lock that is
|
||||
# merely young is never taken over.
|
||||
LOCK_DIR="$LOCK_FILE.d"
|
||||
lock_age() {
|
||||
local ts now
|
||||
ts=$(cat "$LOCK_DIR/ts" 2>/dev/null)
|
||||
case "$ts" in ''|*[!0-9]*) echo 999999; return ;; esac
|
||||
# A missing or unreadable timestamp means mkdir just won the race and the
|
||||
# holder has not written pid/ts yet. Reporting that window as ancient let a
|
||||
# late worker rip the lock away mid start up, so two reloads ran at once:
|
||||
# the very thing this lock exists to prevent. It is a fresh lock instead.
|
||||
#
|
||||
# The directory mtime covers the other side of the same window: a worker
|
||||
# killed between mkdir and the ts write would otherwise leave an ageless
|
||||
# lock that nobody ever takes over.
|
||||
case "$ts" in
|
||||
''|*[!0-9]*)
|
||||
ts=$(date -r "$LOCK_DIR" +%s 2>/dev/null)
|
||||
case "$ts" in ''|*[!0-9]*) echo 0; return ;; esac
|
||||
;;
|
||||
esac
|
||||
now=$(date +%s)
|
||||
echo $((now - ts))
|
||||
}
|
||||
|
||||
# kill -0 is the portable liveness probe available in busybox ash. No readable
|
||||
# pid means the holder never got to write one, which is what a lock left behind
|
||||
# by a crash looks like, so it counts as gone.
|
||||
lock_holder_alive() {
|
||||
local pid
|
||||
pid=$(cat "$LOCK_DIR/pid" 2>/dev/null)
|
||||
case "$pid" in ''|*[!0-9]*) return 1 ;; esac
|
||||
kill -0 "$pid" 2>/dev/null
|
||||
}
|
||||
|
||||
# Returns 0 when this process owns a freshly created lock directory. Deletes are
|
||||
# not in place: rm -rf then mkdir leaves a window where two workers both think
|
||||
# they won. Renaming the old directory is a single step that only one worker can
|
||||
# win; the loser reports busy. mkdir can still lose to a third worker.
|
||||
take_over_stale_lock() {
|
||||
local aside
|
||||
[ "$(lock_age)" -gt 300 ] || return 1
|
||||
lock_holder_alive && return 1
|
||||
aside="$LOCK_DIR.stale.$$"
|
||||
mv "$LOCK_DIR" "$aside" 2>/dev/null || return 1
|
||||
rm -rf "$aside"
|
||||
mkdir "$LOCK_DIR" 2>/dev/null
|
||||
}
|
||||
|
||||
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
|
||||
if [ "$(lock_age)" -gt 300 ]; then
|
||||
# the holder is gone; take the lock over
|
||||
rm -rf "$LOCK_DIR"
|
||||
mkdir "$LOCK_DIR" 2>/dev/null || exit 0
|
||||
else
|
||||
# someone else is already reloading: say so instead of leaving the
|
||||
# status file at "queued" and letting the UI time out
|
||||
if ! take_over_stale_lock; then
|
||||
# someone else is already reloading, or another worker won the takeover:
|
||||
# say so instead of leaving the status file at "queued" and letting the
|
||||
# UI time out
|
||||
write_state busy locked "Another firewall reload is already running"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { access, popen, readfile, writefile, unlink } from 'fs';
|
||||
import { access, popen, readfile, writefile, rename, unlink } from 'fs';
|
||||
import { cursor } from 'uci';
|
||||
|
||||
const uci = cursor();
|
||||
@@ -361,13 +361,22 @@ methods.setup_firewall = {
|
||||
// gets a chance to overwrite it. Without both, a poll arriving
|
||||
// while the worker is still starting would read the previous
|
||||
// run's "done" and report success for work that has not run.
|
||||
run_id = time();
|
||||
let st = writefile('/var/run/tailscale-fwsetup.status.tmp',
|
||||
'queued\nstart\n\n' + run_id + '\n');
|
||||
if (st == null || rename('/var/run/tailscale-fwsetup.status.tmp', '/var/run/tailscale-fwsetup.status') == null) {
|
||||
// clock() returns [seconds, nanoseconds], not milliseconds, so
|
||||
// fold it explicitly. Two clicks within the same second still
|
||||
// get distinct ids; the temp file name carries the id as well,
|
||||
// so concurrent writers cannot clobber each other's queued
|
||||
// state through a shared fixed path.
|
||||
let now = clock();
|
||||
run_id = now[0] * 1000 + int(now[1] / 1000000);
|
||||
let tmp = '/var/run/tailscale-fwsetup.status.tmp.' + run_id;
|
||||
let st = writefile(tmp, 'queued\nstart\n\n' + run_id + '\n');
|
||||
if (st == null || rename(tmp, '/var/run/tailscale-fwsetup.status') == null) {
|
||||
// Without a writable status file the poll can never see a
|
||||
// result, so say so instead of letting the UI wait it out.
|
||||
return { error: 'Cannot write the firewall status file in /var/run. Configuration was not changed.' };
|
||||
// Drop the temp file we may have created, so a failed run
|
||||
// leaves no stray state behind for the next poll.
|
||||
unlink(tmp);
|
||||
return { error: 'Cannot write the firewall status file in /var/run. The configuration was committed but not applied.' };
|
||||
}
|
||||
|
||||
// The background shell exits immediately, so its status only
|
||||
@@ -467,7 +476,12 @@ methods.get_logs = {
|
||||
args: { lines: 200 },
|
||||
call: function(request) {
|
||||
let lines = int(request?.args?.lines) || 200;
|
||||
let cmd = 'logread -l ' + lines + ' 2>/dev/null | grep -i -E "tailscale" || true';
|
||||
if (lines < 1) lines = 200;
|
||||
if (lines > 1000) lines = 1000;
|
||||
// Filter first, then take the tail: logread -l would only hand the last
|
||||
// N lines of every daemon to grep, so a busy box hides the tailscale
|
||||
// records entirely and the log page claims there is nothing to show.
|
||||
let cmd = 'logread 2>/dev/null | grep -i -E "tailscale" | tail -n ' + lines;
|
||||
let result = exec(cmd);
|
||||
if (result.code == 0) {
|
||||
return { success: true, logs: result.stdout };
|
||||
|
||||
Reference in new issue
Block a user