]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
feat: Replace experimental greeting format 2 with info operation
authorJoel Rosdahl <joel@rosdahl.net>
Sat, 16 May 2026 21:23:28 +0000 (23:23 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Mon, 18 May 2026 18:59:41 +0000 (20:59 +0200)
af6f9141bb7c1dad010f42d7aa45be6f984f447b added experimental support for
a new greeting message format including server identity and diagnostics.
Storage helpers are supposed to use the new greeting format when ccache
sets the CRSH_FORMAT_MAX environment variable. While this complicates
the original protocol design, the advantage is that the client gets the
information directly without an extra IPC roundtrip.

After trying this change out for a while, I've decided to revert it:

1. It complicates storage helper implementation a bit too much.
2. It's another variable to set when starting the storage helper
   for usage with `crsh:` URLs.
3. It's actually only when the client has logging enabled that the
   server identity and diagnostics are of interest.

Instead, this commit adds an info operation with the same information,
handled as an ordinary capability evolution.

doc/remote_storage_helper_spec.md
src/ccache/storage/remote/client.cpp
src/ccache/storage/remote/client.hpp
src/ccache/storage/remote/helper.cpp
test/storage/client/main.cpp
test/storage/helper/main.cpp

index be29e17d96654312aa8e786e28ba09a27f1fe28f..22a3f19e5b6a8cee65889082eb292ad71ba93dea 100644 (file)
@@ -46,9 +46,6 @@ The helper program will get startup parameters as environment variables:
 - `CRSH_IDLE_TIMEOUT`: Timeout (in seconds) for the storage helper to wait
   before exiting after client inactivity. If set to 0, the helper will stay up
   indefinitely and never exit due to inactivity.
-- `CRSH_FORMAT_MAX`: Maximum version of the protocol greeting message format
-  supported by the client as a positive integer. If not set, the storage helper
-  must use the default value `1`.
 
 Custom attributes from ccache's `remote_storage` configuration will also
 provided as environment variables:
@@ -105,42 +102,31 @@ This is a specification of the custom binary IPC protocol between ccache
                                     ;   put: key/value not stored
                                     ;   remove: key/value not removed
 <err>             ::= 0x02 <msg>    ; e.g. bad parameters, network/server errors
-<capabilities>    ::= <cap_len> <cap_data>
-<cap_len>         ::= <u8>
-<cap_data>        ::= <cap>*        ; <cap_len> entries
-<cap>             ::= <cap0>        ; currently only one capability is defined
-<cap0>            ::= 0x00          ; get/put/remove/stop operations
 ```
 
-### Server greeting (server to client)
+### Capabilities
 
-In response to the client's startup parameters, the server sends a greeting to
-the client when the client has connected. The server must use the highest
-greeting message format that is supported by both sides. If the client's maximum
-greeting message format (`CRSH_FORMAT_MAX`) is lower than the lowest format the
-server supports, the server must close the connection.
+- 0x00: `get`/`put`/`remove` requests
+- 0x01: `info` request
 
-The client must verify server capabilities before sending any request.
-
-#### Greeting 1
-
-```
-<greeting_1>      ::= 0x01 <capabilities>
-```
+### Server greeting (server to client)
 
-#### Greeting 2
+The server sends a greeting to the client when the client has connected. The
+client must verify protocol version and server capabilities before sending any
+request.
 
 ```
-<greeting_2>      ::= 0x02 <capabilities> <server_identity> <diagnostics>
-<server_identity> ::= <msg>              ; software name and version, etc.
-<diagnostics>     ::= <diag_num> <diag>* ; <diag_num> diagnostics messages
-<diag_num>        ::= <u8>
-<diag>            ::= <msg>              ; message to be logged by the client
+<greeting>        ::= <protocol_ver> <capabilities>
+<protocol_ver>    ::= 0x01          ; protocol version 1
+<capabilities>    ::= <cap_len> <cap_data>
+<cap_len>         ::= <u8>
+<cap_data>        ::= <cap>*        ; <cap_len> entries
+<cap>             ::= <byte>        ; see "Capabilities" above
 ```
 
 ### Requests (client to server with response from server)
 
-#### Get
+#### Get (capability 0x00)
 
 Get a value from remote storage.
 
@@ -149,7 +135,7 @@ Get a value from remote storage.
 <get_response>    ::= <ok> <value> | <noop> | <err>
 ```
 
-#### Put
+#### Put (capability 0x00)
 
 Put a value in remote storage.
 
@@ -159,7 +145,7 @@ Put a value in remote storage.
 <put_response>    ::= <ok> | <noop> | <err>
 ```
 
-#### Remove
+#### Remove (capability 0x00)
 
 Remove a value from remote storage.
 
@@ -168,7 +154,7 @@ Remove a value from remote storage.
 <remove_response> ::= <ok> | <noop> | <err>
 ```
 
-#### Stop
+#### Stop (always available)
 
 Tell the server to shut down and exit. The server must terminate immediately
 without waiting for ongoing client operations to finish.
@@ -179,3 +165,16 @@ without waiting for ongoing client operations to finish.
 ```
 
 Note that the connection might be closed before `<stop_response>` can be read.
+
+#### Info (capability 0x01)
+
+Get information about the helper.
+
+```
+<info_request>    ::= 0x04
+<info_response>   ::= <server_identity> <diagnostics>
+<server_identity> ::= <msg>              ; software name and version, etc.
+<diagnostics>     ::= <diag_num> <diag>* ; <diag_num> diagnostics messages
+<diag_num>        ::= <u8>
+<diag>            ::= <msg>              ; message to be logged by the client
+```
index 119d4a408781075635121e2beaa408733dfeb214..d2387baae7f762ac9a0cdd85ec04008f7fc6c5fc 100644 (file)
@@ -32,6 +32,7 @@ constexpr uint8_t k_request_get = 0x00;
 constexpr uint8_t k_request_put = 0x01;
 constexpr uint8_t k_request_remove = 0x02;
 constexpr uint8_t k_request_stop = 0x03;
+constexpr uint8_t k_request_info = 0x04;
 
 } // namespace
 
@@ -95,21 +96,9 @@ Client::connect(const std::string& path)
 }
 
 uint8_t
-Client::greeting_format() const
+Client::protocol_version() const
 {
-  return m_greeting_format;
-}
-
-const std::string&
-Client::server_identity() const
-{
-  return m_server_identity;
-}
-
-const std::vector<std::string>&
-Client::diagnostics() const
-{
-  return m_diagnostics;
+  return m_protocol_version;
 }
 
 const std::vector<Client::Capability>&
@@ -149,6 +138,37 @@ Client::get(std::span<const uint8_t> key)
   return receive_response_get();
 }
 
+tl::expected<Client::InfoResponse, Client::Error>
+Client::info()
+{
+  if (!m_connected) {
+    return tl::unexpected(Error(Failure::error, "Not connected"));
+  }
+
+  m_request_start_time = std::chrono::steady_clock::now();
+
+  util::Bytes msg({k_request_info});
+  TRY(send_bytes(msg));
+
+  Client::InfoResponse response;
+
+  TRY_ASSIGN(uint8_t identity_len, receive_u8());
+  TRY_ASSIGN(auto identity_bytes, receive_bytes(identity_len));
+  response.server_identity.assign(
+    reinterpret_cast<const char*>(identity_bytes.data()), identity_len);
+
+  TRY_ASSIGN(uint8_t diag_num, receive_u8());
+  response.diagnostics.reserve(diag_num);
+  for (uint8_t i = 0; i < diag_num; ++i) {
+    TRY_ASSIGN(uint8_t msg_len, receive_u8());
+    TRY_ASSIGN(auto msg_bytes, receive_bytes(msg_len));
+    response.diagnostics.emplace_back(
+      reinterpret_cast<const char*>(msg_bytes.data()), msg_len);
+  }
+
+  return response;
+}
+
 tl::expected<bool, Client::Error>
 Client::put(std::span<const uint8_t> key,
             std::span<const uint8_t> value,
@@ -224,22 +244,19 @@ Client::close()
   if (m_connected) {
     m_channel.close();
     m_connected = false;
-    m_greeting_format = 0;
+    m_protocol_version = 0;
     m_capabilities.clear();
-    m_server_identity.clear();
-    m_diagnostics.clear();
   }
 }
 
 tl::expected<void, Client::Error>
 Client::read_greeting()
 {
-  TRY_ASSIGN(m_greeting_format, receive_u8());
-  if (m_greeting_format != k_greeting_format_1
-      && m_greeting_format != k_greeting_format_2) {
+  TRY_ASSIGN(m_protocol_version, receive_u8());
+  if (m_protocol_version != k_protocol_version) {
     return tl::unexpected(
       Error(Failure::error,
-            FMT("Unsupported greeting format: {}", m_greeting_format)));
+            FMT("Unsupported protocol version: {}", m_protocol_version)));
   }
 
   TRY_ASSIGN(uint8_t cap_len, receive_u8());
@@ -250,23 +267,6 @@ Client::read_greeting()
     m_capabilities.push_back(static_cast<Capability>(cap_byte));
   }
 
-  if (m_greeting_format == k_greeting_format_2) {
-    TRY_ASSIGN(uint8_t identity_len, receive_u8());
-    TRY_ASSIGN(auto identity_bytes, receive_bytes(identity_len));
-    m_server_identity.assign(
-      reinterpret_cast<const char*>(identity_bytes.data()), identity_len);
-
-    TRY_ASSIGN(uint8_t diag_num, receive_u8());
-    m_diagnostics.clear();
-    m_diagnostics.reserve(diag_num);
-    for (uint8_t i = 0; i < diag_num; ++i) {
-      TRY_ASSIGN(uint8_t msg_len, receive_u8());
-      TRY_ASSIGN(auto msg_bytes, receive_bytes(msg_len));
-      m_diagnostics.emplace_back(
-        reinterpret_cast<const char*>(msg_bytes.data()), msg_len);
-    }
-  }
-
   return {};
 }
 
index 4091c293e84db223628edcf3cba18d85579c14b8..7d7ed9aa382de3d2aff1f2a5749bf34f568e42e6 100644 (file)
@@ -45,12 +45,11 @@ namespace storage::remote {
 class Client : util::NonCopyable
 {
 public:
-  static constexpr uint8_t k_greeting_format_1 = 0x01;
-  static constexpr uint8_t k_greeting_format_2 = 0x02;
-  static constexpr uint8_t k_max_greeting_format = k_greeting_format_2;
+  static constexpr uint8_t k_protocol_version = 0x01;
 
   enum class Capability : uint8_t {
-    get_put_remove_stop = 0x00, // get/put/remove/stop operations
+    get_put_remove = 0x00, // get/put/remove operations
+    info = 0x01,           // info operation
   };
 
   enum class Status : uint8_t {
@@ -76,6 +75,12 @@ public:
     }
   };
 
+  struct InfoResponse
+  {
+    std::string server_identity;
+    std::vector<std::string> diagnostics;
+  };
+
   struct PutFlags
   {
     bool overwrite = false; // bit 0 (LSB): overwrite existing value
@@ -86,18 +91,21 @@ public:
   ~Client();
 
   tl::expected<void, Error> connect(const std::string& path);
-  uint8_t greeting_format() const;
+  uint8_t protocol_version() const;
   const std::vector<Capability>& capabilities() const;
-  const std::string& server_identity() const;
-  const std::vector<std::string>& diagnostics() const;
   bool has_capability(Capability cap) const;
 
   tl::expected<std::optional<util::Bytes>, Error>
   get(std::span<const uint8_t> key);
+
+  tl::expected<InfoResponse, Error> info();
+
   tl::expected<bool, Error> put(std::span<const uint8_t> key,
                                 std::span<const uint8_t> value,
                                 PutFlags flags);
+
   tl::expected<bool, Error> remove(std::span<const uint8_t> key);
+
   tl::expected<void, Error> stop();
 
   void close();
@@ -108,10 +116,8 @@ private:
 #else
   util::BufferedIpcChannelClient<util::UnixSocketClient> m_channel;
 #endif
-  uint8_t m_greeting_format;
+  uint8_t m_protocol_version = 0;
   std::vector<Capability> m_capabilities;
-  std::string m_server_identity;
-  std::vector<std::string> m_diagnostics;
   bool m_connected = false;
   std::chrono::milliseconds m_data_timeout;
   std::chrono::milliseconds m_request_timeout;
@@ -134,8 +140,10 @@ inline std::string
 to_string(Client::Capability capability)
 {
   switch (capability) {
-  case Client::Capability::get_put_remove_stop:
-    return "get_put_remove_stop";
+  case Client::Capability::get_put_remove:
+    return "get/put/remove";
+  case Client::Capability::info:
+    return "info";
   }
   return FMT("{}", static_cast<int>(capability));
 }
index 63a3fe3d43134a7eab3e9d732b4832ef1b3b85b6..2c0f0a7dc4a4c7114582d1fbe9b169be88e20a4b 100644 (file)
@@ -76,8 +76,7 @@ constexpr std::string_view k_named_pipe_prefix = "\\\\.\\pipe\\";
 std::string
 generate_endpoint_name(
   const Url& url,
-  const std::vector<RemoteStorage::Backend::Attribute>& attributes,
-  uint8_t max_greeting_format)
+  const std::vector<RemoteStorage::Backend::Attribute>& attributes)
 {
   static const uint8_t delimiter[1] = {0};
 
@@ -99,8 +98,6 @@ generate_endpoint_name(
     hash.hash(delimiter);
     hash.hash(attr.value);
   }
-  hash.hash(delimiter);
-  hash.hash(static_cast<int64_t>(max_greeting_format));
   return FMT("storage-{}-{}", url.scheme(), util::format_base16(hash.digest()));
 }
 
@@ -167,8 +164,6 @@ build_helper_env(
   env_vars.emplace_back(FMT("CRSH_URL={}", url.str()));
   env_vars.emplace_back(
     FMT("CRSH_IDLE_TIMEOUT={}", idle_timeout.count() / 1000));
-  env_vars.emplace_back(
-    FMT("CRSH_FORMAT_MAX={}", Client::k_max_greeting_format));
   env_vars.emplace_back(FMT("CRSH_NUM_ATTR={}", attributes.size()));
 
   for (size_t i = 0; i < attributes.size(); ++i) {
@@ -188,8 +183,8 @@ is_ccache_crsh_var(std::string_view entry)
   }
 
   return name == "CRSH_IPC_ENDPOINT" || name == "CRSH_URL"
-         || name == "CRSH_IDLE_TIMEOUT" || name == "CRSH_FORMAT_MAX"
-         || name == "CRSH_NUM_ATTR" || name.starts_with("CRSH_ATTR_KEY_")
+         || name == "CRSH_IDLE_TIMEOUT" || name == "CRSH_NUM_ATTR"
+         || name.starts_with("CRSH_ATTR_KEY_")
          || name.starts_with("CRSH_ATTR_VALUE_");
 }
 
@@ -428,8 +423,7 @@ HelperBackend::HelperBackend(const fs::path& helper_path,
     // No m_endpoint_lock_path needed since we won't spawn a helper.
   } else {
     // The common case:
-    auto endpoint_name =
-      generate_endpoint_name(url, attributes, Client::k_max_greeting_format);
+    auto endpoint_name = generate_endpoint_name(url, attributes);
 #ifdef _WIN32
     m_endpoint = FMT("{}ccache-{}", k_named_pipe_prefix, endpoint_name);
     m_endpoint_lock_path = FMT("{}/{}", temp_dir, endpoint_name);
@@ -450,19 +444,26 @@ HelperBackend::finalize_connection()
 {
   if (util::logging::enabled()) {
     auto capabilities = util::join(m_client.capabilities(), " ");
-    LOG("Storage helper: {} (format: {}, capabilities: {})",
-        !m_client.server_identity().empty() ? m_client.server_identity()
-                                            : "[unknown]",
-        m_client.greeting_format(),
-        capabilities);
-    for (const auto& msg : m_client.diagnostics()) {
+    std::string server_identity = "[unknown]";
+    std::vector<std::string> diagnostics;
+    if (m_client.has_capability(Client::Capability::info)) {
+      auto info_result = m_client.info();
+      if (!info_result) {
+        LOG("Failed to get helper info: {}", info_result.error().message);
+        return tl::unexpected(Failure::error);
+      }
+      server_identity = std::move(info_result->server_identity);
+      diagnostics = std::move(info_result->diagnostics);
+    }
+    LOG("Storage helper: {} (capabilities: {})", server_identity, capabilities);
+    for (const auto& msg : diagnostics) {
       LOG("Storage helper diagnostic: {}", msg);
     }
   }
 
-  if (!m_client.has_capability(Client::Capability::get_put_remove_stop)) {
+  if (!m_client.has_capability(Client::Capability::get_put_remove)) {
     LOG("Storage helper does not support capability {}",
-        static_cast<int>(Client::Capability::get_put_remove_stop));
+        static_cast<int>(Client::Capability::get_put_remove));
     return tl::unexpected(Failure::error);
   }
 
index 347d4110bdedaa76588511d567422a0cdc082b91..fa463bc8ebbfadaac8e36a91466dd675a442625c 100644 (file)
@@ -48,6 +48,7 @@ This is a CLI tool for testing ccache storage helper implementations.
 
 Commands:
     ping                            check if helper is reachable
+    info                            print helper info
     get KEY -o FILE                 get a value and output to file
     get KEY -o -                    get a value and output to stdout
     put [--overwrite] KEY -i FILE   put a value from file
@@ -67,19 +68,16 @@ print_usage(FILE* stream, const char* program_name)
   PRINT(stream, USAGE_TEXT, program_name);
 }
 
-int
+tl::expected<int, std::string>
 cmd_get(Client& client, const std::vector<std::string>& args)
 {
   if (args.size() != 3 || args[1] != "-o") {
-    PRINT(stderr, "Error: get requires: KEY -o OUTPUT\n");
-    PRINT(stderr, "  where OUTPUT is a file path or - for stdout\n");
-    return 1;
+    return tl::unexpected("missing arguments");
   }
 
   auto key_result = util::parse_base16(args[0]);
   if (!key_result) {
-    PRINT(stderr, "Error: Invalid hex key: {}\n", key_result.error());
-    return 1;
+    return tl::unexpected(FMT("invalid hex key: {}", key_result.error()));
   }
   const auto& key = *key_result;
   const auto& output = args[2];
@@ -87,12 +85,11 @@ cmd_get(Client& client, const std::vector<std::string>& args)
   auto result = client.get(key);
 
   if (!result) {
-    PRINT(stderr, "Error: {}\n", result.error().message);
-    return 1;
+    return tl::unexpected(result.error().message);
   }
 
   if (!*result) {
-    PRINT(stderr, "Key not found: {}\n", util::format_base16(key));
+    PRINT(stdout, "Key not found: {}", util::format_base16(key));
     return 2;
   }
 
@@ -102,15 +99,32 @@ cmd_get(Client& client, const std::vector<std::string>& args)
     std::fwrite(value.data(), 1, value.size(), stdout);
   } else {
     if (auto r = util::write_file(output, value); !r) {
-      PRINT(stderr, "Error writing to {}: {}", output, r.error());
-      return 1;
+      return tl::unexpected(FMT("failed writing to {}: {}", output, r.error()));
     }
   }
 
   return 0;
 }
 
-int
+tl::expected<int, std::string>
+cmd_info(Client& client, const std::vector<std::string>& args)
+{
+  if (args.size() != 0) {
+    return tl::unexpected("info does not take any argument");
+  }
+
+  auto result = client.info();
+
+  if (!result) {
+    return tl::unexpected(result.error().message);
+  }
+
+  PRINT(stdout, "Server identity: {}\n", result->server_identity);
+  PRINT(stdout, "Capabilities: {}\n", util::join(client.capabilities(), " "));
+  return 0;
+}
+
+tl::expected<int, std::string>
 cmd_put(Client& client, const std::vector<std::string>& args)
 {
   Client::PutFlags flags;
@@ -122,17 +136,12 @@ cmd_put(Client& client, const std::vector<std::string>& args)
   }
 
   if (args.size() - start_idx != 3) {
-    PRINT(stderr,
-          "Error: put requires: [--overwrite] KEY -i INPUT\n"
-          "                 or: [--overwrite] KEY -v VALUE\n"
-          "  where INPUT is a file path or - for stdin\n");
-    return 1;
+    return tl::unexpected("missing arguments");
   }
 
   auto key_result = util::parse_base16(args[start_idx]);
   if (!key_result) {
-    PRINT(stderr, "Error: Invalid hex key: {}\n", key_result.error());
-    return 1;
+    return tl::unexpected(FMT("invalid hex key: {}", key_result.error()));
   }
   const auto& key = *key_result;
   const auto& mode = args[start_idx + 1];
@@ -146,28 +155,25 @@ cmd_put(Client& client, const std::vector<std::string>& args)
     if (input == "-") {
       auto r = util::read_fd(STDIN_FILENO);
       if (!r) {
-        PRINT(stderr, "Error reading from stdin: {}", r.error());
-        return 1;
+        return tl::unexpected(FMT("failed reading from stdin: {}", r.error()));
       }
       value = std::move(*r);
     } else {
       auto r = util::read_file<util::Bytes>(input);
       if (!r) {
-        PRINT(stderr, "Error reading from {}: {}", input, r.error());
-        return 1;
+        return tl::unexpected(
+          FMT("failed reading from {}: {}", input, r.error()));
       }
       value = std::move(*r);
     }
   } else {
-    PRINT(stderr, "Error: Unknown mode \"{}\". Use -v or -i\n", mode);
-    return 1;
+    return tl::unexpected(FMT("unknown mode flag: {}", mode));
   }
 
   auto result = client.put(key, value, flags);
 
   if (!result) {
-    PRINT(stderr, "Error: {}\n", result.error().message);
-    return 1;
+    return tl::unexpected(result.error().message);
   }
 
   if (*result) {
@@ -179,26 +185,23 @@ cmd_put(Client& client, const std::vector<std::string>& args)
   }
 }
 
-int
+tl::expected<int, std::string>
 cmd_remove(Client& client, const std::vector<std::string>& args)
 {
   if (args.size() != 1) {
-    PRINT(stderr, "Error: remove requires exactly 1 argument: KEY\n");
-    return 1;
+    return tl::unexpected("remove requires exactly 1 argument: KEY");
   }
 
   auto key_result = util::parse_base16(args[0]);
   if (!key_result) {
-    PRINT(stderr, "Error: Invalid hex key: {}\n", key_result.error());
-    return 1;
+    return tl::unexpected(FMT("invalid hex key: {}", key_result.error()));
   }
   const auto& key = *key_result;
 
   auto result = client.remove(key);
 
   if (!result) {
-    PRINT(stderr, "Error: {}\n", result.error().message);
-    return 1;
+    return tl::unexpected(result.error().message);
   }
 
   if (*result) {
@@ -210,31 +213,28 @@ cmd_remove(Client& client, const std::vector<std::string>& args)
   }
 }
 
-int
+tl::expected<int, std::string>
 cmd_stop(Client& client, const std::vector<std::string>& args)
 {
   if (!args.empty()) {
-    PRINT(stderr, "Error: stop takes no arguments\n");
-    return 1;
+    return tl::unexpected("stop takes no arguments");
   }
 
   auto result = client.stop();
 
   if (!result) {
-    PRINT(stderr, "Error: {}\n", result.error().message);
-    return 1;
+    return tl::unexpected(result.error().message);
   }
 
   PRINT(stdout, "Helper stopped\n");
   return 0;
 }
 
-int
+tl::expected<int, std::string>
 cmd_ping(const std::vector<std::string>& args)
 {
   if (!args.empty()) {
-    PRINT(stderr, "Error: ping takes no arguments\n");
-    return 1;
+    return tl::unexpected("ping takes no arguments");
   }
 
   // Connection and protocol verification already done in main.
@@ -244,6 +244,47 @@ cmd_ping(const std::vector<std::string>& args)
 
 } // namespace
 
+tl::expected<void, std::string>
+require_capability(const Client& client, Client::Capability capability)
+{
+  if (!client.has_capability(capability)) {
+    return tl::unexpected(
+      FMT("storage helper does not support capability \"{}\"",
+          to_string(capability)));
+  }
+  return {};
+}
+
+tl::expected<int, std::string>
+handle_command(Client& client,
+               const std::string& command,
+               const std::vector<std::string>& args)
+{
+  int result = 0;
+
+  if (command == "ping") {
+    TRY_ASSIGN(result, cmd_ping(args));
+  } else if (command == "get") {
+    TRY(require_capability(client, Client::Capability::get_put_remove));
+    TRY_ASSIGN(result, cmd_get(client, args));
+  } else if (command == "info") {
+    TRY(require_capability(client, Client::Capability::info));
+    TRY_ASSIGN(result, cmd_info(client, args));
+  } else if (command == "put") {
+    TRY(require_capability(client, Client::Capability::get_put_remove));
+    TRY_ASSIGN(result, cmd_put(client, args));
+  } else if (command == "remove") {
+    TRY(require_capability(client, Client::Capability::get_put_remove));
+    TRY_ASSIGN(result, cmd_remove(client, args));
+  } else if (command == "stop") {
+    TRY_ASSIGN(result, cmd_stop(client, args));
+  } else {
+    return tl::unexpected(FMT("Unknown command: {}", command));
+  }
+
+  return result;
+}
+
 int
 main(int argc, char* argv[])
 {
@@ -264,44 +305,29 @@ main(int argc, char* argv[])
 #else
     argv[1];
 #endif
-  const std::string command = argv[2];
-
-  std::vector<std::string> cmd_args;
-  for (int i = 3; i < argc; ++i) {
-    cmd_args.push_back(argv[i]);
-  }
 
   Client client(k_data_timeout, k_request_timeout);
   auto connect_result = client.connect(ipc_endpoint);
 
   if (!connect_result) {
     PRINT(stderr,
-          "Failed to connect to {}: {}\n",
+          "Failed connecting to {}: {}\n",
           ipc_endpoint,
           connect_result.error().message);
     return 1;
   }
 
-  if (!client.has_capability(Client::Capability::get_put_remove_stop)) {
-    PRINT(stderr,
-          "Helper does not support capability {}\n",
-          to_string(Client::Capability::get_put_remove_stop));
-    return 1;
+  const std::string command = argv[2];
+  std::vector<std::string> args;
+  for (int i = 3; i < argc; ++i) {
+    args.push_back(argv[i]);
   }
 
-  if (command == "ping") {
-    return cmd_ping(cmd_args);
-  } else if (command == "get") {
-    return cmd_get(client, cmd_args);
-  } else if (command == "put") {
-    return cmd_put(client, cmd_args);
-  } else if (command == "remove") {
-    return cmd_remove(client, cmd_args);
-  } else if (command == "stop") {
-    return cmd_stop(client, cmd_args);
+  auto result = handle_command(client, command, args);
+  if (result) {
+    return *result;
   } else {
-    PRINT(stderr, "Unknown command: {}\n\n", command);
-    print_usage(stderr, argv[0]);
+    PRINT(stderr, "Error: {}\n", result.error());
     return 1;
   }
 }
index 011e880b505704677e984c9219fc2f5d4052a22d..c3d8fedb09db5b2525cc4b448b40dc125dd1266d 100644 (file)
@@ -56,7 +56,9 @@
 
 namespace fs = util::filesystem;
 
-constexpr uint8_t CAP_GET_PUT_REMOVE_STOP = 0x00;
+constexpr uint8_t PROTOCOL_VERSION = 0x01;
+constexpr uint8_t CAP_GET_PUT_REMOVE = 0x00;
+constexpr uint8_t CAP_INFO = 0x01;
 
 constexpr uint8_t STATUS_OK = 0x00;
 constexpr uint8_t STATUS_NOOP = 0x01;
@@ -66,6 +68,7 @@ constexpr uint8_t REQ_GET = 0x00;
 constexpr uint8_t REQ_PUT = 0x01;
 constexpr uint8_t REQ_REMOVE = 0x02;
 constexpr uint8_t REQ_STOP = 0x03;
+constexpr uint8_t REQ_INFO = 0x04;
 
 constexpr uint8_t PUT_FLAG_OVERWRITE = 0x01;
 
@@ -104,13 +107,10 @@ fail(const std::string& message)
 class IpcServer
 {
 public:
-  IpcServer(const std::string& endpoint,
-            std::chrono::seconds idle_timeout,
-            uint8_t greeting_format)
+  IpcServer(const std::string& endpoint, std::chrono::seconds idle_timeout)
     : m_endpoint(endpoint),
       m_idle_timeout(idle_timeout),
-      m_last_activity(util::now()),
-      m_greeting_format(greeting_format)
+      m_last_activity(util::now())
   {
   }
 
@@ -120,13 +120,13 @@ private:
   std::string m_endpoint;
   std::chrono::seconds m_idle_timeout;
   util::TimePoint m_last_activity;
-  uint8_t m_greeting_format;
   std::unordered_map<std::string, std::vector<uint8_t>> m_storage;
 
   bool recv_exact(ConnHandle conn, uint8_t* buf, size_t count);
   void send_data(ConnHandle conn, const uint8_t* data, size_t len);
   void send_error(ConnHandle conn, const char* message);
   void handle_get(ConnHandle conn);
+  void handle_info(ConnHandle conn);
   void handle_put(ConnHandle conn);
   void handle_remove(ConnHandle conn);
   void handle_stop(ConnHandle conn);
@@ -259,6 +259,19 @@ IpcServer::handle_get(ConnHandle conn)
   }
 }
 
+void
+IpcServer::handle_info(ConnHandle conn)
+{
+  log_msg("INFO");
+  const std::string_view identity = "ccache-storage-test";
+  std::vector<uint8_t> response;
+  response.push_back(static_cast<uint8_t>(identity.size()));
+  auto id_data = reinterpret_cast<const uint8_t*>(identity.data());
+  response.insert(response.end(), id_data, id_data + identity.size());
+  response.push_back(0); // 0 diagnostics
+  send_data(conn, response.data(), response.size());
+}
+
 void
 IpcServer::handle_put(ConnHandle conn)
 {
@@ -358,21 +371,8 @@ IpcServer::handle_stop(ConnHandle conn)
 void
 IpcServer::handle_client(ConnHandle conn)
 {
-  if (m_greeting_format == 2) {
-    const std::string_view identity = "ccache-storage-test";
-    std::vector<uint8_t> greeting;
-    greeting.push_back(0x02); // greeting_2
-    greeting.push_back(1);    // 1 capability
-    greeting.push_back(CAP_GET_PUT_REMOVE_STOP);
-    greeting.push_back(static_cast<uint8_t>(identity.size()));
-    auto id_data = reinterpret_cast<const uint8_t*>(identity.data());
-    greeting.insert(greeting.end(), id_data, id_data + identity.size());
-    greeting.push_back(0); // 0 diagnostics
-    send_data(conn, greeting.data(), greeting.size());
-  } else {
-    const uint8_t greeting[] = {0x01, 1, CAP_GET_PUT_REMOVE_STOP};
-    send_data(conn, greeting, sizeof(greeting));
-  }
+  uint8_t greeting[] = {PROTOCOL_VERSION, 2, CAP_GET_PUT_REMOVE, CAP_INFO};
+  send_data(conn, greeting, sizeof(greeting));
 
   while (true) {
     uint8_t request_type;
@@ -387,6 +387,10 @@ IpcServer::handle_client(ConnHandle conn)
       handle_get(conn);
       break;
 
+    case REQ_INFO:
+      handle_info(conn);
+      break;
+
     case REQ_PUT:
       handle_put(conn);
       break;
@@ -585,25 +589,12 @@ main()
     idle_timeout = *value;
   }
 
-  uint8_t greeting_format = 1;
-  const char* format_max_env = std::getenv("CRSH_FORMAT_MAX");
-  if (format_max_env) {
-    auto client_format_max = util::parse_unsigned(format_max_env, 1, UINT8_MAX);
-    if (!client_format_max) {
-      fail(FMT("Invalid CRSH_FORMAT_MAX: {}", client_format_max.error()));
-    }
-    constexpr uint8_t server_format_max = 2;
-    greeting_format = std::min<uint8_t>(*client_format_max, server_format_max);
-  }
-
   log_msg("Starting");
   log_msg(FMT("IPC endpoint: {}", endpoint));
   log_msg(FMT("URL: {}", url));
   log_msg(FMT("Idle timeout: {}", idle_timeout));
-  log_msg(FMT("Greeting format: {}", greeting_format));
 
-  IpcServer helper(
-    endpoint, std::chrono::seconds{idle_timeout}, greeting_format);
+  IpcServer helper(endpoint, std::chrono::seconds{idle_timeout});
   helper.run();
 
   log_msg("Shutdown complete");