72 lines
2.9 KiB
Text
72 lines
2.9 KiB
Text
|
|
// 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();
|
||
|
|
}
|