Implement hostname

Change-Id: I676d7cf5c821debf59c62ddad6a06264d57918ec
diff --git a/src/system/IETFSystem.cpp b/src/system/IETFSystem.cpp
index 98bbb3d..5a8a5c7 100644
--- a/src/system/IETFSystem.cpp
+++ b/src/system/IETFSystem.cpp
@@ -20,6 +20,7 @@
 
 const auto IETF_SYSTEM_MODULE_NAME = "ietf-system"s;
 const auto IETF_SYSTEM_STATE_MODULE_PREFIX = "/"s + IETF_SYSTEM_MODULE_NAME + ":system-state/"s;
+const auto IETF_SYSTEM_HOSTNAME_PATH = "/ietf-system:system/hostname";
 
 /** @brief Returns key=value pairs from (e.g. /etc/os-release) as a std::map */
 std::map<std::string, std::string> parseKeyValueFile(const std::filesystem::path& path)
@@ -55,12 +56,27 @@
     return res;
 }
 
+std::optional<std::string> getHostnameFromChange(const std::shared_ptr<sysrepo::Session> session)
+{
+    std::optional<std::string> res;
+
+    auto data = session->get_data(IETF_SYSTEM_HOSTNAME_PATH);
+    if (data) {
+        auto hostnameNode = data->find_path(IETF_SYSTEM_HOSTNAME_PATH)->data().front();
+        auto leaf = std::make_shared<libyang::Data_Node_Leaf_List>(hostnameNode);
+        res = leaf->value_str();
+    }
+
+    return res;
+}
 }
 
 namespace velia::system {
 
 void IETFSystem::initStaticProperties(const std::filesystem::path& osRelease)
 {
+    utils::ensureModuleImplemented(m_srSession, IETF_SYSTEM_MODULE_NAME, "2014-08-06");
+
     std::map<std::string, std::string> osReleaseContents = parseKeyValueFile(osRelease);
 
     std::map<std::string, std::string> opsSystemStateData {
@@ -90,9 +106,51 @@
         SR_SUBSCR_CTX_REUSE);
 }
 
+void IETFSystem::initHostname()
+{
+    sysrepo::ModuleChangeCb hostNameCbRunning = [this] (auto session, auto, auto, auto, auto) {
+        if (auto newHostname = getHostnameFromChange(session)) {
+            velia::utils::execAndWait(m_log, HOSTNAMECTL_EXECUTABLE, {"set-hostname", *newHostname}, "");
+        }
+        return SR_ERR_OK;
+    };
+
+    sysrepo::ModuleChangeCb hostNameCbStartup = [] (auto session, auto, auto, auto, auto) {
+        if (auto newHostname = getHostnameFromChange(session)) {
+            utils::safeWriteFile(BACKUP_ETC_HOSTNAME_FILE, *newHostname);
+        }
+        return SR_ERR_OK;
+    };
+
+    sysrepo::OperGetItemsCb hostNameCbOperational = [] (auto session, auto, auto, auto, auto, auto& parent) {
+        // + 1 for null-terminating byte, HOST_NAME_MAX doesn't count that
+        std::array<char, HOST_NAME_MAX + 1> buffer;
+
+        if (gethostname(buffer.data(), buffer.size()) != 0) {
+            throw std::system_error(errno, std::system_category(), "gethostname() failed");
+        }
+
+        parent->new_path(
+            session->get_context(),
+            IETF_SYSTEM_HOSTNAME_PATH,
+            buffer.data(),
+            LYD_ANYDATA_CONSTSTRING,
+            0);
+
+        return SR_ERR_OK;
+    };
+
+    m_srSubscribe->module_change_subscribe(IETF_SYSTEM_MODULE_NAME.c_str(), hostNameCbRunning, IETF_SYSTEM_HOSTNAME_PATH, 0, SR_SUBSCR_DONE_ONLY);
+    m_srSession->session_switch_ds(SR_DS_STARTUP);
+    m_srSubscribe->module_change_subscribe(IETF_SYSTEM_MODULE_NAME.c_str(), hostNameCbStartup, IETF_SYSTEM_HOSTNAME_PATH, 0, SR_SUBSCR_DONE_ONLY);
+    m_srSession->session_switch_ds(SR_DS_OPERATIONAL);
+    m_srSubscribe->oper_get_items_subscribe(IETF_SYSTEM_MODULE_NAME.c_str(), hostNameCbOperational, IETF_SYSTEM_HOSTNAME_PATH);
+}
+
 /** This class handles multiple system properties and publishes them via the ietf-system model:
  * - OS-identification data from osRelease file
  * - Rebooting
+ * - Hostname
  */
 IETFSystem::IETFSystem(std::shared_ptr<::sysrepo::Session> srSession, const std::filesystem::path& osRelease)
     : m_srSession(std::move(srSession))
@@ -101,5 +159,6 @@
 {
     initStaticProperties(osRelease);
     initSystemRestart();
+    initHostname();
 }
 }