Browse Source

Implement retrieval of daemon environment via control protocol

Davin McCall 3 months ago
parent
commit
2d0e110d49
4 changed files with 67 additions and 4 deletions
  1. 48 2
      src/control.cc
  2. 6 0
      src/includes/control-cmds.h
  3. 3 0
      src/includes/control.h
  4. 10 2
      src/includes/dinit-env.h

+ 48 - 2
src/control.cc

@@ -16,7 +16,7 @@
 // 1 - dinit 0.16 and prior
 // 2 - dinit 0.17 (adds SETTRIGGER, CATLOG, SIGNAL)
 // 3 - dinit 0.17.1 (adds QUERYSERVICEDSCDIR)
-// 4 - (unreleased) (adds CLOSEHANDLE)
+// 4 - (unreleased) (adds CLOSEHANDLE, GETALLENV)
 
 // common communication datatypes
 using namespace dinit_cptypes;
@@ -120,6 +120,9 @@ bool control_conn_t::process_packet()
     if (pktType == cp_cmd::SETENV) {
         return process_setenv();
     }
+    if (pktType == cp_cmd::GETALLENV) {
+        return process_getallenv();
+    }
     if (pktType == cp_cmd::SETTRIGGER) {
         return process_set_trigger();
     }
@@ -966,11 +969,54 @@ bool control_conn_t::process_setenv()
 
 badreq:
     // Queue error response / mark connection bad
-    if (! queue_packet(badreqRep, 1)) return false;
+    if (!queue_packet(badreqRep, 1)) return false;
     bad_conn_close = true;
     return true;
 }
 
+bool control_conn_t::process_getallenv()
+{
+    // 1 byte packet type
+    // 1 byte reserved - must be 0
+
+    constexpr int pkt_size = 2;
+    if (rbuf.get_length() < pkt_size) {
+        chklen = pkt_size;
+        return true;
+    }
+
+    uint8_t reserved_byte = rbuf[1];
+    if (reserved_byte != 0) {
+        char badreqRep[] = { (char)cp_rply::BADREQ };
+        if (!queue_packet(badreqRep, 1)) return false;
+        bad_conn_close = true;
+        return true;
+    }
+
+    // The reply looks like:
+    // 1 byte - reply type
+    // sizeof(size_t) - reply data size
+    // n bytes - reply data (NAME=VALUE, separated by nul characters)
+
+    std::vector<char> env_block;
+    constexpr size_t env_block_hdr_size = sizeof(size_t) + 1;
+    env_block.resize(env_block_hdr_size);
+
+    rbuf.consume(pkt_size);
+    auto env = main_env.build();
+    for (const char *env_var : env.env_list) {
+        if (env_var != nullptr) {
+            env_block.insert(env_block.end(), env_var, env_var + strlen(env_var) + 1);
+        }
+    }
+
+    env_block[0] = (char)cp_rply::ALLENV;
+    size_t block_size = env_block.size() - env_block_hdr_size;
+    memcpy(env_block.data() + 1, &block_size, sizeof(block_size));
+    if (!queue_packet(std::move(env_block))) return false;
+    return true;
+}
+
 bool control_conn_t::process_set_trigger()
 {
     // 1 byte packet type

+ 6 - 0
src/includes/control-cmds.h

@@ -70,6 +70,9 @@ enum class cp_cmd : dinit_cptypes::cp_cmd_t {
 
     // "Close" a service handle
     CLOSEHANDLE = 23,
+
+    // Retrieve complete environment
+    GETALLENV = 24,
 };
 
 // Replies:
@@ -143,6 +146,9 @@ enum class cp_rply : dinit_cptypes::cp_rply_t {
 
     // Service description directory:
     SVCDSCDIR = 77,
+
+    // Retrieve complete environment
+    ALLENV = 78,
 };
 
 // Information (out-of-band):

+ 3 - 0
src/includes/control.h

@@ -185,6 +185,9 @@ class control_conn_t : private service_listener
     // Query service path / load mechanism.
     bool query_load_mech();
 
+    // Get the complete environment
+    bool process_getallenv();
+
     // Notify that data is ready to be read from the socket. Returns true if the connection should
     // be closed.
     bool data_ready() noexcept;

+ 10 - 2
src/includes/dinit-env.h

@@ -99,7 +99,8 @@ public:
     environment &operator=(const environment &other) = delete;
 
     struct env_map {
-        // *non-owning* list of environment variables, i.e. list as suitable for exec
+        // *non-owning* list of environment variables, i.e. list as suitable for exec, including
+        // null at end of list
         std::vector<const char *> env_list;
 
         // map of variable name (via string_view) to its index in env_list
@@ -138,7 +139,8 @@ public:
     // Build a mapping excluding named variables (only called if the parent is the real environment).
     // Note that the return is non-owning, i.e. the variable values are backed by the environment object
     // and their lifetime is bounded to it.
-    env_map build(const env_names &exclude) const {
+    env_map build(const env_names &exclude) const
+    {
         env_map mapping;
 
         if (keep_parent_env) {
@@ -243,6 +245,12 @@ public:
         return mapping;
     }
 
+    // build a mapping, where parent is the real environment
+    env_map build() const
+    {
+        return build(env_names());
+    }
+
     void set_var(std::string &&var_and_val)
     {
         string_view var_name = find_var_name(var_and_val);