mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 12:49:03 -05:00
fix: open the interface whatever owner files hold
An owner file holding a number past a pid raised OverflowError before the screen opened, and a full temp disk stopped the run on the owner write. 0 and negative numbers are unknown owners, not process groups.
This commit is contained in:
1 parent
cf3b61a17b
commit
5869da409b
4 files changed
+61
-8
No files matched your search
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "c2a66e6b26004e96ef5f59a7fb5a2ed65bcaa9afc7602bd3e0d71f50cc561056"
|
||||
$defaultInstallSha256 = "b23c6da4ee131f898c25540bd53677ea4112964fd00b209e4fe85dfdba8427d1"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installerExit = 0
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
|
||||
+24
-6
@@ -12279,8 +12279,9 @@ def _owner_alive(folder: Path) -> bool | None:
|
||||
pid = int((folder / TUI_OWNER).read_text(encoding="utf-8").strip())
|
||||
except (OSError, ValueError):
|
||||
return None
|
||||
if os.name == "nt":
|
||||
# os.kill(pid, 0) ends a process on Windows instead of asking.
|
||||
if os.name == "nt" or pid <= 0:
|
||||
# os.kill(pid, 0) ends a process on Windows instead of asking, and
|
||||
# 0 or a negative number names a process group, not the run.
|
||||
return None
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
@@ -12288,7 +12289,8 @@ def _owner_alive(folder: Path) -> bool | None:
|
||||
return False
|
||||
except PermissionError:
|
||||
return True
|
||||
except OSError:
|
||||
except (OSError, OverflowError):
|
||||
# A number past what a pid holds is no process this run can ask.
|
||||
return None
|
||||
return True
|
||||
|
||||
@@ -12375,9 +12377,20 @@ def run_tui(args: argparse.Namespace) -> int:
|
||||
state, effects = started
|
||||
if flags["term"]:
|
||||
state.exit_code = 128 + flags["term"]
|
||||
work = Path(tempfile.mkdtemp(prefix=TUI_WORK_PREFIX))
|
||||
(work / TUI_OWNER).write_text(str(os.getpid()), encoding="utf-8")
|
||||
_prune_work_folders(work.parent, time.time())
|
||||
try:
|
||||
work = Path(tempfile.mkdtemp(prefix=TUI_WORK_PREFIX))
|
||||
except OSError as exc:
|
||||
_restore_handlers(handlers)
|
||||
print(f"Error: no work folder could be made in {tempfile.gettempdir()}: {exc}. "
|
||||
"Free some space there, or point TMPDIR (TEMP on Windows) at another folder.", file=sys.stderr)
|
||||
return 1
|
||||
try:
|
||||
(work / TUI_OWNER).write_text(str(os.getpid()), encoding="utf-8")
|
||||
except OSError as exc:
|
||||
# A full temp disk: the folder has no owner, and a later run leaves
|
||||
# it a week like any unknown one. The logs fail as they come.
|
||||
print(f"The work folder {work} could not be marked as this run's: {exc}",
|
||||
file=sys.stderr)
|
||||
script = str(Path(__file__).resolve())
|
||||
inbox: queue.Queue = queue.Queue()
|
||||
current: list[_EngineRun] = []
|
||||
@@ -12445,6 +12458,11 @@ def run_tui(args: argparse.Namespace) -> int:
|
||||
if number is not None:
|
||||
handlers[number] = signal.signal(number, handler)
|
||||
sys.stdout = sys.stderr = buffer
|
||||
try:
|
||||
_prune_work_folders(work.parent, time.time())
|
||||
except OSError as exc:
|
||||
# What earlier runs left stays until a later run: this one opens.
|
||||
print(f"Earlier work folders could not be removed: {exc}", file=sys.stderr)
|
||||
if state.exit_code is None:
|
||||
# Stopped while it started: the screen never opens.
|
||||
terminal.enter()
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
||||
TEMP_INSTALLER=""
|
||||
TEMP_DIRECTORY=""
|
||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
DEFAULT_INSTALL_SHA256="c2a66e6b26004e96ef5f59a7fb5a2ed65bcaa9afc7602bd3e0d71f50cc561056"
|
||||
DEFAULT_INSTALL_SHA256="b23c6da4ee131f898c25540bd53677ea4112964fd00b209e4fe85dfdba8427d1"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -10,6 +10,7 @@ installs it).
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import errno
|
||||
import fcntl
|
||||
import functools
|
||||
import hashlib
|
||||
@@ -1801,6 +1802,31 @@ class WorkFolders(unittest.TestCase):
|
||||
work, out = self.run_to(130)
|
||||
self.assertFalse(work.exists())
|
||||
|
||||
def test_a_full_temp_disk_or_a_failing_pruner_still_opens(self):
|
||||
real_write = Path.write_text
|
||||
|
||||
def full(path, *args, **kwargs):
|
||||
if path.name == install.TUI_OWNER:
|
||||
raise OSError(errno.ENOSPC, "No space left on device")
|
||||
return real_write(path, *args, **kwargs)
|
||||
|
||||
with mock.patch.object(Path, "write_text", full):
|
||||
work, out = self.run_to(0)
|
||||
self.assertFalse(work.exists())
|
||||
with mock.patch.object(install, "_prune_work_folders",
|
||||
side_effect=OSError(errno.EACCES, "denied")):
|
||||
work, out = self.run_to(0)
|
||||
self.assertFalse(work.exists())
|
||||
err = io.StringIO()
|
||||
with mock.patch.object(install.tempfile, "mkdtemp",
|
||||
side_effect=OSError(errno.ENOSPC, "No space left")), \
|
||||
mock.patch.object(install, "tui_start", return_value=(fresh_state(), [])), \
|
||||
mock.patch.object(install, "_open_local_sources"), \
|
||||
mock.patch.object(install, "detect_os", return_value="linux"), \
|
||||
mock.patch.object(sys, "stderr", err):
|
||||
self.assertEqual(install.run_tui(_args()), 1)
|
||||
self.assertIn("no work folder could be made", err.getvalue())
|
||||
|
||||
def test_a_stop_while_it_starts_ends_on_the_farewell(self):
|
||||
"""Start+Select during the detection: the signal is the interface's,
|
||||
never the default action that ended Python without a word."""
|
||||
@@ -2456,8 +2482,17 @@ class AuditRoundThree(unittest.TestCase):
|
||||
unknown_old = folder("unknown-old", None, False, 2 * week)
|
||||
other = root / "something-else"
|
||||
other.mkdir()
|
||||
# A stray or hostile owner file is an unknown owner: a number past
|
||||
# what a pid holds raised OverflowError, and 0 or -1 name a group.
|
||||
hostile = [folder(f"hostile{index}", None, False, 60)
|
||||
for index in range(3)]
|
||||
for path, text in zip(hostile, ("9" * 40, "0", "-1")):
|
||||
(path / install.TUI_OWNER).write_text(text)
|
||||
self.assertIsNone(install._owner_alive(path))
|
||||
removed = install._prune_work_folders(root, now)
|
||||
self.assertEqual(sorted(removed), sorted([killed, failed_old, unknown_old]))
|
||||
for path in hostile:
|
||||
shutil.rmtree(path)
|
||||
self.assertEqual(sorted(path.name for path in root.iterdir()),
|
||||
sorted(path.name for path in (running, failed, unknown, other)))
|
||||
|
||||
|
||||
Reference in new issue
Block a user