Ctrl K
ComunidadGuías…

server/src/steam_process_tracker.h

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/** @file src/steam_process_tracker.h
 *  @brief Process ownership and cleanup policy for Steam launches.
 *
 * Steam does not expose a portable "process tree for this launch" API.  This
 * small boundary deliberately owns that missing behaviour.  The policy is
 * usable with synthetic snapshots, which keeps the safety decisions testable
 * without starting a game or depending on a particular Steam runtime.
 */
#pragma once

#include <chrono>
#include <cstdint>
#include <filesystem>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>

namespace platf::steam::lifecycle {

  using process_id_t = std::uint64_t;

  struct process_info {
    process_id_t pid = 0;
    process_id_t parent_pid = 0;
    std::filesystem::path executable;
    std::filesystem::path cwd;
    std::vector<std::string> command_line;
    // Linux /proc start-time ticks provide PID identity across PID reuse.
    // Zero means the platform provider has no identity value.
    std::uint64_t start_time_ticks = 0;
    // Filled only by the desktop Big Picture helper from the process's
    // SteamAppId environment; ordinary provider tracking does not need it.
    std::uint32_t steam_app_id = 0;
  };

  struct process_snapshot {
    // A snapshot may be incomplete when /proc entries disappear while they
    // are being read. Available records remain useful for association; the
    // caller can use complete to decide whether absence proves termination.
    bool complete = true;
    std::map<process_id_t, process_info> processes;
  };

  enum class association_outcome {
    associated,
    baseline_only,
    untrackable,
  };

  struct tracked_process {
    process_info info;
    bool protected_process = false;
  };

  struct tracked_tree {
    process_id_t root_pid = 0;
    std::filesystem::path install_dir;
    std::map<process_id_t, tracked_process> processes;

    bool empty() const { return processes.empty(); }
  };

  struct association_result {
    association_outcome outcome = association_outcome::untrackable;
    tracked_tree tree;
    std::string reason;

    bool associated() const { return outcome == association_outcome::associated; }
  };

  // A process id in this list is never selected as a game process.  Steam
  // itself is also recognized by executable/cmdline below; explicit roots are
  // useful when a launcher uses a wrapper with a non-Steam executable name.
  struct association_options {
    std::vector<process_id_t> protected_steam_roots;
  };

  // Pure policy: compare the post-launch snapshot with the pre-launch
  // baseline and associate only new processes belonging to install_dir.
  association_result associate(const process_snapshot &baseline,
                               const process_snapshot &after,
                               const std::filesystem::path &install_dir,
                               const association_options &options = {});

  bool path_is_within(const std::filesystem::path &path,
                      const std::filesystem::path &directory);
  bool is_protected_steam_process(const process_info &process,
                                  const association_options &options = {});

  class process_snapshot_provider {
  public:
    virtual ~process_snapshot_provider() = default;
    virtual std::optional<process_snapshot> snapshot() = 0;
  };

  // Linux reads /proc and Windows uses the Toolhelp process snapshot API.
  // Other platforms provide an empty successful snapshot.
  std::optional<process_snapshot> snapshot_processes();

  class tracker {
  public:
    explicit tracker(std::shared_ptr<process_snapshot_provider> provider = {});

    // The baseline must be captured immediately before Steam is launched.
    bool begin(const std::filesystem::path &install_dir,
              const association_options &options = {});
    association_result finish();
    association_result finish(const process_snapshot &after);
    // Whether any retained PID is present in a current snapshot. Incomplete
    // snapshots conservatively retain the prior running state.
    bool has_live_processes(const process_snapshot &current) const;
    const tracked_tree &tree() const { return tree_; }
    void clear();

  private:
    std::shared_ptr<process_snapshot_provider> provider_;
    process_snapshot baseline_;
    std::filesystem::path install_dir_;
    association_options options_;
    tracked_tree tree_;
    bool begun_ = false;
  };

  enum class signal_kind { terminate, kill };

  class process_controller {
  public:
    virtual ~process_controller() = default;
    virtual bool signal(process_id_t pid, signal_kind signal) = 0;
    virtual bool alive(process_id_t pid) = 0;
    virtual bool identity_matches(process_id_t pid, const process_info &expected) {
      (void) pid;
      (void) expected;
      return true;
    }
    virtual void sleep_for(std::chrono::milliseconds duration) = 0;
  };

  struct stop_options {
    std::chrono::milliseconds grace_period {2000};
    std::chrono::milliseconds poll_period {25};
  };

  struct stop_result {
    std::size_t terminate_sent = 0;
    std::size_t kill_sent = 0;
    std::size_t skipped = 0;
    bool complete = true;
  };

  // Stops only processes retained in tree.  Children are signalled before
  // parents and every PID is checked as alive by the injectable controller;
  // callers can therefore reject PID reuse or a changed executable.  The
  // implementation never sends a process-group signal.
  stop_result stop_tree(const tracked_tree &tree,
                        process_controller &controller,
                        const stop_options &options = {});

  // Native controller used by production integration.
  std::shared_ptr<process_controller> native_process_controller();

}  // namespace platf::steam::lifecycle