58 lines
2.4 KiB
Python
58 lines
2.4 KiB
Python
def configured_observe_roots():
|
|
roots = config_list(MEMORY_CONFIG, "observeStateRoots", config_list(MEMORY_CONFIG, "webObserveRoots", []))
|
|
return [os.path.abspath(item) for item in roots if isinstance(item, str) and item.startswith("/")]
|
|
|
|
def is_direct_observe_run_path(path):
|
|
resolved = os.path.abspath(path)
|
|
for root in configured_observe_roots():
|
|
if os.path.dirname(resolved) == root and resolved.startswith(root.rstrip("/") + "/"):
|
|
return True
|
|
return False
|
|
|
|
def path_has_open_fd(path):
|
|
resolved = os.path.realpath(path)
|
|
prefix = resolved.rstrip("/") + "/"
|
|
proc_root = "/proc"
|
|
try:
|
|
pids = [name for name in os.listdir(proc_root) if name.isdigit()]
|
|
except OSError:
|
|
return True
|
|
for pid in pids:
|
|
base = os.path.join(proc_root, pid)
|
|
for name in ["cwd", "root"]:
|
|
try:
|
|
target = os.path.realpath(os.readlink(os.path.join(base, name)))
|
|
except OSError:
|
|
continue
|
|
if target == resolved or target.startswith(prefix):
|
|
return True
|
|
fd_dir = os.path.join(base, "fd")
|
|
try:
|
|
fds = os.listdir(fd_dir)
|
|
except OSError:
|
|
continue
|
|
for fd in fds:
|
|
try:
|
|
target = os.path.realpath(os.readlink(os.path.join(fd_dir, fd)))
|
|
except OSError:
|
|
continue
|
|
if target == resolved or target.startswith(prefix):
|
|
return True
|
|
return False
|
|
|
|
def assert_web_observe_candidate(path):
|
|
resolved = os.path.abspath(path)
|
|
if not is_direct_observe_run_path(resolved):
|
|
raise RuntimeError("refusing to remove web-observe path outside configured direct run roots: %s" % path)
|
|
if os.path.islink(resolved) or not os.path.isdir(resolved):
|
|
raise RuntimeError("refusing to remove non-directory or symlink web-observe path: %s" % path)
|
|
stale_hours = config_float(MEMORY_CONFIG, "staleRunMaxAgeHours", 6.0, minimum=0.0)
|
|
record = observe_run_record(resolved, stale_hours)
|
|
if record.get("pidAlive"):
|
|
raise RuntimeError("refusing to remove active web-observe run with live pid: %s" % path)
|
|
if not record.get("staleSignal"):
|
|
raise RuntimeError("refusing to remove web-observe run without stale signal: %s" % path)
|
|
if path_has_open_fd(resolved):
|
|
raise RuntimeError("refusing to remove web-observe run with open fd/cwd reference: %s" % path)
|
|
return record
|