mirror of
https://github.com/9001/copyparty.git
synced 2026-06-18 20:22:27 -06:00
make signal-handler less shit;
previously: threading.Condition to wakeup the actual handler; exciting chance of heisenbugs / deadlocks (theoretically) almost went with os.pipe on unix and socketpairs on windows, but turns out SimpleQueue is perfect and safe for this purpose SimpleQueue is 3.7+ so use a regular queue on <3.7 (same problems as original approach) also need dedicated thread for popping the queue on <3.7 to avoid deadlock on most platforms (--sig-thr) new features: --logrot-sig sets a signal for immediate log-rotate --stack-sig sets a signal to dump stack to log/stdout --reload-sig sets a signal to initiates config-reload (was hardcoded to USR1 previously)
This commit is contained in:
parent
f23ec5d9f8
commit
f4f97b6cc3
|
|
@ -1234,6 +1234,7 @@ def add_general(ap, nc, srvname):
|
||||||
ap2.add_argument("--mimes", action="store_true", help="list default mimetype mapping and exit")
|
ap2.add_argument("--mimes", action="store_true", help="list default mimetype mapping and exit")
|
||||||
ap2.add_argument("--rmagic", action="store_true", help="do expensive analysis to improve accuracy of returned mimetypes; will make file-downloads, rss, and webdav slower (volflag=rmagic)")
|
ap2.add_argument("--rmagic", action="store_true", help="do expensive analysis to improve accuracy of returned mimetypes; will make file-downloads, rss, and webdav slower (volflag=rmagic)")
|
||||||
ap2.add_argument("-j", metavar="CORES", type=int, default=1, help="num cpu-cores for uploads/downloads (0=all); keeping the default is almost always best")
|
ap2.add_argument("-j", metavar="CORES", type=int, default=1, help="num cpu-cores for uploads/downloads (0=all); keeping the default is almost always best")
|
||||||
|
ap2.add_argument("--reload-sig", metavar="S", type=u, default=("" if ANYWIN else "USR1"), help="reload server config when unix-signal \033[33mS\033[0m is received; examples: [\033[32mSIGUSR1\033[0m], [\033[32mUSR1\033[0m], [\033[32m10\033[0m]")
|
||||||
ap2.add_argument("--vc-url", metavar="URL", type=u, default="", help="URL to check for vulnerable versions (default-disabled)")
|
ap2.add_argument("--vc-url", metavar="URL", type=u, default="", help="URL to check for vulnerable versions (default-disabled)")
|
||||||
ap2.add_argument("--vc-age", metavar="HOURS", type=int, default=3, help="how many hours to wait between vulnerability checks")
|
ap2.add_argument("--vc-age", metavar="HOURS", type=int, default=3, help="how many hours to wait between vulnerability checks")
|
||||||
ap2.add_argument("--vc-exit", action="store_true", help="panic and exit if current version is vulnerable")
|
ap2.add_argument("--vc-exit", action="store_true", help="panic and exit if current version is vulnerable")
|
||||||
|
|
@ -1706,6 +1707,7 @@ def add_logging(ap):
|
||||||
ap2.add_argument("-lo", metavar="PATH", type=u, default="", help="logfile; use .txt for plaintext or .xz for compressed. Example: \033[32mcpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz\033[0m (NB: some errors may appear on STDOUT only)")
|
ap2.add_argument("-lo", metavar="PATH", type=u, default="", help="logfile; use .txt for plaintext or .xz for compressed. Example: \033[32mcpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz\033[0m (NB: some errors may appear on STDOUT only)")
|
||||||
ap2.add_argument("--flo", metavar="N", type=int, default=1, help="log format for \033[33m-lo\033[0m; [\033[32m1\033[0m]=classic/colors, [\033[32m2\033[0m]=no-color")
|
ap2.add_argument("--flo", metavar="N", type=int, default=1, help="log format for \033[33m-lo\033[0m; [\033[32m1\033[0m]=classic/colors, [\033[32m2\033[0m]=no-color")
|
||||||
ap2.add_argument("--rlo", metavar="TXT", type=u, default=".1", help="logrotate counter format; see \033[33m--help-rlo\033[0m")
|
ap2.add_argument("--rlo", metavar="TXT", type=u, default=".1", help="logrotate counter format; see \033[33m--help-rlo\033[0m")
|
||||||
|
ap2.add_argument("--logrot-sig", metavar="S", type=u, default="", help="immediately logrotate when unix-signal \033[33mS\033[0m is received; examples: [\033[32mSIGHUP\033[0m], [\033[32mHUP\033[0m], [\033[32m1\033[0m]")
|
||||||
ap2.add_argument("--no-ansi", action="store_true", default=not VT100, help="disable colors; same as environment-variable NO_COLOR")
|
ap2.add_argument("--no-ansi", action="store_true", default=not VT100, help="disable colors; same as environment-variable NO_COLOR")
|
||||||
ap2.add_argument("--ansi", action="store_true", help="force colors; overrides environment-variable NO_COLOR")
|
ap2.add_argument("--ansi", action="store_true", help="force colors; overrides environment-variable NO_COLOR")
|
||||||
ap2.add_argument("--no-logflush", action="store_true", help="don't flush the logfile after each write; tiny bit faster")
|
ap2.add_argument("--no-logflush", action="store_true", help="don't flush the logfile after each write; tiny bit faster")
|
||||||
|
|
@ -1980,10 +1982,15 @@ def add_debug(ap):
|
||||||
ap2.add_argument("--no-scandir", action="store_true", help="kernel-bug workaround: disable scandir; do a listdir + stat on each file instead")
|
ap2.add_argument("--no-scandir", action="store_true", help="kernel-bug workaround: disable scandir; do a listdir + stat on each file instead")
|
||||||
ap2.add_argument("--no-fastboot", action="store_true", help="wait for initial filesystem indexing before accepting client requests")
|
ap2.add_argument("--no-fastboot", action="store_true", help="wait for initial filesystem indexing before accepting client requests")
|
||||||
ap2.add_argument("--no-htp", action="store_true", help="disable httpserver threadpool, create threads as-needed instead")
|
ap2.add_argument("--no-htp", action="store_true", help="disable httpserver threadpool, create threads as-needed instead")
|
||||||
|
if ANYWIN or sys.version_info < (3, 7):
|
||||||
|
ap2.add_argument("--sig-thr", action="store_true", default=True, help=argparse.SUPPRESS)
|
||||||
|
else:
|
||||||
|
ap2.add_argument("--sig-thr", action="store_true", help="start separate thread for OS-signals (try this if CTRL-C is busted)")
|
||||||
ap2.add_argument("--rm-sck", action="store_true", help="when listening on unix-sockets, do a basic delete+bind instead of the default atomic bind")
|
ap2.add_argument("--rm-sck", action="store_true", help="when listening on unix-sockets, do a basic delete+bind instead of the default atomic bind")
|
||||||
ap2.add_argument("--srch-dbg", action="store_true", help="explain search processing, and do some extra expensive sanity checks")
|
ap2.add_argument("--srch-dbg", action="store_true", help="explain search processing, and do some extra expensive sanity checks")
|
||||||
ap2.add_argument("--rclone-mdns", action="store_true", help="use mdns-domain instead of server-ip on /?hc")
|
ap2.add_argument("--rclone-mdns", action="store_true", help="use mdns-domain instead of server-ip on /?hc")
|
||||||
ap2.add_argument("--stackmon", metavar="P,S", type=u, default="", help="write stacktrace to \033[33mP\033[0math every \033[33mS\033[0m second, for example --stackmon=\033[32m./st/%%Y-%%m/%%d/%%H%%M.xz,60")
|
ap2.add_argument("--stackmon", metavar="P,S", type=u, default="", help="write stacktrace to \033[33mP\033[0math every \033[33mS\033[0m second, for example --stackmon=\033[32m./st/%%Y-%%m/%%d/%%H%%M.xz,60")
|
||||||
|
ap2.add_argument("--stack-sig", metavar="S", type=u, default="", help="show stacktrace when unix-signal \033[33mS\033[0m is received; examples: [\033[32mSIGUSR2\033[0m], [\033[32mUSR2\033[0m], [\033[32m12\033[0m]")
|
||||||
ap2.add_argument("--log-thrs", metavar="SEC", type=float, default=0.0, help="list active threads every \033[33mSEC\033[0m")
|
ap2.add_argument("--log-thrs", metavar="SEC", type=float, default=0.0, help="list active threads every \033[33mSEC\033[0m")
|
||||||
ap2.add_argument("--log-fk", metavar="REGEX", type=u, default="", help="log filekey params for files where path matches \033[33mREGEX\033[0m; [\033[32m.\033[0m] (a single dot) = all files")
|
ap2.add_argument("--log-fk", metavar="REGEX", type=u, default="", help="log filekey params for files where path matches \033[33mREGEX\033[0m; [\033[32m.\033[0m] (a single dot) = all files")
|
||||||
ap2.add_argument("--bak-flips", action="store_true", help="[up2k] if a client uploads a bitflipped/corrupted chunk, store a copy according to \033[33m--bf-nc\033[0m and \033[33m--bf-dir\033[0m")
|
ap2.add_argument("--bak-flips", action="store_true", help="[up2k] if a client uploads a bitflipped/corrupted chunk, store a copy according to \033[33m--bf-nc\033[0m and \033[33m--bf-dir\033[0m")
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ from .util import (
|
||||||
odfusion,
|
odfusion,
|
||||||
pybin,
|
pybin,
|
||||||
read_utf8,
|
read_utf8,
|
||||||
|
signame2int,
|
||||||
start_log_thrs,
|
start_log_thrs,
|
||||||
start_stackmon,
|
start_stackmon,
|
||||||
termsize,
|
termsize,
|
||||||
|
|
@ -107,6 +108,12 @@ if PY2:
|
||||||
else:
|
else:
|
||||||
from urllib.request import Request, urlopen
|
from urllib.request import Request, urlopen
|
||||||
|
|
||||||
|
try:
|
||||||
|
from queue import SimpleQueue
|
||||||
|
except:
|
||||||
|
# yuul b. alwright
|
||||||
|
from queue import Queue as SimpleQueue
|
||||||
|
|
||||||
|
|
||||||
VER_IDP_DB = 1
|
VER_IDP_DB = 1
|
||||||
VER_SESSION_DB = 1
|
VER_SESSION_DB = 1
|
||||||
|
|
@ -140,13 +147,9 @@ class SvcHub(object):
|
||||||
self.logf: Optional[typing.TextIO] = None
|
self.logf: Optional[typing.TextIO] = None
|
||||||
self.logf_base_fn = ""
|
self.logf_base_fn = ""
|
||||||
self.is_dut = False # running in unittest; always False
|
self.is_dut = False # running in unittest; always False
|
||||||
self.stop_req = False
|
|
||||||
self.stopping = False
|
self.stopping = False
|
||||||
self.stopped = False
|
self.stopped = False
|
||||||
self.reload_req = False
|
|
||||||
self.reload_mutex = threading.Lock()
|
self.reload_mutex = threading.Lock()
|
||||||
self.stop_cond = threading.Condition()
|
|
||||||
self.nsigs = 3
|
|
||||||
self.retcode = 0
|
self.retcode = 0
|
||||||
self.httpsrv_up = 0
|
self.httpsrv_up = 0
|
||||||
self.qr_tsz = None
|
self.qr_tsz = None
|
||||||
|
|
@ -156,6 +159,12 @@ class SvcHub(object):
|
||||||
self.cmon = 0
|
self.cmon = 0
|
||||||
self.tstack = 0.0
|
self.tstack = 0.0
|
||||||
|
|
||||||
|
self.sig_logrot = -999
|
||||||
|
self.sig_reload = -999
|
||||||
|
self.sig_stack = -999
|
||||||
|
self.nsigs = 7
|
||||||
|
self.sig = SimpleQueue()
|
||||||
|
|
||||||
self.iphash = HMaccas(os.path.join(self.E.cfg, "iphash"), 8)
|
self.iphash = HMaccas(os.path.join(self.E.cfg, "iphash"), 8)
|
||||||
|
|
||||||
if args.sss or args.s >= 3:
|
if args.sss or args.s >= 3:
|
||||||
|
|
@ -1451,31 +1460,37 @@ class SvcHub(object):
|
||||||
|
|
||||||
sigs = [signal.SIGINT, signal.SIGTERM]
|
sigs = [signal.SIGINT, signal.SIGTERM]
|
||||||
if not ANYWIN:
|
if not ANYWIN:
|
||||||
sigs.append(signal.SIGUSR1)
|
sigs.append(signal.SIGHUP)
|
||||||
|
|
||||||
|
for (opt, mem) in (
|
||||||
|
("logrot_sig", "sig_logrot"),
|
||||||
|
("reload_sig", "sig_reload"),
|
||||||
|
("stack_sig", "sig_stack"),
|
||||||
|
):
|
||||||
|
zs = getattr(self.args, opt)
|
||||||
|
if not zs:
|
||||||
|
continue
|
||||||
|
zi = signame2int(zs)
|
||||||
|
setattr(self, mem, zi)
|
||||||
|
sigs.append(zi)
|
||||||
|
|
||||||
for sig in sigs:
|
for sig in sigs:
|
||||||
signal.signal(sig, self.signal_handler)
|
signal.signal(sig, self.signal_handler)
|
||||||
|
|
||||||
# macos hangs after shutdown on sigterm with while-sleep,
|
if self.args.sig_thr:
|
||||||
# windows cannot ^c stop_cond (and win10 does the macos thing but winxp is fine??)
|
Daemon(self._signal_thr, "svchub-sig")
|
||||||
# linux is fine with both,
|
|
||||||
# never lucky
|
|
||||||
if ANYWIN:
|
|
||||||
# msys-python probably fine but >msys-python
|
|
||||||
Daemon(self.stop_thr, "svchub-sig")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while not self.stop_req:
|
while not self.stopping:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.shutdown()
|
|
||||||
# cant join; eats signals on win10
|
# cant join; eats signals on win10
|
||||||
while not self.stopped:
|
while not self.stopped:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
else:
|
else:
|
||||||
self.stop_thr()
|
self._signal_thr()
|
||||||
|
|
||||||
def start_zeroconf(self) -> None:
|
def start_zeroconf(self) -> None:
|
||||||
self.zc_ngen += 1
|
self.zc_ngen += 1
|
||||||
|
|
@ -1533,17 +1548,6 @@ class SvcHub(object):
|
||||||
self.asrv.load_sessions(True)
|
self.asrv.load_sessions(True)
|
||||||
self.broker.reload_sessions()
|
self.broker.reload_sessions()
|
||||||
|
|
||||||
def stop_thr(self) -> None:
|
|
||||||
while not self.stop_req:
|
|
||||||
with self.stop_cond:
|
|
||||||
self.stop_cond.wait(9001)
|
|
||||||
|
|
||||||
if self.reload_req:
|
|
||||||
self.reload_req = False
|
|
||||||
self.reload(True, True)
|
|
||||||
|
|
||||||
self.shutdown()
|
|
||||||
|
|
||||||
def kill9(self, delay: float = 0.0) -> None:
|
def kill9(self, delay: float = 0.0) -> None:
|
||||||
if delay > 0.01:
|
if delay > 0.01:
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
|
|
@ -1556,26 +1560,42 @@ class SvcHub(object):
|
||||||
os.kill(os.getpid(), signal.SIGKILL)
|
os.kill(os.getpid(), signal.SIGKILL)
|
||||||
|
|
||||||
def signal_handler(self, sig: int, frame: Optional[FrameType]) -> None:
|
def signal_handler(self, sig: int, frame: Optional[FrameType]) -> None:
|
||||||
if self.stopping:
|
if sig in (signal.SIGINT, signal.SIGTERM):
|
||||||
if self.nsigs <= 0:
|
self.nsigs -= 1
|
||||||
|
|
||||||
|
if self.nsigs == 0:
|
||||||
try:
|
try:
|
||||||
threading.Thread(target=self.pr, args=("OMBO BREAKER",)).start()
|
threading.Thread(target=self.pr, args=("OMBO BREAKER",)).start()
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if self.nsigs <= 0:
|
||||||
self.kill9()
|
self.kill9()
|
||||||
else:
|
|
||||||
self.nsigs -= 1
|
|
||||||
return
|
|
||||||
|
|
||||||
if not ANYWIN and sig == signal.SIGUSR1:
|
self.sig.put(sig)
|
||||||
self.reload_req = True
|
|
||||||
|
def _signal_thr(self) -> None:
|
||||||
|
while not self.stopping:
|
||||||
|
sig = self.sig.get()
|
||||||
|
self._signal_handler(sig)
|
||||||
|
|
||||||
|
def _signal_handler(self, sig: int) -> None:
|
||||||
|
if sig == self.sig_logrot:
|
||||||
|
self.log("root", "signal: logrotate")
|
||||||
|
dt = datetime.now(self.tz)
|
||||||
|
self.logf_base_fn = "\t"
|
||||||
|
self._set_next_day(dt)
|
||||||
|
|
||||||
|
elif sig == self.sig_reload:
|
||||||
|
self.log("root", "signal: reload")
|
||||||
|
self.reload(True, True)
|
||||||
|
|
||||||
|
elif sig == self.sig_stack:
|
||||||
|
self.log("root", "signal: stack%s" % (alltrace(),))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.stop_req = True
|
self.shutdown()
|
||||||
|
|
||||||
with self.stop_cond:
|
|
||||||
self.stop_cond.notify_all()
|
|
||||||
|
|
||||||
def shutdown(self) -> None:
|
def shutdown(self) -> None:
|
||||||
if self.stopping:
|
if self.stopping:
|
||||||
|
|
@ -1583,10 +1603,8 @@ class SvcHub(object):
|
||||||
|
|
||||||
# start_log_thrs(print, 0.1, 1)
|
# start_log_thrs(print, 0.1, 1)
|
||||||
|
|
||||||
|
self.nsigs = 3
|
||||||
self.stopping = True
|
self.stopping = True
|
||||||
self.stop_req = True
|
|
||||||
with self.stop_cond:
|
|
||||||
self.stop_cond.notify_all()
|
|
||||||
|
|
||||||
ret = 1
|
ret = 1
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -1635,6 +1635,16 @@ def expand_osenv_cs(txt) -> str:
|
||||||
raise Exception(t)
|
raise Exception(t)
|
||||||
|
|
||||||
|
|
||||||
|
def signame2int(txt: str) -> int:
|
||||||
|
try:
|
||||||
|
return int(txt)
|
||||||
|
except:
|
||||||
|
txt = txt.upper()
|
||||||
|
if not txt.startswith("SIG"):
|
||||||
|
txt = "SIG" + txt
|
||||||
|
return int(getattr(signal, txt))
|
||||||
|
|
||||||
|
|
||||||
def rice_tid() -> str:
|
def rice_tid() -> str:
|
||||||
tid = threading.current_thread().ident
|
tid = threading.current_thread().ident
|
||||||
c = sunpack(b"B" * 5, spack(b">Q", tid)[-5:])
|
c = sunpack(b"B" * 5, spack(b">Q", tid)[-5:])
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue