Files
ComfyAssets-ComfyUI_PromptM…/tools/comfyui_stopped.py
T
Vito Sansevero 1a90d3b0ae fix(tools): harden the #247 repair apply after review
- refuse while any other process holds the library, its -wal or -shm open
  (tools/comfyui_stopped.py), also in a rehearsal; without /proc only with
  --i-stopped-comfyui; require main.py in --comfyui-dir; check again just
  before BEGIN IMMEDIATE
- the plan records its library (normcase realpath) and max row id; apply
  refuses on another library
- refuse when opening the library changes its schema (run from a checkout
  at the live code version)
- verify the backup (integrity_check and counts) and never overwrite one
- target_text is a drift key and must match its hash; keep compares the
  linked text's hash, not the stored column
- the dry run rehearses the orphan deletes when --delete-orphans is chosen
- a .md plan path is refused; the list is <stem>.list.md
- any error during apply rolls back, names the backup, exits 2
- relpath on another drive falls back to the full path
2026-10-28 07:16:50 -07:00

82 lines
2.9 KiB
Python

"""Is ComfyUI stopped? The checks a library repair runs before writing (#247).
``comfyui_running`` looks for ``main.py`` processes of one ComfyUI folder
and a listener on its port. ``open_by_others`` lists the processes that hold
the library file, its ``-wal`` or its ``-shm`` open. Both read ``/proc``
(Linux); without it ``open_by_others`` answers ``[NO_PROC]``: it cannot
tell, and the caller refuses unless the user says ComfyUI is stopped.
Entries that cannot be read (another user's process, one that just exited)
are skipped.
"""
import os
import socket
NO_PROC = (
"cannot list the processes that hold the library open (no /proc): stop "
"ComfyUI and pass --i-stopped-comfyui"
)
def _same(path):
return os.path.normcase(os.path.realpath(path))
def _pids(proc, own_pid=None):
try:
names = os.listdir(proc)
except OSError:
return []
return [name for name in names if name.isdigit() and name != str(own_pid)]
def comfyui_running(comfyui_dir, port, proc="/proc"):
"""``main.py`` processes running from ``comfyui_dir`` and a listener on
``port`` (127.0.0.1), each as one line; empty when none is found."""
found = []
folder = _same(comfyui_dir)
for pid in _pids(proc):
try:
with open(os.path.join(proc, pid, "cmdline"), "rb") as stream:
parts = stream.read().split(b"\0")
cwd = _same(os.readlink(os.path.join(proc, pid, "cwd")))
except OSError:
continue
if any(part.endswith(b"main.py") for part in parts) and (
cwd == folder or any(folder.encode() in part for part in parts)
):
found.append(f"process {pid} (main.py in {cwd})")
with socket.socket() as probe:
probe.settimeout(1)
if probe.connect_ex(("127.0.0.1", port)) == 0:
found.append(f"something listens on port {port}")
return found
def open_by_others(database, proc="/proc", own_pid=None):
"""Processes other than ``own_pid`` (default: this one) holding
``database``, its ``-wal`` or ``-shm`` open; ``[NO_PROC]`` without /proc."""
if not os.path.isdir(proc):
return [NO_PROC]
own_pid = os.getpid() if own_pid is None else own_pid
targets = {_same(database + suffix): suffix for suffix in ("", "-wal", "-shm")}
found = []
for pid in _pids(proc, own_pid):
fd_dir = os.path.join(proc, pid, "fd")
try:
fds = os.listdir(fd_dir)
except OSError:
continue
held = set()
for fd in fds:
try:
target = os.readlink(os.path.join(fd_dir, fd))
except OSError:
continue
target = target.replace(" (deleted)", "")
if os.path.isabs(target) and _same(target) in targets:
held.add(os.path.basename(database) + targets[_same(target)])
if held:
found.append(f"process {pid} has {', '.join(sorted(held))} open")
return found