From 77efd03be8160f0f16276944d72e492c23d58f77 Mon Sep 17 00:00:00 2001
From: Vinicius Rangel <vinicius.l.rangel@gmail.com>
Date: Mon, 13 Nov 2023 09:43:13 -0300
Subject: [PATCH] [orbis-kenel] implement sys_kenv (#60)

---
 orbis-kernel/include/orbis/KernelContext.hpp | 14 +++-
 orbis-kernel/src/sys/sys_environment.cpp     | 80 +++++++++++++++++++-
 2 files changed, 90 insertions(+), 4 deletions(-)

diff --git a/orbis-kernel/include/orbis/KernelContext.hpp b/orbis-kernel/include/orbis/KernelContext.hpp
index a6e55e9d..49d6c58b 100644
--- a/orbis-kernel/include/orbis/KernelContext.hpp
+++ b/orbis-kernel/include/orbis/KernelContext.hpp
@@ -55,9 +55,7 @@ class alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) KernelContext final {
   void deleteProcess(Process *proc);
   Process *findProcessById(pid_t pid) const;
 
-  utils::LinkedNode<Process> *getProcessList() {
-    return m_processes;
-  }
+  utils::LinkedNode<Process> *getProcessList() { return m_processes; }
 
   long allocatePid() {
     std::lock_guard lock(m_thread_id_mtx);
@@ -143,6 +141,13 @@ class alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) KernelContext final {
     return {};
   }
 
+  std::tuple<utils::kmap<utils::kstring, char[128]> &,
+             std::unique_lock<shared_mutex>>
+  getKernelEnv() {
+    std::unique_lock lock(m_kenv_mtx);
+    return {m_kenv, std::move(lock)};
+  }
+
   enum {
     c_golden_ratio_prime = 2654404609u,
     c_umtx_chains = 512,
@@ -193,6 +198,9 @@ class alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) KernelContext final {
 
   shared_mutex mIpmiServerMtx;
   utils::kmap<utils::kstring, Ref<IpmiServer>> mIpmiServers;
+
+  shared_mutex m_kenv_mtx;
+  utils::kmap<utils::kstring, char[128]> m_kenv; // max size: 127 + '\0'
 };
 
 extern KernelContext &g_context;
diff --git a/orbis-kernel/src/sys/sys_environment.cpp b/orbis-kernel/src/sys/sys_environment.cpp
index 8b394e2a..e22a3de9 100644
--- a/orbis-kernel/src/sys/sys_environment.cpp
+++ b/orbis-kernel/src/sys/sys_environment.cpp
@@ -1,7 +1,85 @@
 #include "sys/sysproto.hpp"
 
+#include "KernelContext.hpp"
+
 orbis::SysResult orbis::sys_kenv(Thread *thread, sint what,
                                  ptr<const char> name, ptr<char> value,
                                  sint len) {
-  return ErrorCode::NOSYS;
+  enum action { kenv_get, kenv_set, kenv_unset, kenv_dump };
+
+  const auto &[kenv, _] = thread->tproc->context->getKernelEnv();
+
+  if (what == kenv_dump) {
+    int needed = 0;
+    int done = 0;
+
+    for (const auto &[key, env_value] : kenv) {
+      size_t entry = 0;
+      // Entry: size of both full buffers, the '=' and the '\0' at the end
+      if (value == nullptr || len == 0) {
+        entry = key.size() + 1 + strnlen(env_value, 128) + 1;
+      } else {
+        char buf[128 * 2 + 2];
+
+        char *_buf = buf;
+        std::strncpy(buf, key.data(), key.size());
+        _buf += key.size();
+
+        *_buf++ = '=';
+
+        const size_t value_size = strnlen(env_value, 128);
+        std::strncpy(_buf, env_value, value_size);
+        _buf += value_size;
+
+        *_buf++ = '\0';
+
+        entry = _buf - buf;
+        ORBIS_RET_ON_ERROR(uwriteRaw(value + done, buf, entry));
+        len -= entry;
+        done += entry;
+      }
+      needed += entry;
+    }
+
+    if (done != needed) {
+      thread->retval[0] = needed;
+    }
+    return {};
+  }
+
+  char _name_buf[128];
+  ORBIS_RET_ON_ERROR(ureadString(_name_buf, sizeof(_name_buf), name));
+  const std::string_view _name(_name_buf, strnlen(_name_buf, 128));
+
+  switch (what) {
+  case kenv_get: {
+    const auto it = kenv.find(_name);
+    if (it == kenv.end()) {
+      return ErrorCode::NOENT;
+    }
+    const char *buf = it->second;
+    ORBIS_RET_ON_ERROR(uwriteRaw(value, buf, std::min(len, 128)));
+    break;
+  }
+  case kenv_set: {
+    if (len < 1) {
+      return ErrorCode::INVAL;
+    }
+    char *_value_buf = kenv[kstring(_name)];
+    ORBIS_RET_ON_ERROR(ureadString(_value_buf, 128, value));
+    break;
+  }
+  case kenv_unset: {
+    const auto it = kenv.find(_name);
+    if (it == kenv.end()) {
+      return ErrorCode::NOENT;
+    }
+    kenv.erase(it);
+    break;
+  }
+  default:
+    return ErrorCode::INVAL;
+  }
+
+  return {};
 }