Ctrl K
ComunidadGuías…

server/src/usbip_input.h

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

Archivo original ↗Preguntar a KiBot
/**
 * @file src/usbip_input.h
 * @brief The importer's per-platform edge. Everything that DECIDES anything lives in
 *        usbip_input_policy.h; this header is only the part that has to look at a real machine.
 *
 * The split is deliberate and it is the whole reason this feature can be trusted. The platform
 * layer answers questions ("where is the client", "is the driver loaded") and nothing else. The
 * policy layer turns those answers into a verdict and a sentence, and it runs in CI on a machine
 * that has no client, no driver and no second PC. A decision that lives in a platform file is a
 * decision that can only be tested on the one platform it was written for, which is how a Windows
 * result came to be written down as a universal one in this plan's first draft.
 *
 * Terminology: the machine a device is plugged into is the EXPORTER, the machine it arrives on is
 * the IMPORTER. This file is the importer's. Never write "host" or "client" bare — see
 * ArtMoon/docs/usb-ip-input-passthrough.md.
 */
#pragma once

#include <string>

#include "src/usbip_input_policy.h"

namespace input::usbip {
  /**
   * @brief Where this platform's USB/IP client lives, or empty if it is not installed.
   *
   * The two platforms answer this differently and the difference is not cosmetic. On Windows the
   * client is deliberately NOT on PATH — measured on the reference machine, a PATH lookup reports
   * "not installed" for a fully working install — so Windows checks the location our installer
   * uses and then the uninstall record. On Linux it is an ordinary system binary and is resolved
   * the ordinary way.
   *
   * @return An absolute path, or empty. Never a bare name that would need PATH to mean anything.
   */
  std::string client_path();

  /**
   * @brief Gather the facts about this machine that decide whether a device could arrive.
   *
   * Runs nothing privileged and changes nothing. A field this platform cannot answer is left
   * false rather than guessed at.
   */
  ClientProbe probe_client();

  /**
   * @brief The verdict: is the client usable here, and if not, exactly what is missing.
   *
   * Portable, and defined here because it is the same judgement on both platforms — only the
   * facts differ. Callers use this, not probe_client(), so the decision cannot be made twice in
   * two different ways.
   */
  inline ClientAvailability client_available() {
    return assess_client(probe_client());
  }

  /**
   * @brief What a run of the client produced.
   *
   * stdout and stderr are kept APART, not merged, and that is deliberate on both platforms. The
   * reason a tool refused is on stderr while the exit code only says "no", so a merged buffer turns
   * a diagnosable refusal into an anonymous one. This mirrors the exporter half, where a shell
   * pipeline that merged the two made a held device look like an unreadable one.
   */
  struct run_result_t {
    int code = 0;
    std::string out;
    std::string err;
  };

  /**
   * @brief Take one device from the exporter. Blocking; the caller decides where it runs.
   *
   * Windows drives the client directly as the logged-on user - proven: a UAC-filtered limited token
   * attached successfully. Linux CANNOT, because `/sys/devices/platform/vhci_hcd.0/attach` is a
   * root-only write, so there this goes through the privileged helper and the polkit action beside
   * it. The asymmetry is real and is not to be smoothed over by giving Windows a helper it
   * provably does not need.
   */
  run_result_t run_attach(std::string_view exporter, std::string_view busid);

  /**
   * @brief Ask the exporter what it is offering.
   *
   * Read-only and unprivileged on both platforms: it is a query, not an action, so it needs neither
   * the helper nor elevation. Runs the client with the two streams kept apart.
   */
  run_result_t run_list_remote(std::string_view exporter);

  /**
   * @brief Ask this machine what it is currently holding.
   *
   * This is the ONLY way to learn a held device's port, and the port is the only thing `detach`
   * accepts - so a session that attaches and does not read this back cannot give the device up.
   *
   * Read-only on both platforms. It is NOT reliably unprivileged on Linux, and an earlier version of
   * this comment claimed it was, "proven" on the reference box. That proof held only where the host
   * happened to run as root: the remote busid lives in /var/run/vhci_hcd, which is 0700 root, so a
   * host running as an ordinary user is shown the port table and never which device is on it. On
   * Linux this therefore goes through the privileged helper, which reads those records as root.
   */
  run_result_t run_list_attached();

  /**
   * @brief Give one device back. Takes a PORT, not a busid - `detach` accepts nothing else, which
   *        is why the caller has to remember busid -> port for the life of the session.
   */
  run_result_t run_detach(int port);

  /**
   * @brief Attach, and say what actually happened. The judgement is the policy's, not this file's,
   *        so the same output is read the same way wherever it came from.
   */
  inline AttachResult attach(std::string_view exporter, std::string_view busid) {
    const auto result = run_attach(exporter, busid);
    return classify_attach(result.code, result.out, result.err);
  }
}  // namespace input::usbip