// SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // How crafter-debug finds running apps. Each app writes // $HOME/.cache/crafter-debug/instances/.json, readable by its // user only, and holds an flock on .lock for as long as it runs. // // Spelled with $HOME, not $XDG_CACHE_HOME: inside the CrafterBuild Flatpak // XDG_CACHE_HOME points into ~/.var/app/..., which a native crafter-build // would not see, while $HOME/.cache is the same path for both. Every // `flatpak run` is its own sandbox with its own pid namespace, so pids // collide and cannot name an instance or tell whether it lives: a random // instance id names it, and the lock says whether it lives, since flock works // across sandboxes on the shared filesystem. An app killed without running // its destructor leaves its files behind with the lock free, and the next // listing removes them. The pattern is Crafter.Review's LifetimeLock. export module Crafter.Debug.Core:Discovery; import std; export namespace Crafter::Debug { struct InstanceRecord { std::string name; std::string instanceId; // The app's own pid, in its own pid namespace: what a debugger // started inside that sandbox attaches to. std::int64_t pid = 0; // Inside a Flatpak: the sandbox's [Instance] instance-id (for // `flatpak enter`), and the host pid of its init (bwrapinfo.json's // child-pid), which `kill -KILL` ends the whole sandbox through. std::string flatpakInstance; std::int64_t hostInitPid = 0; std::string exe; std::uint16_t port = 0; std::string token; std::uint32_t protocol = 0; std::string sourceId; // The crafter-debug client built with this app. std::string client; // Seconds since the epoch. std::int64_t startedAt = 0; }; // $CRAFTER_DEBUG_DIR when set (tests), else $HOME/.cache/crafter-debug/instances. std::filesystem::path DiscoveryDirectory(); // An app's presence: writes the record and holds the lock while it lives. class Registration { public: // Fills in instanceId, pid, the Flatpak fields, exe and startedAt. // Throws std::system_error when the directory or files cannot be made. explicit Registration(InstanceRecord record); ~Registration(); Registration(const Registration&) = delete; Registration& operator=(const Registration&) = delete; const InstanceRecord& Record() const { return record; } private: InstanceRecord record; std::filesystem::path recordPath; std::filesystem::path lockPath; std::int32_t lockFd = -1; }; struct Instance { InstanceRecord record; std::filesystem::path recordPath; }; // Every live instance, newest first; the files of dead ones are removed. std::vector ListInstances(); }