aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/daemon.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/daemon.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/daemon.py72
1 files changed, 41 insertions, 31 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/daemon.py b/packages/meshbay-node/src/meshbay_node/daemon.py
index 7341423..47241cd 100644
--- a/packages/meshbay-node/src/meshbay_node/daemon.py
+++ b/packages/meshbay-node/src/meshbay_node/daemon.py
@@ -52,6 +52,7 @@ from meshbay_node.media_cache import MediaCache
from meshbay_node.tmdb import TmdbClient
from meshbay_node.musicbrainz import MusicBrainzClient
from meshbay_node.keystore import create_keystore, load_keystore, load_or_create_keystore
+from meshbay_node.platform import chmod_private, config_dir, data_dir, state_dir
from meshbay_node.roster import Roster
from meshbay_node.transport import (
Denylist,
@@ -219,7 +220,7 @@ class NodeDaemon:
self._config.data_dir.mkdir(parents=True, exist_ok=True)
self._ui_token_file = self._config.data_dir / "ui-token"
self._ui_token_file.write_text(ui_token)
- self._ui_token_file.chmod(0o600)
+ chmod_private(self._ui_token_file)
from meshbay_node.ui import create_ui_app
ui_app = create_ui_app(self._state)
ui_cfg = uvicorn.Config(
@@ -677,8 +678,12 @@ class NodeDaemon:
# 12. Wait for shutdown
stop_event = asyncio.Event()
loop = asyncio.get_event_loop()
- for sig in (signal.SIGINT, signal.SIGTERM):
- loop.add_signal_handler(sig, stop_event.set)
+ if sys.platform == "win32":
+ for sig in (signal.SIGINT, signal.SIGTERM):
+ signal.signal(sig, lambda *_: stop_event.set())
+ else:
+ for sig in (signal.SIGINT, signal.SIGTERM):
+ loop.add_signal_handler(sig, stop_event.set)
# Milestone 14.8: re-read node.toml without dropping connections.
try:
loop.add_signal_handler(
@@ -1734,7 +1739,7 @@ def main() -> None:
"",
]
cfg_path.write_text("\n".join(toml_lines) + "\n")
- os.chmod(cfg_path, 0o600)
+ chmod_private(cfg_path)
print(f"Config written to {cfg_path}")
unlock_file = config_dir / "unlock.key"
@@ -1742,7 +1747,7 @@ def main() -> None:
import secrets
key = secrets.token_urlsafe(32)
unlock_file.write_text(key + "\n")
- os.chmod(unlock_file, 0o600)
+ chmod_private(unlock_file)
print(f"Unlock key created: {unlock_file}")
cfg = load_config(cfg_path)
@@ -1759,7 +1764,10 @@ def main() -> None:
print()
print("Next steps:")
print(f" 1. Link this node key on {hub_url} → Settings → Link Node")
- print(" 2. systemctl --user enable --now meshbay-node")
+ if sys.platform == "win32":
+ print(" 2. meshbay-node (start the daemon)")
+ else:
+ print(" 2. systemctl --user enable --now meshbay-node")
print(" 3. meshbay-node group add <name> --dir /path/to/files")
print(" 4. meshbay-node gek init")
print(" 5. meshbay-node operator pair")
@@ -1768,12 +1776,12 @@ def main() -> None:
if args.command == "reset":
import shutil
- config_dir = Path.home() / ".config" / "meshbay"
- data_dir = Path.home() / ".local" / "share" / "meshbay"
- state_dir = Path.home() / ".local" / "state" / "meshbay"
+ config_dir_ = config_dir()
+ data_dir_ = data_dir()
+ state_dir_ = state_dir()
items = []
- for d in (config_dir, data_dir):
+ for d in (config_dir_, data_dir_):
if d.exists():
for child in sorted(d.iterdir()):
items.append(child)
@@ -1800,10 +1808,10 @@ def main() -> None:
import urllib.request
import urllib.error
- token_file = data_dir / "ui-token"
+ token_file = data_dir_ / "ui-token"
if token_file.exists():
try:
- cfg = Config(config_dir / "node.toml")
+ cfg = Config(config_dir_ / "node.toml")
tok = token_file.read_text().strip()
url = (f"http://127.0.0.1:{cfg.node.ui_port}"
f"/api/unlink?t={tok}")
@@ -1814,16 +1822,17 @@ def main() -> None:
except Exception:
print("Could not unlink from hub (daemon not reachable).")
- _sp.run(["systemctl", "--user", "disable", "--now", "meshbay-node"],
- capture_output=True)
+ if sys.platform != "win32":
+ _sp.run(["systemctl", "--user", "disable", "--now", "meshbay-node"],
+ capture_output=True)
- for d in (config_dir, data_dir):
+ for d in (config_dir_, data_dir_):
if d.exists():
shutil.rmtree(d)
print(f"Removed {d}")
- if state_dir.is_dir():
- shutil.rmtree(state_dir)
- print(f"Removed {state_dir}")
+ if state_dir_.is_dir():
+ shutil.rmtree(state_dir_)
+ print(f"Removed {state_dir_}")
print("Node state erased. Run 'meshbay-node init' to start over.")
return
@@ -2056,16 +2065,9 @@ def main() -> None:
return
if args.command == "reload":
- # Milestone 14.8. The daemon re-reads node.toml; groups that appeared or
- # whose roots changed are picked up without dropping live connections.
- #
- # Delegated to systemd rather than hunting a PID with pgrep and signalling
- # it directly: an unanchored (or merely unlucky) pattern match there has
- # already SIGHUPed a developer's own running node by accident — see the
- # comment this replaced, and test_cli_dispatch.py's stub_daemon fixture,
- # which had to stub os.kill for exactly that reason. The unit already
- # declares `ExecReload=/bin/kill -HUP $MAINPID`, so systemd sends the
- # signal to the one process it actually started.
+ if sys.platform == "win32":
+ print("reload is not supported on Windows — restart the daemon instead.")
+ sys.exit(1)
_systemctl_user(
"reload", "meshbay-node",
not_running_hint="Node is not running as a systemd unit — start it "
@@ -2075,9 +2077,10 @@ def main() -> None:
return
if args.command == "restart-daemon":
- # Same reasoning as reload: no PID hunting, no manual respawn — systemd
- # already knows how to stop and start this unit, and does not need this
- # process to guess where its log file is.
+ if sys.platform == "win32":
+ print("restart-daemon is not supported on Windows — stop and start "
+ "the daemon manually.")
+ sys.exit(1)
_systemctl_user(
"restart", "meshbay-node",
not_running_hint="meshbay-node is not installed as a systemd unit — "
@@ -2356,6 +2359,13 @@ def main() -> None:
print("Error: hub.username not set in config. Run: meshbay-node init")
sys.exit(1)
+ from meshbay_node.platform import check_media_tools
+ try:
+ check_media_tools(cfg.node.ffmpeg_path, cfg.node.ffprobe_path)
+ except RuntimeError as e:
+ print(f"Error: {e}")
+ sys.exit(1)
+
daemon = NodeDaemon(cfg, Path(args.config or DEFAULT_CONFIG_PATH))
asyncio.run(daemon.run())