Fix critical race conditions and unsafe disk report command
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
import tempfile
|
||||
from typing import Any, Dict
|
||||
|
||||
_PATH = "/var/server-bot/runtime.json"
|
||||
_STATE: Dict[str, Any] = {}
|
||||
_LOCK = threading.RLock()
|
||||
_LOADED = False
|
||||
|
||||
|
||||
def configure(path: str | None):
|
||||
@@ -13,40 +17,57 @@ def configure(path: str | None):
|
||||
|
||||
|
||||
def _load_from_disk():
|
||||
global _STATE
|
||||
global _STATE, _LOADED
|
||||
if not os.path.exists(_PATH):
|
||||
_STATE = {}
|
||||
_LOADED = True
|
||||
return
|
||||
try:
|
||||
with open(_PATH, "r", encoding="utf-8") as f:
|
||||
_STATE = json.load(f)
|
||||
except Exception:
|
||||
_STATE = {}
|
||||
_LOADED = True
|
||||
|
||||
|
||||
def _save():
|
||||
os.makedirs(os.path.dirname(_PATH), exist_ok=True)
|
||||
directory = os.path.dirname(_PATH) or "."
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
try:
|
||||
with open(_PATH, "w", encoding="utf-8") as f:
|
||||
json.dump(_STATE, f)
|
||||
fd, tmp_path = tempfile.mkstemp(prefix=".runtime.", suffix=".json", dir=directory)
|
||||
try:
|
||||
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
||||
json.dump(_STATE, f, ensure_ascii=False)
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
os.replace(tmp_path, _PATH)
|
||||
finally:
|
||||
if os.path.exists(tmp_path):
|
||||
try:
|
||||
os.unlink(tmp_path)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def get_state() -> Dict[str, Any]:
|
||||
if not _STATE:
|
||||
_load_from_disk()
|
||||
return _STATE
|
||||
with _LOCK:
|
||||
if not _LOADED:
|
||||
_load_from_disk()
|
||||
return _STATE
|
||||
|
||||
|
||||
def set_state(key: str, value: Any):
|
||||
if not _STATE:
|
||||
_load_from_disk()
|
||||
_STATE[key] = value
|
||||
_save()
|
||||
with _LOCK:
|
||||
if not _LOADED:
|
||||
_load_from_disk()
|
||||
_STATE[key] = value
|
||||
_save()
|
||||
|
||||
|
||||
def get(key: str, default: Any = None) -> Any:
|
||||
if not _STATE:
|
||||
_load_from_disk()
|
||||
return _STATE.get(key, default)
|
||||
with _LOCK:
|
||||
if not _LOADED:
|
||||
_load_from_disk()
|
||||
return _STATE.get(key, default)
|
||||
|
||||
Reference in New Issue
Block a user