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/CMakeLists.txt b/CMakeLists.txt
index 0da5edc..857499e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -147,6 +147,9 @@
if(NOT CHPASSWD_EXECUTABLE)
find_program(CHPASSWD_EXECUTABLE chpasswd)
endif()
+if(NOT SYSTEMCTL_EXECUTABLE)
+ find_program(SYSTEMCTL_EXECUTABLE systemctl)
+endif()
set(VELIA_AUTHORIZED_KEYS_FORMAT "{HOME}/.ssh/authorized_keys" CACHE STRING "pattern for determining path to users' SSH authorized_keys file. Must at least one of '{USER}' or '{HOME}' which will get replaced by the name of the user and the home directory of the user respectively.")
if(NOT VELIA_AUTHORIZED_KEYS_FORMAT)
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@"
diff --git a/tests/sysrepo_system-ietfsystem.cpp b/tests/sysrepo_system-ietfsystem.cpp
index 04add24..219ce1c 100644
--- a/tests/sysrepo_system-ietfsystem.cpp
+++ b/tests/sysrepo_system-ietfsystem.cpp
@@ -13,10 +13,10 @@
TEST_SYSREPO_INIT_LOGS;
TEST_SYSREPO_INIT;
+ TEST_SYSREPO_INIT_CLIENT;
SECTION("Test system-state")
{
- TEST_SYSREPO_INIT_CLIENT;
static const auto modulePrefix = "/ietf-system:system-state"s;
SECTION("Valid data")
@@ -69,4 +69,15 @@
REQUIRE_THROWS_AS(std::make_shared<velia::system::IETFSystem>(srSess, CMAKE_CURRENT_SOURCE_DIR "/tests/system/missing-keys"), std::out_of_range);
}
}
+
+#ifdef TEST_RPC_SYSTEM_REBOOT
+ SECTION("RPC system-restart")
+ {
+ auto sysrepo = std::make_shared<velia::system::IETFSystem>(srSess, CMAKE_CURRENT_SOURCE_DIR "/tests/system/os-release");
+
+ auto rpcInput = std::make_shared<sysrepo::Vals>(0);
+ auto res = client->rpc_send("/ietf-system:system-restart", rpcInput);
+ REQUIRE(res->val_cnt() == 0);
+ }
+#endif
}