Crafter.Debug/interfaces/Crafter.Debug.Core-Discovery.cppm

72 lines
2.9 KiB
Text
Raw Normal View History

// 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/<instanceId>.json, readable by its
// user only, and holds an flock on <instanceId>.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<Instance> ListInstances();
}