system: Implement remote system reboot via Sysrepo

The ietf-system model [1] models an RPC (ietf-system:system-restart)
which causes system restart.

The restart is done simply by invoking `systemctl reboot` command.

Originally, I wanted just to call systemd's Reboot method over its D-Bus
API [2], however, it does not shutdown services and just goes straight
into the reboot process. The version from logind, that can do this, is
not available on our boxes.

[1] https://tools.ietf.org/html/rfc7317
[2] https://www.freedesktop.org/wiki/Software/systemd/dbus/

Change-Id: I7d690a11402eaf408d9dce04d3ac0ef9006bcdb8
diff --git a/src/system/IETFSystem.cpp b/src/system/IETFSystem.cpp
index fcfe6b5..e508d20 100644
--- a/src/system/IETFSystem.cpp
+++ b/src/system/IETFSystem.cpp
@@ -8,6 +8,8 @@
 #include <boost/algorithm/string/predicate.hpp>
 #include <fstream>
 #include "IETFSystem.h"
+#include "system_vars.h"
+#include "utils/exec.h"
 #include "utils/io.h"
 #include "utils/log.h"
 #include "utils/sysrepo.h"
@@ -60,6 +62,7 @@
 /** @brief Reads some OS-identification data from osRelease file and publishes them via ietf-system model */
 IETFSystem::IETFSystem(std::shared_ptr<::sysrepo::Session> srSession, const std::filesystem::path& osRelease)
     : m_srSession(std::move(srSession))
+    , m_srSubscribe(std::make_shared<::sysrepo::Subscribe>(m_srSession))
     , m_log(spdlog::get("system"))
 {
     std::map<std::string, std::string> osReleaseContents = parseKeyValueFile(osRelease);
@@ -71,5 +74,20 @@
     };
 
     utils::valuesPush(opsSystemStateData, m_srSession, SR_DS_OPERATIONAL);
+
+    m_srSubscribe->rpc_subscribe(
+        ("/" + IETF_SYSTEM_MODULE_NAME + ":system-restart").c_str(),
+        [this](::sysrepo::S_Session session, [[maybe_unused]] const char* op_path, [[maybe_unused]] const ::sysrepo::S_Vals input, [[maybe_unused]] sr_event_t event, [[maybe_unused]] uint32_t request_id, [[maybe_unused]] ::sysrepo::S_Vals_Holder output) {
+            try {
+                velia::utils::execAndWait(m_log, SYSTEMCTL_EXECUTABLE, {"reboot"}, "", {});
+            } catch(const std::runtime_error& e) {
+                session->set_error("Reboot procedure failed.", nullptr);
+                return SR_ERR_OPERATION_FAILED;
+            }
+
+            return SR_ERR_OK;
+        },
+        0,
+        SR_SUBSCR_CTX_REUSE);
 }
 }
diff --git a/src/system/IETFSystem.h b/src/system/IETFSystem.h
index 4367369..c9ec195 100644
--- a/src/system/IETFSystem.h
+++ b/src/system/IETFSystem.h
@@ -14,10 +14,11 @@
 
 class IETFSystem {
 public:
-    explicit IETFSystem(std::shared_ptr<::sysrepo::Session> srSession, const std::filesystem::path& osRelease);
+    IETFSystem(std::shared_ptr<::sysrepo::Session> srSession, const std::filesystem::path& osRelease);
 
 private:
     std::shared_ptr<::sysrepo::Session> m_srSession;
+    std::shared_ptr<::sysrepo::Subscribe> m_srSubscribe;
     velia::Log m_log;
 };
 }
diff --git a/src/system/system_vars.h.in b/src/system/system_vars.h.in
index 833951a..8098a2e 100644
--- a/src/system/system_vars.h.in
+++ b/src/system/system_vars.h.in
@@ -7,3 +7,4 @@
 #define BACKUP_ETC_SHADOW_FILE "@VELIA_BACKUP_ETC_SHADOW@"
 #define SSH_KEYGEN_EXECUTABLE "@SSH_KEYGEN_EXECUTABLE@"
 #define CHPASSWD_EXECUTABLE "@CHPASSWD_EXECUTABLE@"
+#define SYSTEMCTL_EXECUTABLE "@SYSTEMCTL_EXECUTABLE@"